From 7db82a22a9d1712289e760a458d3c76625b596fc Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 12 Jul 2022 23:22:27 +0800 Subject: [PATCH 01/50] udp bug which causes us a lot of trouble --- libs/hbb_common/src/udp.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/hbb_common/src/udp.rs b/libs/hbb_common/src/udp.rs index 4213392a5..3532dd1e0 100644 --- a/libs/hbb_common/src/udp.rs +++ b/libs/hbb_common/src/udp.rs @@ -27,6 +27,8 @@ fn new_socket(addr: SocketAddr, reuse: bool, buf_size: usize) -> Result 0 { socket.set_recv_buffer_size(buf_size).ok(); } From f1724820b1de55ba85aa52fb38fd8218dd5df85d Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 12 Jul 2022 23:28:32 +0800 Subject: [PATCH 02/50] missed socket.set_nonblocking for tcp --- libs/hbb_common/src/tcp.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/hbb_common/src/tcp.rs b/libs/hbb_common/src/tcp.rs index 7966920c2..ce4a805d5 100644 --- a/libs/hbb_common/src/tcp.rs +++ b/libs/hbb_common/src/tcp.rs @@ -68,6 +68,8 @@ fn new_socket(addr: std::net::SocketAddr, reuse: bool) -> Result Date: Tue, 12 Jul 2022 23:32:37 +0800 Subject: [PATCH 03/50] fix tcp --- libs/hbb_common/src/tcp.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/libs/hbb_common/src/tcp.rs b/libs/hbb_common/src/tcp.rs index ce4a805d5..7966920c2 100644 --- a/libs/hbb_common/src/tcp.rs +++ b/libs/hbb_common/src/tcp.rs @@ -68,8 +68,6 @@ fn new_socket(addr: std::net::SocketAddr, reuse: bool) -> Result Date: Tue, 12 Jul 2022 19:08:05 +0200 Subject: [PATCH 04/50] Hungrian translation --- src/lang.rs | 3 + src/lang/hu.rs | 288 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 291 insertions(+) create mode 100644 src/lang/hu.rs diff --git a/src/lang.rs b/src/lang.rs index b37c4beb6..f01833718 100644 --- a/src/lang.rs +++ b/src/lang.rs @@ -8,6 +8,7 @@ mod de; mod en; mod eo; mod es; +mod hu; mod fr; mod id; mod it; @@ -28,6 +29,7 @@ lazy_static::lazy_static! { ("tw", "繁體中文"), ("pt", "Português"), ("es", "Español"), + ("hu", "Magyar"), ("ru", "Русский"), ("sk", "Slovenčina"), ("id", "Indonesia"), @@ -68,6 +70,7 @@ pub fn translate_locale(name: String, locale: &str) -> String { "tw" => tw::T.deref(), "de" => de::T.deref(), "es" => es::T.deref(), + "hu" => hu::T.deref(), "ru" => ru::T.deref(), "eo" => eo::T.deref(), "id" => id::T.deref(), diff --git a/src/lang/hu.rs b/src/lang/hu.rs new file mode 100644 index 000000000..aef200bcf --- /dev/null +++ b/src/lang/hu.rs @@ -0,0 +1,288 @@ +azy_static::lazy_static! { +pub static ref T: std::collections::HashMap<&'static str, &'static str> = + [ + ("Status", "Státusz"), + ("Your Desktop", "A te asztalod"), + ("desk_tip", "Az asztalod ezzel az ID-vel, és jelszóval érhető el."), + ("Password", "Jelszó"), + ("Ready", "Kész"), + ("Established", "Létrejött"), + ("connecting_status", "Kapcsolódás a RustDesk hálózatához..."), + ("Enable Service", "A szolgáltatás bekapcsolása"), + ("Start Service", "A szolgáltatás elindítása"), + ("Service is running", "A szolgáltatás fut"), + ("Service is not running", "A szolgáltatás nem válaszol"), + ("not_ready_status", "A RustDesk nem áll készen. Kérlek nézd meg a hálózati beállításaidat."), + ("Control Remote Desktop", "Távoli Asztal Kontrollálása"), + ("Transfer File", "Fájl Transzfer"), + ("Connect", "Kapcsolódás"), + ("Recent Sessions", "Korábbi Sessionök"), + ("Address Book", "Címköny"), + ("Confirmation", "Megerősít"), + ("TCP Tunneling", "TCP Tunneling"), + ("Remove", "Eltávolít"), + ("Refresh random password", "Véletlenszerű jelszó frissítése"), + ("Set your own password", "Saját jelszó beállítása"), + ("Enable Keyboard/Mouse", "Billentyűzet/Egér bekapcsolása"), + ("Enable Clipboard", "Megosztott vágólap bekapcsolása"), + ("Enable File Transfer", "Fájl transzer bekapcsolása"), + ("Enable TCP Tunneling", "TCP Tunneling bekapcsolása"), + ("IP Whitelisting", "IP Fehérlista"), + ("ID/Relay Server", "ID/Relay Szerver"), + ("Stop service", "Szolgáltatás Kikapcsolása"), + ("Change ID", "ID Megváltoztatása"), + ("Website", "Weboldal"), + ("About", "Rólunk: "), + ("Mute", "Némítás"), + ("Audio Input", "Audo Bemenet"), + ("Enhancements", "Javítások"), + ("Hardware Codec", "Hardware Kodek"), + ("Adaptive Bitrate", "Adaptív Bitrate"), + ("ID Server", "ID Szerver"), + ("Relay Server", "Relay Szerver"), + ("API Server", "API Szerver"), + ("invalid_http", "A címnek mindenképpen http(s)://-el kell kezdődnie."), + ("Invalid IP", "A megadott íp cím helytelen."), + ("id_change_tip", "Csak a-z, A-Z, 0-9 csoportokba tartozó karakterek, illetve a _ karakter van engedélyezve. Az első karakternek mindenképpen a-z, A-Z csoportokba kell esnie. Az ID hosszúsága 6-tól, 16 karakter."), + ("Invalid format", "Érvénytelen formátum"), + ("server_not_support", "Még nem támogatott a szerver által"), + ("Not available", "Nem érhető el"), + ("Too frequent", "Túl gyakori"), + ("Cancel", "Mégsem"), + ("Skip", "Kihagy"), + ("Close", "Bezár"), + ("Retry", "Újrapróbál"), + ("OK", "OK"), + ("Password Required", "A jelszó megadása kötelező"), + ("Please enter your password", "Kérlek írd be a jelszavad"), + ("Remember password", "Kérlek emlékezz a jelszóra"), + ("Wrong Password", "Hibás jelszó"), + ("Do you want to enter again?", "Újra szeretnéd próbálni?"), + ("Connection Error", "Kapcsolódási Hiba"), + ("Error", "Hiba"), + ("Reset by the peer", "A kapcsolatot alaphelyzetbe állt"), + ("Connecting...", "Kapcsolódás..."), + ("Connection in progress. Please wait.", "A kapcsolódás folyamatban van. Kérlek várj."), + ("Please try 1 minute later", "Kérlek próbáld újra 1 perc múlva."), + ("Login Error", "Belépési Hiba"), + ("Successful", "Sikeres"), + ("Connected, waiting for image...", "Kapcsolódva, várakozás a képre..."), + ("Name", "Név"), + ("Type", "Fajta"), + ("Modified", "Módosított"), + ("Size", "Méret"), + ("Show Hidden Files", "Rejtett Fájlok Mutatása"), + ("Receive", "Kapni"), + ("Send", "Küldeni"), + ("Refresh File", "Fájlok Frissítése"), + ("Local", "Lokális"), + ("Remote", "Távoli"), + ("Remote Computer", "Távoli Számítógép"), + ("Local Computer", "Lokális Számítógép"), + ("Confirm Delete", "Törlés Megerősítése"), + ("Delete", "Törlés"), + ("Properties", "Tulajdonságok"), + ("Multi Select", "Több fájl kiválasztása"), + ("Empty Directory", "Üres Könyvtár"), + ("Not an empty directory", "Nem egy üres könyvtár"), + ("Are you sure you want to delete this file?", "Biztosan törölni szeretnéd ezt a fájlt?"), + ("Are you sure you want to delete this empty directory?", "Biztosan törölni szeretnéd ezt az üres könyvtárat?"), + ("Are you sure you want to delete the file of this directory?", "Biztosan törölni szeretnéd a fájlokat ebben a könyvtárban?"), + ("Do this for all conflicts", "Ezt tedd az összes konfliktussal"), + ("This is irreversible!", "Ez a folyamat visszafordíthatatlan!"), + ("Deleting", "A törlés folyamatban"), + ("files", "fájlok"), + ("Waiting", "Várunk"), + ("Finished", "Végzett"), + ("Speed", "Gyorsaság"), + ("Custom Image Quality", "Egyedi Képminőség"), + ("Privacy mode", "Inkognító mód"), + ("Block user input", "Felhasználói input blokkokolása"), + ("Unblock user input", "Felhasználói input blokkolásának feloldása"), + ("Adjust Window", "Ablakméret beállítása"), + ("Original", "Eredeti"), + ("Shrink", "Zsugorítás"), + ("Stretch", "Nyújtás"), + ("Good image quality", "Jó képminőség"), + ("Balanced", "Balanszolt"), + ("Optimize reaction time", "Válaszidő optimializálása"), + ("Custom", "Egyedi"), + ("Show remote cursor", "Távoli kurzor mutatása"), + ("Show quality monitor", "Minőségi monitor mutatása"), + ("Disable clipboard", "Vágólap Kikapcsolása"), + ("Lock after session end", "Lezárás a session végén"), + ("Insert", "Beszúrás"), + ("Insert Lock", "Beszúrási Zároló"), + ("Refresh", "Frissítés"), + ("ID does not exist", "Ez az ID nem létezik"), + ("Failed to connect to rendezvous server", "A randevú szerverhez való kapcsolódás sikertelen"), + ("Please try later", "Kérlek próbád később"), + ("Remote desktop is offline", "A távoli asztal offline"), + ("Key mismatch", "Eltérés a kulcsokban"), + ("Timeout", "Időtúllépés"), + ("Failed to connect to relay server", "A relay szerverhez való kapcsolódás sikertelen"), + ("Failed to connect via rendezvous server", "A randevú szerverrel való kapcsolódás sikertelen"), + ("Failed to connect via relay server", "A relay szerverrel való kapcsolódás sikertelen"), + ("Failed to make direct connection to remote desktop", "A távoli asztalhoz való direkt kapcsolódás sikertelen"), + ("Set Password", "Jelszó Beállítása"), + ("OS Password", "Operációs Rendszer Jelszavának Beállítása"), + ("install_tip", "Az UAC (Felhasználói Fiók Felügyelet) miatt, a RustDesk nem fog rendesen funkcionálni mint távoli oldal néhány esetben. Hogy ezt kikerüld, vagy kikapcsold, kérlek nyomj rá a gombra ezalatt az üzenet alatt, hogy feltelepítsd a RustDesket a rendszerre."), + ("Click to upgrade", "Kattints a frissítés telepítéséhez"), + ("Click to download", "Kattints a letöltéshez"), + ("Click to update", "Kattints a frissítés letöltéséhez"), + ("Configure", "Beállítás"), + ("config_acc", "Ahhoz hogy a RustDesket távolról irányítani tudd, \"Elérhetőségi\" jogokat kell adnod a RustDesk-nek."), + ("config_screen", "Ahhoz hogy a RustDesket távolról irányítani tudd, \"Képernyőfelvételi\" jogokat kell adnod a RustDesk-nek."), + ("Installing ...", "Telepítés..."), + ("Install", "Telepítés"), + ("Installation", "Telepítés"), + ("Installation Path", "Telepítési útvonal"), + ("Create start menu shortcuts", "Smart menu parancsikon létrehozása"), + ("Create desktop icon", "Asztali icon létrehozása"), + ("agreement_tip", "Azzal hogy elindítod a telepítést, elfogadod a licenszszerződést."), + ("Accept and Install", "Elfogadás és Telepítés"), + ("End-user license agreement", "Felhasználói licencszerződés"), + ("Generating ...", "Generálás..."), + ("Your installation is lower version.", "A jelenleg feltelepített verzió régebbi."), + ("not_close_tcp_tip", "Ne zárd be ezt az ablakot miközben a tunnelt használod"), + ("Listening ...", "Halgazózás..."), + ("Remote Host", "Távoli Host"), + ("Remote Port", "Távoli Port"), + ("Action", "Akció"), + ("Add", "Add"), + ("Local Port", "Lokális Port"), + ("setup_server_tip", "Egy gyorsabb kapcsolatért, kérlek hostolj egy saját szervert"), + ("Too short, at least 6 characters.", "Túl rövid, legalább 6 karakter"), + ("The confirmation is not identical.", "A megerősítés nem volt azonos"), + ("Permissions", "Jogok"), + ("Accept", "Elfogad"), + ("Dismiss", "Elutasít"), + ("Disconnect", "Szétkapcsolás"), + ("Allow using keyboard and mouse", "Billentyűzet és egér használatának engedélyezése"), + ("Allow using clipboard", "Vágólap használatának engedélyezése"), + ("Allow hearing sound", "Hang átvitelének engedélyezése"), + ("Allow file copy and paste", "Fájlok másolásának és beillesztésének engedélyezése"), + ("Connected", "Kapcsolódva"), + ("Direct and encrypted connection", "Direkt, és titkosított kapcsolat"), + ("Relayed and encrypted connection", "Relayelt, és titkosított kapcsolat"), + ("Direct and unencrypted connection", "Direkt, és nem titkosított kapcsolat"), + ("Relayed and unencrypted connection", "Rekayelt, és nem titkosított kapcsolat"), + ("Enter Remote ID", "Kérlek írd be a távoli ID-t"), + ("Enter your password", "Kérlek írd be a jelszavadat"), + ("Logging in...", "A belépés folyamatban..."), + ("Enable RDP session sharing", "Az RDP session megosztás engedélyezése"), + ("Auto Login", "Automatikus Login"), + ("Enable Direct IP Access", "Direkt IP elérés engedélyezése"), + ("Rename", "Átnevezés"), + ("Space", "Hely"), + ("Create Desktop Shortcut", "Asztali Parancsikon Lértehozása"), + ("Change Path", "Elérési Útvonal Megváltoztatása"), + ("Create Folder", "Mappa Készítése"), + ("Please enter the folder name", "Kérlek írd be a mappa nevét"), + ("Fix it", "Kérlek javísd meg"), + ("Warning", "Figyelem"), + ("Login screen using Wayland is not supported", "A belépési kijelzővel a Wayland használata nem támogatott"), + ("Reboot required", "Újraindítás szükséges"), + ("Unsupported display server ", "Nem támogatott kijelző szerver"), + ("x11 expected", "x11-re számítottt"), + ("Port", "Port"), + ("Settings", "Beállítások"), + ("Username", "Felhasználónév"), + ("Invalid port", "Érvénytelen port"), + ("Closed manually by the peer", "A kapcsolat manuálisan be lett zárva a másik fél álltal"), + ("Enable remote configuration modification", "Távoli konfiguráció módosítás engedélyezése"), + ("Run without install", "Futtatás feltelepítés nélkül"), + ("Always connected via relay", "Mindig relay által kapcsolódott"), + ("Always connect via relay", "Mindig relay által kapcsolódik"), + ("whitelist_tip", "Csak a fehérlistán lévő címek érhetnek el"), + ("Login", "Belépés"), + ("Logout", "Kilépés"), + ("Tags", "Tagok"), + ("Search ID", "ID keresése"), + ("Current Wayland display server is not supported", "Jelenleg a Wayland display szerver nem támogatott"), + ("whitelist_sep", "Ide jönnek a címek, vesző, pontosvessző, space, vagy új sorral elválasztva"), + ("Add ID", "ID Hozzáadása"), + ("Add Tag", "Tag Hozzáadása"), + ("Unselect all tags", "Az összes tag kiválasztásának törlése"), + ("Network error", "Hálózati hiba"), + ("Username missed", "A felhasználónév kimaradt"), + ("Password missed", "A jelszó kimaradt"), + ("Wrong credentials", "Hibás felhasználónév vagy jelszó"), + ("Edit Tag", "A tag(ok) szerkeztése"), + ("Unremember Password", "A jelszó megjegyzésének törlése"), + ("Favorites", "Kedvencek"), + ("Add to Favorites", "Hozzáadás a kedvencekhez"), + ("Remove from Favorites", "Eltávolítás a kedvencektől"), + ("Empty", "Üres"), + ("Invalid folder name", "Helytelen fájlnév"), + ("Socks5 Proxy", "Socks5-ös Proxy"), + ("Hostname", "Hostnév"), + ("Discovered", "Felfedezett"), + ("install_daemon_tip", "Ahhoz hogy a RustDesk bootkor elinduljon, telepítened kell a rendszer szolgáltatást."), + ("Remote ID", "Távoli ID"), + ("Paste", "Beillesztés"), + ("Paste here?", "Beillesztés ide?"), + ("Are you sure to close the connection?", "Biztos vagy benne hogy be szeretnéd zárni a kapcsolatot?"), + ("Download new version", "Új verzó letöltése"), + ("Touch mode", "Érintési mód bekapcsolása"), + ("Mouse mode", "Egérhasználati mód bekapcsolása"), + ("One-Finger Tap", "Egyújas érintés"), + ("Left Mouse", "Baloldali Egér"), + ("One-Long Tap", "Egy hosszú érintés"), + ("Two-Finger Tap", "Két újas érintés"), + ("Right Mouse", "Jobboldali Egér"), + ("One-Finger Move", "Egyújas mozgatás"), + ("Double Tap & Move", "Kétszeri érintés, és Mozgatás"), + ("Mouse Drag", "Egérrel való húzás"), + ("Three-Finger vertically", "Három ujj függőlegesen"), + ("Mouse Wheel", "Egérgörgő"), + ("Two-Finger Move", "Kátújas mozgatás"), + ("Canvas Move", "Vászon mozgatása"), + ("Pinch to Zoom", "Húzd össze a nagyításhoz"), + ("Canvas Zoom", "Vászon Nagyítása"), + ("Reset canvas", "Vászon visszaállítása"), + ("No permission of file transfer", "Nincs jogod fájl transzer indításához"), + ("Note", "Megyjegyzés"), + ("Connection", "Kapcsolat"), + ("Share Screen", "Képernyő Megosztása"), + ("CLOSE", "BEZÁR"), + ("OPEN", "MEGNYIT"), + ("Chat", "Chat"), + ("Total", "Összes"), + ("items", "Tárgyak"), + ("Selected", "Kiválasztott"), + ("Screen Capture", "Képernyőrögzítő"), + ("Input Control", "Input Kontrol"), + ("Audio Capture", "Audió Rögzítés"), + ("File Connection", "Fájlkapcsolat"), + ("Screen Connection", "kijelzőkapcsolat"), + ("Do you accept?", "Elfogadod?"), + ("Open System Setting", "Rendszer beállítások megnyitása"), + ("How to get Android input permission?", "Hogyan állíthatok be Android input jogokat?"), + ("android_input_permission_tip1", "Ahhoz hogy egy távoli eszköz kontolálhassa az Android eszközödet egérrel vagy érintéssel, jogot kell adnod a RustDesk-nek, hogy használja az \"Elérhetőségi\" szolgáltatást."), + ("android_input_permission_tip2", "Kérlek navigálj a rendszer beállításaihoz, keresd meg vagy írd be hogy [Feltelepített Szolgáltatások], és kapcsold be a [RustDesk Input] szolgáltatást."), + ("android_new_connection_tip", "Új kontrollálási kérés érkezett, amely irányítani szeretné az eszközöded."), + ("android_service_will_start_tip", "A \"Kijelzőfelvétel\" bekapcsolása automatikusan elindítja majd a szolgáltatás, ami megengedi más eszközöknek hogy kérést kezdeményezzenek az eszközöd felé."), + ("android_stop_service_tip", "A szolgáltatás bezárása automatikusan szétkapcsol minden létező kapcsolatot."), + ("android_version_audio_tip", "A jelenlegi Android verzió nem támogatja az hangrögzítést, kérlek frissíts legalább Android 10-re, vagy egy újabb verzióra."), + ("android_start_service_tip", "Nyomj a [Szolgáltatás Indítása] opcióra, vagy adj [Képernyőfotó készítése] jogot az applikációnak hogy elindítsd a képernyőmegosztó szolgáltatást."), + ("Account", "Fiók"), + ("Overwrite", "Felülírás"), + ("This file exists, skip or overwrite this file?", "Ez a fájl már létezik, skippeljünk, vagy felülírjuk ezt a fájlt?"), + ("Quit", "Kilépés"), + ("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"), + ("Help", "Segítség"), + ("Failed", "Sikertelen"), + ("Succeeded", "Sikeres"), + ("Someone turns on privacy mode, exit", "Valaki bekacsolta a privát módot, lépj ki"), + ("Unsupported", "Nem támogatott"), + ("Peer denied", "Elutasítva a távoli fél álltal"), + ("Please install plugins", "Kérek telepítsd a pluginokat"), + ("Peer exit", "A távoli fél kilépett"), + ("Failed to turn off", "Nem tudtuk kikapcsolni"), + ("Turned off", "Kikapcsolva"), + ("In privacy mode", "Belépés a privát módba"), + ("Out privacy mode", "Kilépés a privát módból"), + ("Language", "Nyelv"), + ].iter().cloned().collect(); +} \ No newline at end of file From bb67ba92bdeb3c655dfba2dee5a0ef922f2534e4 Mon Sep 17 00:00:00 2001 From: KoxSosen <67807644+KoxSosen@users.noreply.github.com> Date: Tue, 12 Jul 2022 19:18:12 +0200 Subject: [PATCH 05/50] Ooops --- src/lang/hu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/hu.rs b/src/lang/hu.rs index aef200bcf..3e8ff9eee 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -1,4 +1,4 @@ -azy_static::lazy_static! { +lazy_static::lazy_static! { pub static ref T: std::collections::HashMap<&'static str, &'static str> = [ ("Status", "Státusz"), From 7055826e5dc71586b6e4c603a61b11632be157db Mon Sep 17 00:00:00 2001 From: Simon <67807644+KoxSosen@users.noreply.github.com> Date: Wed, 13 Jul 2022 10:17:22 +0200 Subject: [PATCH 06/50] Android fixes --- src/lang/hu.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 3e8ff9eee..f4453cbe0 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -9,9 +9,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Established", "Létrejött"), ("connecting_status", "Kapcsolódás a RustDesk hálózatához..."), ("Enable Service", "A szolgáltatás bekapcsolása"), - ("Start Service", "A szolgáltatás elindítása"), + ("Start Service", "Szolgáltatás Elindítása"), ("Service is running", "A szolgáltatás fut"), - ("Service is not running", "A szolgáltatás nem válaszol"), + ("Service is not running", "A szolgáltatás nem fut"), ("not_ready_status", "A RustDesk nem áll készen. Kérlek nézd meg a hálózati beállításaidat."), ("Control Remote Desktop", "Távoli Asztal Kontrollálása"), ("Transfer File", "Fájl Transzfer"), @@ -244,14 +244,14 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("No permission of file transfer", "Nincs jogod fájl transzer indításához"), ("Note", "Megyjegyzés"), ("Connection", "Kapcsolat"), - ("Share Screen", "Képernyő Megosztása"), - ("CLOSE", "BEZÁR"), - ("OPEN", "MEGNYIT"), + ("Share Screen", "Képernyőmegosztás"), + ("CLOSE", "LETILT"), + ("OPEN", "ENGEDÉLYEZ"), ("Chat", "Chat"), ("Total", "Összes"), ("items", "Tárgyak"), ("Selected", "Kiválasztott"), - ("Screen Capture", "Képernyőrögzítő"), + ("Screen Capture", "Képernyőrögzítés"), ("Input Control", "Input Kontrol"), ("Audio Capture", "Audió Rögzítés"), ("File Connection", "Fájlkapcsolat"), @@ -262,10 +262,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("android_input_permission_tip1", "Ahhoz hogy egy távoli eszköz kontolálhassa az Android eszközödet egérrel vagy érintéssel, jogot kell adnod a RustDesk-nek, hogy használja az \"Elérhetőségi\" szolgáltatást."), ("android_input_permission_tip2", "Kérlek navigálj a rendszer beállításaihoz, keresd meg vagy írd be hogy [Feltelepített Szolgáltatások], és kapcsold be a [RustDesk Input] szolgáltatást."), ("android_new_connection_tip", "Új kontrollálási kérés érkezett, amely irányítani szeretné az eszközöded."), - ("android_service_will_start_tip", "A \"Kijelzőfelvétel\" bekapcsolása automatikusan elindítja majd a szolgáltatás, ami megengedi más eszközöknek hogy kérést kezdeményezzenek az eszközöd felé."), + ("android_service_will_start_tip, "A "\Képernyőrögzítés\" engedélyezése automatikusan elindítja majd a szolgáltatást, amely megengedi más eszközöknek hogy kérést kezdeményezzenek az eszköz felé."), ("android_stop_service_tip", "A szolgáltatás bezárása automatikusan szétkapcsol minden létező kapcsolatot."), - ("android_version_audio_tip", "A jelenlegi Android verzió nem támogatja az hangrögzítést, kérlek frissíts legalább Android 10-re, vagy egy újabb verzióra."), - ("android_start_service_tip", "Nyomj a [Szolgáltatás Indítása] opcióra, vagy adj [Képernyőfotó készítése] jogot az applikációnak hogy elindítsd a képernyőmegosztó szolgáltatást."), + ("android_version_audio_tip", "A jelenlegi Android verzió nem támogatja a hangrögzítést, kérlek frissíts legalább Android 10-re, vagy egy újabb verzióra."), + ("android_start_service_tip", "Nyomj a [Szolgáltatás Indítása] opcióra, vagy adj [Képernyőrözítési] jogot az applikációnak hogy elindítsd a képernyőmegosztó szolgáltatást."), ("Account", "Fiók"), ("Overwrite", "Felülírás"), ("This file exists, skip or overwrite this file?", "Ez a fájl már létezik, skippeljünk, vagy felülírjuk ezt a fájlt?"), @@ -285,4 +285,4 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Out privacy mode", "Kilépés a privát módból"), ("Language", "Nyelv"), ].iter().cloned().collect(); -} \ No newline at end of file +} From 168fb76bf319e2c3c0557a1a86fa046db6c3cc18 Mon Sep 17 00:00:00 2001 From: Simon <67807644+KoxSosen@users.noreply.github.com> Date: Wed, 13 Jul 2022 10:24:51 +0200 Subject: [PATCH 07/50] Further fixes --- src/lang/hu.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lang/hu.rs b/src/lang/hu.rs index f4453cbe0..b14a3f415 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -51,7 +51,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Cancel", "Mégsem"), ("Skip", "Kihagy"), ("Close", "Bezár"), - ("Retry", "Újrapróbál"), + ("Retry", "Újrapróbálkozás"), ("OK", "OK"), ("Password Required", "A jelszó megadása kötelező"), ("Please enter your password", "Kérlek írd be a jelszavad"), @@ -262,7 +262,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("android_input_permission_tip1", "Ahhoz hogy egy távoli eszköz kontolálhassa az Android eszközödet egérrel vagy érintéssel, jogot kell adnod a RustDesk-nek, hogy használja az \"Elérhetőségi\" szolgáltatást."), ("android_input_permission_tip2", "Kérlek navigálj a rendszer beállításaihoz, keresd meg vagy írd be hogy [Feltelepített Szolgáltatások], és kapcsold be a [RustDesk Input] szolgáltatást."), ("android_new_connection_tip", "Új kontrollálási kérés érkezett, amely irányítani szeretné az eszközöded."), - ("android_service_will_start_tip, "A "\Képernyőrögzítés\" engedélyezése automatikusan elindítja majd a szolgáltatást, amely megengedi más eszközöknek hogy kérést kezdeményezzenek az eszköz felé."), + ("android_service_will_start_tip, "A \"Képernyőrögzítés\" engedélyezése automatikusan elindítja majd a szolgáltatást, amely megengedi más eszközöknek hogy kérést kezdeményezzenek az eszköz felé."), ("android_stop_service_tip", "A szolgáltatás bezárása automatikusan szétkapcsol minden létező kapcsolatot."), ("android_version_audio_tip", "A jelenlegi Android verzió nem támogatja a hangrögzítést, kérlek frissíts legalább Android 10-re, vagy egy újabb verzióra."), ("android_start_service_tip", "Nyomj a [Szolgáltatás Indítása] opcióra, vagy adj [Képernyőrözítési] jogot az applikációnak hogy elindítsd a képernyőmegosztó szolgáltatást."), @@ -277,7 +277,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Someone turns on privacy mode, exit", "Valaki bekacsolta a privát módot, lépj ki"), ("Unsupported", "Nem támogatott"), ("Peer denied", "Elutasítva a távoli fél álltal"), - ("Please install plugins", "Kérek telepítsd a pluginokat"), + ("Please install plugins", "Kérlek telepítsd a pluginokat"), ("Peer exit", "A távoli fél kilépett"), ("Failed to turn off", "Nem tudtuk kikapcsolni"), ("Turned off", "Kikapcsolva"), From a08cdd96edc9007beb4800d240e86b8f98c8e2e0 Mon Sep 17 00:00:00 2001 From: KoxSosen <67807644+KoxSosen@users.noreply.github.com> Date: Wed, 13 Jul 2022 15:18:59 +0200 Subject: [PATCH 08/50] More fixes, Hungarian README --- README-HU.md | 182 +++++++++++++++++++++++++++++++++++++++++++++++++ src/lang/hu.rs | 20 +++--- 2 files changed, 192 insertions(+), 10 deletions(-) create mode 100644 README-HU.md diff --git a/README-HU.md b/README-HU.md new file mode 100644 index 000000000..c699a61f5 --- /dev/null +++ b/README-HU.md @@ -0,0 +1,182 @@ +

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

+ +Beszélgess velünk: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk) + +[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) + +Még egy távoli elérésű asztali szoftwer, Rust-ban írva. Működik mindenféle konfiguráció nélkül, feltelepítéssel, vagy a nélkül. A saját adataidat teljesen te kezeled, nincs szükség aggódásra a biztonságuk felől. Használhatod a RustDesk punblikus randevú/relay szerverét, vagy szervereit, [hostolhatsz sajátot](https://rustdesk.com/server), vagy akár [írhatsz is egyet](https://github.com/rustdesk/rustdesk-server-demo). + +![image](https://user-images.githubusercontent.com/71636191/171661982-430285f0-2e12-4b1d-9957-4a58e375304d.png) + +A RustDesk szívesen fogad hozzájárulást, támogatást mindenkitől. Lásd a [`CONTRIBUTING.md`](CONTRIBUTING.md) fájlt a kezdéshez. + +[**Hogyan működik a RustDesk?**](https://github.com/rustdesk/rustdesk/wiki/How-does-RustDesk-work%3F) + +[**BINARY LELTÖLTÉS**](https://github.com/rustdesk/rustdesk/releases) + +[Get it on F-Droid](https://f-droid.org/en/packages/com.carriez.flutter_hbb) + +## Ingyenes publikus szerverek + +Ezalatt az üzenet alatt találhatóak azok a publikus szerverek, amelyeket ingyen használhatsz. Ezek a szerverek változhatnak a jövőben, illetve a hálózatuk lehet hogy lassú lehet. +| Hely | Host | Specifikáció | +| --------- | ------------- | ------------------ | +| Seoul | AWS lightsail | 1 VCPU / 0.5GB RAM | +| Singapore | Vultr | 1 VCPU / 1GB RAM | +| Dallas | Vultr | 1 VCPU / 1GB RAM | | + +## Dependencies + +Az asztali verziók [sciter](https://sciter.com/)-t használnak a GUI-hoz, kérlek telepítsd a dynamikus könyvtárat magad. + +[Windows](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x64/sciter.dll) | +[Linux](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so) | +[MacOS](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.osx/libsciter.dylib) + +A telefonos verziók Flutter-t hasznának. Később lehetséges hogy Sciterről Flutterre migrálunk az asztali verziókban is. + +## Építési pontok + +- Készítsd elő a Rust, C++ fejlesztői környezetet (env) + +- Telepítsd a [vcpkg](https://github.com/microsoft/vcpkg)-t, és állítsd be a `VCPKG_ROOT` környezeti változót helyesen + + - Windows: vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static + - Linux/MacOS: vcpkg install libvpx libyuv opus + +- Futtasd a `cargo run` parancsot + +## [Építés](https://rustdesk.com/docs/en/dev/build/) + +## Hogyan építs Linuxon + +### Ubuntu 18 (Debian 10) + +```sh +sudo apt install -y g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake +``` + +### Fedora 28 (CentOS 8) + +```sh +sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libxdo-devel libXfixes-devel pulseaudio-libs-devel cmake alsa-lib-devel +``` + +### Arch (Manjaro) + +```sh +sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio +``` + +### Telepítsd a pynput csomagot + +```sh +pip3 install pynput +``` + +### Telepítsd a vcpkg-t + +```sh +git clone https://github.com/microsoft/vcpkg +cd vcpkg +git checkout 2021.12.01 +cd .. +vcpkg/bootstrap-vcpkg.sh +export VCPKG_ROOT=$HOME/vcpkg +vcpkg/vcpkg install libvpx libyuv opus +``` + +### Fixeld a libvpx-t (Fedora-n csak) + +```sh +cd vcpkg/buildtrees/libvpx/src +cd * +./configure +sed -i 's/CFLAGS+=-I/CFLAGS+=-fPIC -I/g' Makefile +sed -i 's/CXXFLAGS+=-I/CXXFLAGS+=-fPIC -I/g' Makefile +make +cp libvpx.a $HOME/vcpkg/installed/x64-linux/lib/ +cd +``` + +### Építés + +```sh +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +source $HOME/.cargo/env +git clone https://github.com/rustdesk/rustdesk +cd rustdesk +mkdir -p target/debug +wget https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so +mv libsciter-gtk.so target/debug +VCPKG_ROOT=$HOME/vcpkg cargo run +``` + +### Válts Wayland-ról X11-re (Xorg) + +A RustDesk nem támogatja a Waylendet. [Itt](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) található egy tutorial amelynek segítségével beállíthatod a Xorg-ot mint alap GNOME session. + +## Hogyan építs Dockerrel + +Kezdjünk a repo clónozásával, majd pedig a Docker container megépítésével: + +```sh +git clone https://github.com/rustdesk/rustdesk +cd rustdesk +docker build -t "rustdesk-builder" . +``` + +Ezután, minden egyes alkalommal amikor meg kell építened a RustDesk-et, futtasd a kövezkező parancsot: + +```sh +docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder +``` + +Fontos, hogy az első építés lehet hogy több ideig fog tartani mint a következőek, mivel a dependenciek még nincsenek cachelve. Emelett, ha esetleg szeretnél valamilyen argumentumot hozzáadni az építő parancshoz, akkor megteheted a paracssor végén, a `` opció használatával. Például ha egy optimalizált release éptést szeretnél megépíteni, akkor add hozzá a fenti parancsorhoz a `--release` opciót. A futtatható binary elérhető lesz a target mappában a rendszereden, futtatni a következőképpen tudod: + +```sh +target/debug/rustdesk +``` + +Vagy ha release binary, akkor: + +```sh +target/release/rustdesk +``` + +Kérlek mindenképpen nézd meg hogy ezeket a parancsokat a root RustDesk mappában futtatod e, különben a RustDesk lehet hogy nem fogja megtalálni az építéshez szükséges elemeket. Fontos az is, hogy jelenleg más cargo subparancsok, például `install`vagy `run` nem támogatottak, mivel egy Dockeres építés esetén elindítanák a programot a containeren belül. + + +## Fájl Struktúra + +- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: video codec, config, tcp/udp wrapper, protobuf, fs functions for file transfer, and some other utility functions +- **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: screen capture +- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: platform specific keyboard/mouse control +- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: GUI +- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: audio/clipboard/input/video services, and network connections +- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: start a peer connection +- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: Communicate with [rustdesk-server](https://github.com/rustdesk/rustdesk-server), wait for remote direct (TCP hole punching) or relayed connection +- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: platform specific code +- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: Flutter code for mobile +- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: Javascript for Flutter web client + +## Képernyőképek + +![image](https://user-images.githubusercontent.com/71636191/113112362-ae4deb80-923b-11eb-957d-ff88daad4f06.png) + +![image](https://user-images.githubusercontent.com/71636191/113112619-f705a480-923b-11eb-911d-97e984ef52b6.png) + +![image](https://user-images.githubusercontent.com/71636191/113112857-3fbd5d80-923c-11eb-9836-768325faf906.png) + +![image](https://user-images.githubusercontent.com/71636191/135385039-38fdbd72-379a-422d-b97f-33df71fb1cec.png) diff --git a/src/lang/hu.rs b/src/lang/hu.rs index b14a3f415..ccf221f3e 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -101,8 +101,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Unblock user input", "Felhasználói input blokkolásának feloldása"), ("Adjust Window", "Ablakméret beállítása"), ("Original", "Eredeti"), - ("Shrink", "Zsugorítás"), - ("Stretch", "Nyújtás"), + ("Shrink", "Zsugorított"), + ("Stretch", "Nyújtott"), ("Good image quality", "Jó képminőség"), ("Balanced", "Balanszolt"), ("Optimize reaction time", "Válaszidő optimializálása"), @@ -137,7 +137,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Install", "Telepítés"), ("Installation", "Telepítés"), ("Installation Path", "Telepítési útvonal"), - ("Create start menu shortcuts", "Smart menu parancsikon létrehozása"), + ("Create start menu shortcuts", "Start menu parancsikon létrehozása"), ("Create desktop icon", "Asztali icon létrehozása"), ("agreement_tip", "Azzal hogy elindítod a telepítést, elfogadod a licenszszerződést."), ("Accept and Install", "Elfogadás és Telepítés"), @@ -176,7 +176,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Rename", "Átnevezés"), ("Space", "Hely"), ("Create Desktop Shortcut", "Asztali Parancsikon Lértehozása"), - ("Change Path", "Elérési Útvonal Megváltoztatása"), + ("Change Path", "Útvonal Megváltoztatása"), ("Create Folder", "Mappa Készítése"), ("Please enter the folder name", "Kérlek írd be a mappa nevét"), ("Fix it", "Kérlek javísd meg"), @@ -198,7 +198,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Login", "Belépés"), ("Logout", "Kilépés"), ("Tags", "Tagok"), - ("Search ID", "ID keresése"), + ("Search ID", "ID keresés"), ("Current Wayland display server is not supported", "Jelenleg a Wayland display szerver nem támogatott"), ("whitelist_sep", "Ide jönnek a címek, vesző, pontosvessző, space, vagy új sorral elválasztva"), ("Add ID", "ID Hozzáadása"), @@ -217,7 +217,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Invalid folder name", "Helytelen fájlnév"), ("Socks5 Proxy", "Socks5-ös Proxy"), ("Hostname", "Hostnév"), - ("Discovered", "Felfedezett"), + ("Discovered", "Felfedezés"), ("install_daemon_tip", "Ahhoz hogy a RustDesk bootkor elinduljon, telepítened kell a rendszer szolgáltatást."), ("Remote ID", "Távoli ID"), ("Paste", "Beillesztés"), @@ -237,10 +237,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Three-Finger vertically", "Három ujj függőlegesen"), ("Mouse Wheel", "Egérgörgő"), ("Two-Finger Move", "Kátújas mozgatás"), - ("Canvas Move", "Vászon mozgatása"), + ("Canvas Move", "Nézet Mozgatása"), ("Pinch to Zoom", "Húzd össze a nagyításhoz"), - ("Canvas Zoom", "Vászon Nagyítása"), - ("Reset canvas", "Vászon visszaállítása"), + ("Canvas Zoom", "Nézet Nagyítása"), + ("Reset canvas", "Nézet visszaállítása"), ("No permission of file transfer", "Nincs jogod fájl transzer indításához"), ("Note", "Megyjegyzés"), ("Connection", "Kapcsolat"), @@ -255,7 +255,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Input Control", "Input Kontrol"), ("Audio Capture", "Audió Rögzítés"), ("File Connection", "Fájlkapcsolat"), - ("Screen Connection", "kijelzőkapcsolat"), + ("Screen Connection", "Új Vizuális Kapcsolat"), ("Do you accept?", "Elfogadod?"), ("Open System Setting", "Rendszer beállítások megnyitása"), ("How to get Android input permission?", "Hogyan állíthatok be Android input jogokat?"), From 121d3bb2b435dc6230337f6f397b492724038fb6 Mon Sep 17 00:00:00 2001 From: KoxSosen <67807644+KoxSosen@users.noreply.github.com> Date: Wed, 13 Jul 2022 15:25:58 +0200 Subject: [PATCH 09/50] Add HU and sync languages in READMEs --- README-AR.md | 2 +- README-CS.md | 2 +- README-DE.md | 2 +- README-EO.md | 2 +- README-ES.md | 2 +- README-FA.md | 2 +- README-FI.md | 2 +- README-FR.md | 2 +- README-HU.md | 6 +++--- README-ID.md | 2 +- README-IT.md | 2 +- README-JP.md | 2 +- README-KR.md | 2 +- README-ML.md | 2 +- README-NL.md | 2 +- README-PL.md | 2 +- README-PTBR.md | 2 +- README-RU.md | 2 +- README-ZH.md | 2 +- README.md | 2 +- 20 files changed, 22 insertions(+), 22 deletions(-) diff --git a/README-AR.md b/README-AR.md index 055a654d2..636d2611e 100644 --- a/README-AR.md +++ b/README-AR.md @@ -5,7 +5,7 @@ DockerStructureSnapshot
- [česky] | [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
+ [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
لغتك الأم, Doc و RustDesk UI, README نحن بحاجة إلى مساعدتك لترجمة هذا

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

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

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

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

diff --git a/README-FA.md b/README-FA.md index 0f7ca1a95..033709a80 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] | [中文] | | [Magyar] | [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 a2d7534e0..27ccb75b6 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] | [한국어] | [العربي]
+ [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
Tarvitsemme apua tämän README-tiedoston kääntämiseksi äidinkielellesi

diff --git a/README-FR.md b/README-FR.md index b1f8e3670..1709c3274 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] | [한국어] | [العربي]
+ [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
Nous avons besoin de votre aide pour traduire ce README dans votre langue maternelle.

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

@@ -13,7 +13,7 @@ Beszélgess velünk: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https: [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) -Még egy távoli elérésű asztali szoftwer, Rust-ban írva. Működik mindenféle konfiguráció nélkül, feltelepítéssel, vagy a nélkül. A saját adataidat teljesen te kezeled, nincs szükség aggódásra a biztonságuk felől. Használhatod a RustDesk punblikus randevú/relay szerverét, vagy szervereit, [hostolhatsz sajátot](https://rustdesk.com/server), vagy akár [írhatsz is egyet](https://github.com/rustdesk/rustdesk-server-demo). +Még egy távoli elérésű asztali szoftver, Rust-ban írva. Működik mindenféle konfiguráció nélkül, feltelepítéssel, vagy a nélkül. A saját adataidat teljesen te kezeled, nincs szükség aggódásra a biztonságuk felől. Használhatod a RustDesk punblikus randevú/relay szerverét, vagy szervereit, [hostolhatsz sajátot](https://rustdesk.com/server), vagy akár [írhatsz is egyet](https://github.com/rustdesk/rustdesk-server-demo). ![image](https://user-images.githubusercontent.com/71636191/171661982-430285f0-2e12-4b1d-9957-4a58e375304d.png) diff --git a/README-ID.md b/README-ID.md index 624336f45..632271cd4 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] | [한국어] | [العربي]
+ [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
Kami membutuhkan bantuan Anda untuk menerjemahkan README ini dan RustDesk UI ke bahasa asli anda

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

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

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

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

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

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

diff --git a/README-PTBR.md b/README-PTBR.md index 76b360283..bb6a25cbf 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] | [한국어] | [العربي]
+ [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
Precisamos de sua ajuda para traduzir este README e a UI do RustDesk para sua língua nativa

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

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

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

From 01fc7521ff3a3a9fac5d5a8238b76870aae50833 Mon Sep 17 00:00:00 2001 From: KoxSosen <67807644+KoxSosen@users.noreply.github.com> Date: Wed, 13 Jul 2022 17:00:18 +0200 Subject: [PATCH 10/50] Finishing --- README-HU.md | 8 ++++---- src/lang/hu.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README-HU.md b/README-HU.md index 088599701..eeeaaa37d 100644 --- a/README-HU.md +++ b/README-HU.md @@ -13,11 +13,11 @@ Beszélgess velünk: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https: [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) -Még egy távoli elérésű asztali szoftver, Rust-ban írva. Működik mindenféle konfiguráció nélkül, feltelepítéssel, vagy a nélkül. A saját adataidat teljesen te kezeled, nincs szükség aggódásra a biztonságuk felől. Használhatod a RustDesk punblikus randevú/relay szerverét, vagy szervereit, [hostolhatsz sajátot](https://rustdesk.com/server), vagy akár [írhatsz is egyet](https://github.com/rustdesk/rustdesk-server-demo). +A RustDesk egy távoli elérésű asztali szoftver, Rust-ban írva. Működik mindenféle konfiguráció nélkül, feltelepítéssel, vagy anélkül. Az adataidat teljesen te kezeled, nincs szükség aggódásra a harmadik felek miatt. Használhatod a RustDesk punblikus randevú/relay szervereit, [hostolhatsz sajátot](https://rustdesk.com/server), vagy akár [írhatsz is egyet](https://github.com/rustdesk/rustdesk-server-demo). ![image](https://user-images.githubusercontent.com/71636191/171661982-430285f0-2e12-4b1d-9957-4a58e375304d.png) -A RustDesk szívesen fogad hozzájárulást, támogatást mindenkitől. Lásd a [`CONTRIBUTING.md`](CONTRIBUTING.md) fájlt a kezdéshez. +A RustDesk szívesen fogad minden contributiont, támogatást mindenkitől. Lásd a [`CONTRIBUTING.md`](CONTRIBUTING.md) fájlt a kezdéshez. [**Hogyan működik a RustDesk?**](https://github.com/rustdesk/rustdesk/wiki/How-does-RustDesk-work%3F) @@ -57,7 +57,7 @@ A telefonos verziók Flutter-t hasznának. Később lehetséges hogy Sciterről - Futtasd a `cargo run` parancsot -## [Építés](https://rustdesk.com/docs/en/dev/build/) +## [Építés](https://rustdesk.com/docs/hu/dev/build/) ## Hogyan építs Linuxon @@ -143,7 +143,7 @@ Ezután, minden egyes alkalommal amikor meg kell építened a RustDesk-et, futta docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder ``` -Fontos, hogy az első építés lehet hogy több ideig fog tartani mint a következőek, mivel a dependenciek még nincsenek cachelve. Emelett, ha esetleg szeretnél valamilyen argumentumot hozzáadni az építő parancshoz, akkor megteheted a paracssor végén, a `` opció használatával. Például ha egy optimalizált release éptést szeretnél megépíteni, akkor add hozzá a fenti parancsorhoz a `--release` opciót. A futtatható binary elérhető lesz a target mappában a rendszereden, futtatni a következőképpen tudod: +Fontos, hogy az első építés lehet hogy több ideig fog tartani mint a következőek, mivel a dependenciek még nincsenek cachelve. Emelett, ha esetleg szeretnél valamilyen argumentumot hozzáadni az építő parancshoz, akkor megteheted a paracssor végén, a `` argumentum használatával. Például ha egy optimalizált release éptést szeretnél megépíteni, akkor add hozzá a fenti parancsorhoz a `--release` opciót. A futtatható binary elérhető lesz a target mappában a rendszereden, futtatni a következőképpen tudod: ```sh target/debug/rustdesk diff --git a/src/lang/hu.rs b/src/lang/hu.rs index ccf221f3e..d6a656709 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -270,7 +270,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Overwrite", "Felülírás"), ("This file exists, skip or overwrite this file?", "Ez a fájl már létezik, skippeljünk, vagy felülírjuk ezt a fájlt?"), ("Quit", "Kilépés"), - ("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"), + ("doc_mac_permission", "https://rustdesk.com/docs/hu/manual/mac/#enable-permissions"), ("Help", "Segítség"), ("Failed", "Sikertelen"), ("Succeeded", "Sikeres"), From 7a00b927ce8569beeccbf9dafb0f4c2ac85bc648 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 13 Jul 2022 23:38:35 +0800 Subject: [PATCH 11/50] typo --- src/lang/hu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/hu.rs b/src/lang/hu.rs index d6a656709..e7a95870e 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -262,7 +262,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("android_input_permission_tip1", "Ahhoz hogy egy távoli eszköz kontolálhassa az Android eszközödet egérrel vagy érintéssel, jogot kell adnod a RustDesk-nek, hogy használja az \"Elérhetőségi\" szolgáltatást."), ("android_input_permission_tip2", "Kérlek navigálj a rendszer beállításaihoz, keresd meg vagy írd be hogy [Feltelepített Szolgáltatások], és kapcsold be a [RustDesk Input] szolgáltatást."), ("android_new_connection_tip", "Új kontrollálási kérés érkezett, amely irányítani szeretné az eszközöded."), - ("android_service_will_start_tip, "A \"Képernyőrögzítés\" engedélyezése automatikusan elindítja majd a szolgáltatást, amely megengedi más eszközöknek hogy kérést kezdeményezzenek az eszköz felé."), + ("android_service_will_start_tip", "A \"Képernyőrögzítés\" engedélyezése automatikusan elindítja majd a szolgáltatást, amely megengedi más eszközöknek hogy kérést kezdeményezzenek az eszköz felé."), ("android_stop_service_tip", "A szolgáltatás bezárása automatikusan szétkapcsol minden létező kapcsolatot."), ("android_version_audio_tip", "A jelenlegi Android verzió nem támogatja a hangrögzítést, kérlek frissíts legalább Android 10-re, vagy egy újabb verzióra."), ("android_start_service_tip", "Nyomj a [Szolgáltatás Indítása] opcióra, vagy adj [Képernyőrözítési] jogot az applikációnak hogy elindítsd a képernyőmegosztó szolgáltatást."), From bb8257b7f29a8a6f3ee7d6f1fd4e7992846a2341 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 14 Jul 2022 15:21:26 +0800 Subject: [PATCH 12/50] https://github.com/rustdesk/rustdesk/issues/899 --- src/client.rs | 22 +++++++++++--- src/common.rs | 62 ++++++++++++++++++++++++++++++-------- src/ipc.rs | 30 +++++++++++++++--- src/mobile.rs | 1 - src/rendezvous_mediator.rs | 7 +++-- 5 files changed, 98 insertions(+), 24 deletions(-) diff --git a/src/client.rs b/src/client.rs index d63ce970c..c6ccd2c26 100644 --- a/src/client.rs +++ b/src/client.rs @@ -148,11 +148,25 @@ impl Client { true, )); } - let rendezvous_server = crate::get_rendezvous_server(1_000).await; - log::info!("rendezvous server: {}", rendezvous_server); - + let (mut rendezvous_server, servers, contained) = crate::get_rendezvous_server(1_000).await; let mut socket = - socket_client::connect_tcp(&*rendezvous_server, any_addr, RENDEZVOUS_TIMEOUT).await?; + socket_client::connect_tcp(&*rendezvous_server, any_addr, RENDEZVOUS_TIMEOUT).await; + debug_assert!(!servers.contains(&rendezvous_server)); + if socket.is_err() && !servers.is_empty() { + log::info!("try the other servers: {:?}", servers); + for server in servers { + socket = socket_client::connect_tcp(&*server, any_addr, RENDEZVOUS_TIMEOUT).await; + if socket.is_ok() { + rendezvous_server = server; + break; + } + } + crate::refresh_rendezvous_server(); + } else if !contained { + crate::refresh_rendezvous_server(); + } + log::info!("rendezvous server: {}", rendezvous_server); + let mut socket = socket?; let my_addr = socket.local_addr(); let mut signed_id_pk = Vec::new(); let mut relay_server = "".to_owned(); diff --git a/src/common.rs b/src/common.rs index 667dd3e7e..5ef127f46 100644 --- a/src/common.rs +++ b/src/common.rs @@ -12,7 +12,6 @@ use hbb_common::{ rendezvous_proto::*, sleep, socket_client, tokio, ResultType, }; -#[cfg(any(target_os = "android", target_os = "ios", feature = "cli"))] use hbb_common::{config::RENDEZVOUS_PORT, futures::future::join_all}; use std::sync::{Arc, Mutex}; @@ -247,7 +246,7 @@ async fn test_nat_type_() -> ResultType { return Ok(true); } let start = std::time::Instant::now(); - let rendezvous_server = get_rendezvous_server(1_000).await; + let (rendezvous_server, _, _) = get_rendezvous_server(1_000).await; let server1 = rendezvous_server; let tmp: Vec<&str> = server1.split(":").collect(); if tmp.len() != 2 { @@ -316,31 +315,59 @@ async fn test_nat_type_() -> ResultType { Ok(ok) } -#[cfg(any(target_os = "android", target_os = "ios"))] -pub async fn get_rendezvous_server(_ms_timeout: u64) -> String { - Config::get_rendezvous_server() +pub async fn get_rendezvous_server(ms_timeout: u64) -> (String, Vec, bool) { + let (mut a, mut b) = get_rendezvous_server_(ms_timeout).await; + let mut b: Vec = b + .drain(..) + .map(|x| { + if !x.contains(":") { + format!("{}:{}", x, config::RENDEZVOUS_PORT) + } else { + x + } + }) + .collect(); + let c = if b.contains(&a) { + b = b.drain(..).filter(|x| x != &a).collect(); + true + } else { + a = b.pop().unwrap_or(a); + false + }; + (a, b, c) } +#[inline] +#[cfg(any(target_os = "android", target_os = "ios"))] +fn get_rendezvous_server_(_ms_timeout: u64) -> (String, Vec) { + ( + Config::get_rendezvous_server(), + Config::get_rendezvous_servers(), + ) +} + +#[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] -pub async fn get_rendezvous_server(ms_timeout: u64) -> String { +async fn get_rendezvous_server_(ms_timeout: u64) -> (String, Vec) { crate::ipc::get_rendezvous_server(ms_timeout).await } +#[inline] #[cfg(any(target_os = "android", target_os = "ios"))] pub async fn get_nat_type(_ms_timeout: u64) -> i32 { Config::get_nat_type() } +#[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] pub async fn get_nat_type(ms_timeout: u64) -> i32 { crate::ipc::get_nat_type(ms_timeout).await } -#[cfg(any(target_os = "android", target_os = "ios", feature = "cli"))] #[tokio::main(flavor = "current_thread")] async fn test_rendezvous_server_() { let servers = Config::get_rendezvous_servers(); - hbb_common::config::ONLINE.lock().unwrap().clear(); + Config::reset_online(); let mut futs = Vec::new(); for host in servers { futs.push(tokio::spawn(async move { @@ -363,11 +390,21 @@ async fn test_rendezvous_server_() { join_all(futs).await; } -#[cfg(any(target_os = "android", target_os = "ios", feature = "cli"))] pub fn test_rendezvous_server() { std::thread::spawn(test_rendezvous_server_); } +pub fn refresh_rendezvous_server() { + #[cfg(any(target_os = "android", target_os = "ios", feature = "cli"))] + test_rendezvous_server(); + #[cfg(not(any(target_os = "android", target_os = "ios", feature = "cli")))] + std::thread::spawn(|| { + if crate::ipc::test_rendezvous_server().is_err() { + test_rendezvous_server(); + } + }); +} + #[inline] pub fn get_time() -> i64 { std::time::SystemTime::now() @@ -437,14 +474,15 @@ pub fn is_modifier(evt: &KeyEvent) -> bool { } pub fn check_software_update() { - std::thread::spawn(move || allow_err!(_check_software_update())); + std::thread::spawn(move || allow_err!(check_software_update_())); } #[tokio::main(flavor = "current_thread")] -async fn _check_software_update() -> hbb_common::ResultType<()> { +async fn check_software_update_() -> hbb_common::ResultType<()> { sleep(3.).await; - let rendezvous_server = socket_client::get_target_addr(&get_rendezvous_server(1_000).await)?; + let rendezvous_server = + socket_client::get_target_addr(&format!("rs-sg.rustdesk.com:{}", config::RENDEZVOUS_PORT))?; let mut socket = socket_client::new_udp(Config::get_any_listen_addr(), RENDEZVOUS_TIMEOUT).await?; diff --git a/src/ipc.rs b/src/ipc.rs index 24a156eba..7df06cd22 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -127,6 +127,7 @@ pub enum Data { ClipbaordFile(ClipbaordFile), ClipboardFileEnabled(bool), PrivacyModeState((i32, PrivacyModeState)), + TestRendezvousServer, } #[tokio::main(flavor = "current_thread")] @@ -286,7 +287,11 @@ async fn handle(data: Data, stream: &mut Connection) { } else if name == "salt" { value = Some(Config::get_salt()); } else if name == "rendezvous_server" { - value = Some(Config::get_rendezvous_server()); + value = Some(format!( + "{},{}", + Config::get_rendezvous_server(), + Config::get_rendezvous_servers().join(",") + )); } else if name == "rendezvous_servers" { value = Some(Config::get_rendezvous_servers().join(",")); } else { @@ -336,7 +341,9 @@ async fn handle(data: Data, stream: &mut Connection) { .await ); } - + Data::TestRendezvousServer => { + crate::test_rendezvous_server(); + } _ => {} } } @@ -520,11 +527,17 @@ pub fn get_password() -> String { } } -pub async fn get_rendezvous_server(ms_timeout: u64) -> String { +pub async fn get_rendezvous_server(ms_timeout: u64) -> (String, Vec) { if let Ok(Some(v)) = get_config_async("rendezvous_server", ms_timeout).await { - v + let mut urls = v.split(","); + let a = urls.next().unwrap_or_default().to_owned(); + let b: Vec = urls.map(|x| x.to_owned()).collect(); + (a, b) } else { - Config::get_rendezvous_server() + ( + Config::get_rendezvous_server(), + Config::get_rendezvous_servers(), + ) } } @@ -636,3 +649,10 @@ pub async fn set_socks(value: config::Socks5Server) -> ResultType<()> { .await?; Ok(()) } + +#[tokio::main(flavor = "current_thread")] +pub async fn test_rendezvous_server() -> ResultType<()> { + let mut c = connect(1000, "").await?; + c.send(&Data::TestRendezvousServer).await?; + Ok(()) +} diff --git a/src/mobile.rs b/src/mobile.rs index 5b9651e54..942510b17 100644 --- a/src/mobile.rs +++ b/src/mobile.rs @@ -591,7 +591,6 @@ impl Connection { log::debug!("Exit io_loop of id={}", session.id); } Err(err) => { - crate::common::test_rendezvous_server(); session.msgbox("error", "Connection Error", &err.to_string()); } } diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index a7f90b977..e2397fa16 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -51,9 +51,12 @@ impl RendezvousMediator { check_zombie(); let server = new_server(); if Config::get_nat_type() == NatType::UNKNOWN_NAT as i32 { - crate::common::test_nat_type(); + crate::test_nat_type(); nat_tested = true; } + if !Config::get_option("stop-service").is_empty() { + crate::test_rendezvous_server(); + } let server_cloned = server.clone(); tokio::spawn(async move { direct_server(server_cloned).await; @@ -68,7 +71,7 @@ impl RendezvousMediator { Config::reset_online(); if Config::get_option("stop-service").is_empty() { if !nat_tested { - crate::common::test_nat_type(); + crate::test_nat_type(); nat_tested = true; } let mut futs = Vec::new(); From c6c5d2cb98d91e2fb0d5b7f1f94b4f1149ece0df Mon Sep 17 00:00:00 2001 From: tom Date: Thu, 14 Jul 2022 17:20:01 +0800 Subject: [PATCH 13/50] upgrade protobuf to 3.1.0 and use with-bytes feature, issues:958 --- libs/hbb_common/Cargo.toml | 4 +- libs/hbb_common/build.rs | 3 +- libs/hbb_common/src/fs.rs | 4 +- libs/hbb_common/src/lib.rs | 7 ++- libs/scrap/src/common/codec.rs | 6 +-- src/client.rs | 16 +++---- src/client/helper.rs | 6 +-- src/clipboard_file.rs | 36 +++++++-------- src/common.rs | 12 ++--- src/port_forward.rs | 10 ++--- src/rendezvous_mediator.rs | 18 ++++---- src/server.rs | 4 +- src/server/connection.rs | 64 +++++++++++++-------------- src/server/input_service.rs | 22 +++++----- src/ui.rs | 2 +- src/ui/cm.rs | 2 +- src/ui/remote.rs | 80 +++++++++++++++++----------------- 17 files changed, 148 insertions(+), 148 deletions(-) diff --git a/libs/hbb_common/Cargo.toml b/libs/hbb_common/Cargo.toml index bc31223cc..000cbae96 100644 --- a/libs/hbb_common/Cargo.toml +++ b/libs/hbb_common/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -protobuf = "3.0.0-alpha.2" +protobuf = { version = "3.1.0", features = ["with-bytes"] } tokio = { version = "1.15", features = ["full"] } tokio-util = { version = "0.6", features = ["full"] } futures = "0.3" @@ -38,7 +38,7 @@ mac_address = "1.1" quic = [] [build-dependencies] -protobuf-codegen-pure = "3.0.0-alpha.2" +protobuf-codegen = { version = "3.1.0" } [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3", features = ["winuser"] } diff --git a/libs/hbb_common/build.rs b/libs/hbb_common/build.rs index 99dacb7ec..5c1c6af22 100644 --- a/libs/hbb_common/build.rs +++ b/libs/hbb_common/build.rs @@ -1,6 +1,7 @@ fn main() { std::fs::create_dir_all("src/protos").unwrap(); - protobuf_codegen_pure::Codegen::new() + protobuf_codegen::Codegen::new() + .pure() .out_dir("src/protos") .inputs(&["protos/rendezvous.proto", "protos/message.proto"]) .include("protos") diff --git a/libs/hbb_common/src/fs.rs b/libs/hbb_common/src/fs.rs index 4512ce940..4880b4622 100644 --- a/libs/hbb_common/src/fs.rs +++ b/libs/hbb_common/src/fs.rs @@ -573,7 +573,7 @@ impl TransferJob { log::info!("file num truncated, ignoring"); } else { match r.union { - Some(file_transfer_send_confirm_request::Union::skip(s)) => { + Some(file_transfer_send_confirm_request::Union::Skip(s)) => { if s { log::debug!("skip file id:{}, file_num:{}", r.id, r.file_num); self.skip_current_file(); @@ -581,7 +581,7 @@ impl TransferJob { self.set_file_confirmed(true); } } - Some(file_transfer_send_confirm_request::Union::offset_blk(_offset)) => { + Some(file_transfer_send_confirm_request::Union::OffsetBlk(_offset)) => { self.set_file_confirmed(true); } _ => {} diff --git a/libs/hbb_common/src/lib.rs b/libs/hbb_common/src/lib.rs index 0a9dace0c..07c6a6868 100644 --- a/libs/hbb_common/src/lib.rs +++ b/libs/hbb_common/src/lib.rs @@ -1,8 +1,7 @@ pub mod compress; -#[path = "./protos/message.rs"] -pub mod message_proto; -#[path = "./protos/rendezvous.rs"] -pub mod rendezvous_proto; +pub mod protos; +pub use protos::message as message_proto; +pub use protos::rendezvous as rendezvous_proto; pub use bytes; pub use futures; pub use protobuf; diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index e06c0e29a..1d9deba68 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -251,11 +251,11 @@ impl Decoder { rgb: &mut Vec, ) -> ResultType { match frame { - video_frame::Union::vp9s(vp9s) => { + video_frame::Union::Vp9s(vp9s) => { Decoder::handle_vp9s_video_frame(&mut self.vpx, vp9s, rgb) } #[cfg(feature = "hwcodec")] - video_frame::Union::h264s(h264s) => { + video_frame::Union::H264s(h264s) => { if let Some(decoder) = &mut self.hw.h264 { Decoder::handle_hw_video_frame(decoder, h264s, rgb, &mut self.i420) } else { @@ -263,7 +263,7 @@ impl Decoder { } } #[cfg(feature = "hwcodec")] - video_frame::Union::h265s(h265s) => { + video_frame::Union::H265s(h265s) => { if let Some(decoder) = &mut self.hw.h265 { Decoder::handle_hw_video_frame(decoder, h265s, rgb, &mut self.i420) } else { diff --git a/src/client.rs b/src/client.rs index d63ce970c..33d966d2a 100644 --- a/src/client.rs +++ b/src/client.rs @@ -165,7 +165,7 @@ impl Client { for i in 1..=3 { log::info!("#{} punch attempt with {}, id: {}", i, my_addr, peer); let mut msg_out = RendezvousMessage::new(); - use hbb_common::protobuf::ProtobufEnum; + use hbb_common::protobuf::Enum; let nat_type = NatType::from_i32(my_nat_type).unwrap_or(NatType::UNKNOWN_NAT); msg_out.set_punch_hole_request(PunchHoleRequest { id: peer.to_owned(), @@ -179,7 +179,7 @@ impl Client { if let Some(Ok(bytes)) = socket.next_timeout(i * 6000).await { if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(&bytes) { match msg_in.union { - Some(rendezvous_message::Union::punch_hole_response(ph)) => { + Some(rendezvous_message::Union::PunchHoleResponse(ph)) => { if ph.socket_addr.is_empty() { if !ph.other_failure.is_empty() { bail!(ph.other_failure); @@ -199,8 +199,8 @@ impl Client { } } } else { - peer_nat_type = ph.get_nat_type(); - is_local = ph.get_is_local(); + peer_nat_type = ph.nat_type(); + is_local = ph.is_local(); signed_id_pk = ph.pk; relay_server = ph.relay_server; peer_addr = AddrMangle::decode(&ph.socket_addr); @@ -208,13 +208,13 @@ impl Client { break; } } - Some(rendezvous_message::Union::relay_response(rr)) => { + Some(rendezvous_message::Union::RelayResponse(rr)) => { log::info!( "relay requested from peer, time used: {:?}, relay_server: {}", start.elapsed(), rr.relay_server ); - signed_id_pk = rr.get_pk().into(); + signed_id_pk = rr.pk().into(); let mut conn = Self::create_relay(peer, rr.uuid, rr.relay_server, key, conn_type) .await?; @@ -383,7 +383,7 @@ impl Client { Some(res) => { let bytes = res?; if let Ok(msg_in) = Message::parse_from_bytes(&bytes) { - if let Some(message::Union::signed_id(si)) = msg_in.union { + if let Some(message::Union::SignedId(si)) = msg_in.union { if let Ok((id, their_pk_b)) = decode_id_pk(&si.id, &sign_pk) { if id == peer_id { let their_pk_b = box_::PublicKey(their_pk_b); @@ -466,7 +466,7 @@ impl Client { socket.send(&msg_out).await?; if let Some(Ok(bytes)) = socket.next_timeout(CONNECT_TIMEOUT).await { if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(&bytes) { - if let Some(rendezvous_message::Union::relay_response(rs)) = msg_in.union { + if let Some(rendezvous_message::Union::RelayResponse(rs)) = msg_in.union { if !rs.refuse_reason.is_empty() { bail!(rs.refuse_reason); } diff --git a/src/client/helper.rs b/src/client/helper.rs index ea12cb7ee..26dc37ba4 100644 --- a/src/client/helper.rs +++ b/src/client/helper.rs @@ -69,9 +69,9 @@ pub enum CodecFormat { impl From<&VideoFrame> for CodecFormat { fn from(it: &VideoFrame) -> Self { match it.union { - Some(video_frame::Union::vp9s(_)) => CodecFormat::VP9, - Some(video_frame::Union::h264s(_)) => CodecFormat::H264, - Some(video_frame::Union::h265s(_)) => CodecFormat::H265, + Some(video_frame::Union::Vp9s(_)) => CodecFormat::VP9, + Some(video_frame::Union::H264s(_)) => CodecFormat::H264, + Some(video_frame::Union::H265s(_)) => CodecFormat::H265, _ => CodecFormat::Unknown, } } diff --git a/src/clipboard_file.rs b/src/clipboard_file.rs index 39b2eb766..2f35065c9 100644 --- a/src/clipboard_file.rs +++ b/src/clipboard_file.rs @@ -17,8 +17,8 @@ pub fn clip_2_msg(clip: ClipbaordFile) -> Message { }); } Message { - union: Some(message::Union::cliprdr(Cliprdr { - union: Some(cliprdr::Union::format_list(CliprdrServerFormatList { + union: Some(message::Union::Cliprdr(Cliprdr { + union: Some(cliprdr::Union::FormatList(CliprdrServerFormatList { conn_id, formats, ..Default::default() @@ -29,8 +29,8 @@ pub fn clip_2_msg(clip: ClipbaordFile) -> Message { } } ClipbaordFile::ServerFormatListResponse { conn_id, msg_flags } => Message { - union: Some(message::Union::cliprdr(Cliprdr { - union: Some(cliprdr::Union::format_list_response( + union: Some(message::Union::Cliprdr(Cliprdr { + union: Some(cliprdr::Union::FormatListResponse( CliprdrServerFormatListResponse { conn_id, msg_flags, @@ -45,8 +45,8 @@ pub fn clip_2_msg(clip: ClipbaordFile) -> Message { conn_id, requested_format_id, } => Message { - union: Some(message::Union::cliprdr(Cliprdr { - union: Some(cliprdr::Union::format_data_request( + union: Some(message::Union::Cliprdr(Cliprdr { + union: Some(cliprdr::Union::FormatDataRequest( CliprdrServerFormatDataRequest { conn_id, requested_format_id, @@ -62,8 +62,8 @@ pub fn clip_2_msg(clip: ClipbaordFile) -> Message { msg_flags, format_data, } => Message { - union: Some(message::Union::cliprdr(Cliprdr { - union: Some(cliprdr::Union::format_data_response( + union: Some(message::Union::Cliprdr(Cliprdr { + union: Some(cliprdr::Union::FormatDataResponse( CliprdrServerFormatDataResponse { conn_id, msg_flags, @@ -86,8 +86,8 @@ pub fn clip_2_msg(clip: ClipbaordFile) -> Message { have_clip_data_id, clip_data_id, } => Message { - union: Some(message::Union::cliprdr(Cliprdr { - union: Some(cliprdr::Union::file_contents_request( + union: Some(message::Union::Cliprdr(Cliprdr { + union: Some(cliprdr::Union::FileContentsRequest( CliprdrFileContentsRequest { conn_id, stream_id, @@ -111,8 +111,8 @@ pub fn clip_2_msg(clip: ClipbaordFile) -> Message { stream_id, requested_data, } => Message { - union: Some(message::Union::cliprdr(Cliprdr { - union: Some(cliprdr::Union::file_contents_response( + union: Some(message::Union::Cliprdr(Cliprdr { + union: Some(cliprdr::Union::FileContentsResponse( CliprdrFileContentsResponse { conn_id, msg_flags, @@ -130,7 +130,7 @@ pub fn clip_2_msg(clip: ClipbaordFile) -> Message { pub fn msg_2_clip(msg: Cliprdr) -> Option { match msg.union { - Some(cliprdr::Union::format_list(data)) => { + Some(cliprdr::Union::FormatList(data)) => { let mut format_list: Vec<(i32, String)> = Vec::new(); for v in data.formats.iter() { format_list.push((v.id, v.format.clone())); @@ -140,26 +140,26 @@ pub fn msg_2_clip(msg: Cliprdr) -> Option { format_list, }) } - Some(cliprdr::Union::format_list_response(data)) => { + Some(cliprdr::Union::FormatListResponse(data)) => { Some(ClipbaordFile::ServerFormatListResponse { conn_id: data.conn_id, msg_flags: data.msg_flags, }) } - Some(cliprdr::Union::format_data_request(data)) => { + Some(cliprdr::Union::FormatDataRequest(data)) => { Some(ClipbaordFile::ServerFormatDataRequest { conn_id: data.conn_id, requested_format_id: data.requested_format_id, }) } - Some(cliprdr::Union::format_data_response(data)) => { + Some(cliprdr::Union::FormatDataResponse(data)) => { Some(ClipbaordFile::ServerFormatDataResponse { conn_id: data.conn_id, msg_flags: data.msg_flags, format_data: data.format_data, }) } - Some(cliprdr::Union::file_contents_request(data)) => { + Some(cliprdr::Union::FileContentsRequest(data)) => { Some(ClipbaordFile::FileContentsRequest { conn_id: data.conn_id, stream_id: data.stream_id, @@ -172,7 +172,7 @@ pub fn msg_2_clip(msg: Cliprdr) -> Option { clip_data_id: data.clip_data_id, }) } - Some(cliprdr::Union::file_contents_response(data)) => { + Some(cliprdr::Union::FileContentsResponse(data)) => { Some(ClipbaordFile::FileContentsResponse { conn_id: data.conn_id, msg_flags: data.msg_flags, diff --git a/src/common.rs b/src/common.rs index 667dd3e7e..9917a47f2 100644 --- a/src/common.rs +++ b/src/common.rs @@ -8,7 +8,7 @@ use hbb_common::{ get_version_number, log, message_proto::*, protobuf::Message as _, - protobuf::ProtobufEnum, + protobuf::Enum, rendezvous_proto::*, sleep, socket_client, tokio, ResultType, }; @@ -32,7 +32,7 @@ lazy_static::lazy_static! { #[inline] pub fn valid_for_numlock(evt: &KeyEvent) -> bool { - if let Some(key_event::Union::control_key(ck)) = evt.union { + if let Some(key_event::Union::ControlKey(ck)) = evt.union { let v = ck.value(); (v >= ControlKey::Numpad0.value() && v <= ControlKey::Numpad9.value()) || v == ControlKey::Decimal.value() @@ -284,7 +284,7 @@ async fn test_nat_type_() -> ResultType { socket.send(&msg_out).await?; if let Some(Ok(bytes)) = socket.next_timeout(RENDEZVOUS_TIMEOUT).await { if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(&bytes) { - if let Some(rendezvous_message::Union::test_nat_response(tnr)) = msg_in.union { + if let Some(rendezvous_message::Union::TestNatResponse(tnr)) = msg_in.union { if i == 0 { port1 = tnr.port; } else { @@ -412,7 +412,7 @@ pub const POSTFIX_SERVICE: &'static str = "_service"; #[inline] pub fn is_control_key(evt: &KeyEvent, key: &ControlKey) -> bool { - if let Some(key_event::Union::control_key(ck)) = evt.union { + if let Some(key_event::Union::ControlKey(ck)) = evt.union { ck.value() == key.value() } else { false @@ -421,7 +421,7 @@ pub fn is_control_key(evt: &KeyEvent, key: &ControlKey) -> bool { #[inline] pub fn is_modifier(evt: &KeyEvent) -> bool { - if let Some(key_event::Union::control_key(ck)) = evt.union { + if let Some(key_event::Union::ControlKey(ck)) = evt.union { let v = ck.value(); v == ControlKey::Alt.value() || v == ControlKey::Shift.value() @@ -457,7 +457,7 @@ async fn _check_software_update() -> hbb_common::ResultType<()> { use hbb_common::protobuf::Message; if let Some(Ok((bytes, _))) = socket.next_timeout(30_000).await { if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(&bytes) { - if let Some(rendezvous_message::Union::software_update(su)) = msg_in.union { + if let Some(rendezvous_message::Union::SoftwareUpdate(su)) = msg_in.union { let version = hbb_common::get_version_from_url(&su.url); if get_version_number(&version) > get_version_number(crate::VERSION) { *SOFTWARE_UPDATE_URL.lock().unwrap() = su.url; diff --git a/src/port_forward.rs b/src/port_forward.rs index afc65f236..36d011b37 100644 --- a/src/port_forward.rs +++ b/src/port_forward.rs @@ -120,21 +120,21 @@ async fn connect_and_login( Ok(Some(Ok(bytes))) => { let msg_in = Message::parse_from_bytes(&bytes)?; match msg_in.union { - Some(message::Union::hash(hash)) => { + Some(message::Union::Hash(hash)) => { interface.handle_hash(hash, &mut stream).await; } - Some(message::Union::login_response(lr)) => match lr.union { - Some(login_response::Union::error(err)) => { + Some(message::Union::LoginResponse(lr)) => match lr.union { + Some(login_response::Union::Error(err)) => { interface.handle_login_error(&err); return Ok(None); } - Some(login_response::Union::peer_info(pi)) => { + Some(login_response::Union::PeerInfo(pi)) => { interface.handle_peer_info(pi); break; } _ => {} } - Some(message::Union::test_delay(t)) => { + Some(message::Union::TestDelay(t)) => { interface.handle_test_delay(t, &mut stream).await; } _ => {} diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index a7f90b977..4381fbc74 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -157,7 +157,7 @@ impl RendezvousMediator { Some(Ok((bytes, _))) => { if let Ok(msg_in) = Message::parse_from_bytes(&bytes) { match msg_in.union { - Some(rendezvous_message::Union::register_peer_response(rpr)) => { + Some(rendezvous_message::Union::RegisterPeerResponse(rpr)) => { update_latency(); if rpr.request_pk { log::info!("request_pk received from {}", host); @@ -165,7 +165,7 @@ impl RendezvousMediator { continue; } } - Some(rendezvous_message::Union::register_pk_response(rpr)) => { + Some(rendezvous_message::Union::RegisterPkResponse(rpr)) => { update_latency(); match rpr.result.enum_value_or_default() { register_pk_response::Result::OK => { @@ -179,28 +179,28 @@ impl RendezvousMediator { _ => {} } } - Some(rendezvous_message::Union::punch_hole(ph)) => { + Some(rendezvous_message::Union::PunchHole(ph)) => { let rz = rz.clone(); let server = server.clone(); tokio::spawn(async move { allow_err!(rz.handle_punch_hole(ph, server).await); }); } - Some(rendezvous_message::Union::request_relay(rr)) => { + Some(rendezvous_message::Union::RequestRelay(rr)) => { let rz = rz.clone(); let server = server.clone(); tokio::spawn(async move { allow_err!(rz.handle_request_relay(rr, server).await); }); } - Some(rendezvous_message::Union::fetch_local_addr(fla)) => { + Some(rendezvous_message::Union::FetchLocalAddr(fla)) => { let rz = rz.clone(); let server = server.clone(); tokio::spawn(async move { allow_err!(rz.handle_intranet(fla, server).await); }); } - Some(rendezvous_message::Union::configure_update(cu)) => { + Some(rendezvous_message::Union::ConfigureUpdate(cu)) => { let v0 = Config::get_rendezvous_servers(); Config::set_option("rendezvous-servers".to_owned(), cu.rendezvous_servers.join(",")); Config::set_serial(cu.serial); @@ -367,7 +367,7 @@ impl RendezvousMediator { socket }; let mut msg_out = Message::new(); - use hbb_common::protobuf::ProtobufEnum; + use hbb_common::protobuf::Enum; let nat_type = NatType::from_i32(Config::get_nat_type()).unwrap_or(NatType::UNKNOWN_NAT); msg_out.set_punch_hole_sent(PunchHoleSent { socket_addr: ph.socket_addr, @@ -564,7 +564,7 @@ fn lan_discovery() -> ResultType<()> { if let Ok((len, addr)) = socket.recv_from(&mut buf) { if let Ok(msg_in) = Message::parse_from_bytes(&buf[0..len]) { match msg_in.union { - Some(rendezvous_message::Union::peer_discovery(p)) => { + Some(rendezvous_message::Union::PeerDiscovery(p)) => { if p.cmd == "ping" { let mut msg_out = Message::new(); let peer = PeerDiscovery { @@ -612,7 +612,7 @@ pub fn discover() -> ResultType<()> { if let Ok((len, _)) = socket.recv_from(&mut buf) { if let Ok(msg_in) = Message::parse_from_bytes(&buf[0..len]) { match msg_in.union { - Some(rendezvous_message::Union::peer_discovery(p)) => { + Some(rendezvous_message::Union::PeerDiscovery(p)) => { last_recv_time = Instant::now(); if p.cmd == "pong" { if p.mac != mac { diff --git a/src/server.rs b/src/server.rs index 3b68fc7dd..d437ce6d4 100644 --- a/src/server.rs +++ b/src/server.rs @@ -7,7 +7,7 @@ use hbb_common::{ config::{Config, Config2, CONNECT_TIMEOUT, RELAY_PORT}, log, message_proto::*, - protobuf::{Message as _, ProtobufEnum}, + protobuf::{Message as _, Enum}, rendezvous_proto::*, socket_client, sodiumoxide::crypto::{box_, secretbox, sign}, @@ -140,7 +140,7 @@ pub async fn create_tcp_connection( Some(res) => { let bytes = res?; if let Ok(msg_in) = Message::parse_from_bytes(&bytes) { - if let Some(message::Union::public_key(pk)) = msg_in.union { + if let Some(message::Union::PublicKey(pk)) = msg_in.union { if pk.asymmetric_value.len() == box_::PUBLICKEYBYTES { let nonce = box_::Nonce([0u8; box_::NONCEBYTES]); let mut pk_ = [0u8; box_::PUBLICKEYBYTES]; diff --git a/src/server/connection.rs b/src/server/connection.rs index 46c730092..e8344c4a7 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -91,7 +91,7 @@ impl Subscriber for ConnInner { #[inline] fn send(&mut self, msg: Arc) { match &msg.union { - Some(message::Union::video_frame(_)) => { + Some(message::Union::VideoFrame(_)) => { self.tx_video.as_mut().map(|tx| { allow_err!(tx.send((Instant::now(), msg))); }); @@ -354,7 +354,7 @@ impl Connection { if latency > 1000 { match &msg.union { - Some(message::Union::audio_frame(_)) => { + Some(message::Union::AudioFrame(_)) => { // log::info!("audio frame latency {}", instant.elapsed().as_secs_f32()); continue; } @@ -779,7 +779,7 @@ impl Connection { } async fn on_message(&mut self, msg: Message) -> bool { - if let Some(message::Union::login_request(lr)) = msg.union { + if let Some(message::Union::LoginRequest(lr)) = msg.union { if let Some(o) = lr.option.as_ref() { self.update_option(o).await; if let Some(q) = o.video_codec_state.clone().take() { @@ -804,7 +804,7 @@ impl Connection { return true; } match lr.union { - Some(login_request::Union::file_transfer(ft)) => { + Some(login_request::Union::FileTransfer(ft)) => { if !Config::get_option("enable-file-transfer").is_empty() { self.send_login_error("No permission of file transfer") .await; @@ -813,7 +813,7 @@ impl Connection { } self.file_transfer = Some((ft.dir, ft.show_hidden)); } - Some(login_request::Union::port_forward(mut pf)) => { + Some(login_request::Union::PortForward(mut pf)) => { if !Config::get_option("enable-tunnel").is_empty() { self.send_login_error("No permission of IP tunneling").await; sleep(1.).await; @@ -897,7 +897,7 @@ impl Connection { } } } - } else if let Some(message::Union::test_delay(t)) = msg.union { + } else if let Some(message::Union::TestDelay(t)) = msg.union { if t.from_client { let mut msg_out = Message::new(); msg_out.set_test_delay(t); @@ -912,7 +912,7 @@ impl Connection { } } else if self.authorized { match msg.union { - Some(message::Union::mouse_event(me)) => { + Some(message::Union::MouseEvent(me)) => { #[cfg(any(target_os = "android", target_os = "ios"))] if let Err(e) = call_main_service_mouse_input(me.mask, me.x, me.y) { log::debug!("call_main_service_mouse_input fail:{}", e); @@ -927,7 +927,7 @@ impl Connection { self.input_mouse(me, self.inner.id()); } } - Some(message::Union::key_event(me)) => { + Some(message::Union::KeyEvent(me)) => { #[cfg(not(any(target_os = "android", target_os = "ios")))] if self.keyboard { if is_enter(&me) { @@ -943,8 +943,8 @@ impl Connection { }; if is_press { match me.union { - Some(key_event::Union::unicode(_)) - | Some(key_event::Union::seq(_)) => { + Some(key_event::Union::Unicode(_)) + | Some(key_event::Union::Seq(_)) => { self.input_key(me, false); } _ => { @@ -956,14 +956,14 @@ impl Connection { } } } - Some(message::Union::clipboard(cb)) => + Some(message::Union::Clipboard(cb)) => { #[cfg(not(any(target_os = "android", target_os = "ios")))] if self.clipboard { update_clipboard(cb, None); } } - Some(message::Union::cliprdr(_clip)) => { + Some(message::Union::Cliprdr(_clip)) => { if self.file_transfer_enabled() { #[cfg(windows)] if let Some(clip) = msg_2_clip(_clip) { @@ -971,13 +971,13 @@ impl Connection { } } } - Some(message::Union::file_action(fa)) => { + Some(message::Union::FileAction(fa)) => { if self.file_transfer.is_some() { match fa.union { - Some(file_action::Union::read_dir(rd)) => { + Some(file_action::Union::ReadDir(rd)) => { self.read_dir(&rd.path, rd.include_hidden); } - Some(file_action::Union::all_files(f)) => { + Some(file_action::Union::AllFiles(f)) => { match fs::get_recursive_files(&f.path, f.include_hidden) { Err(err) => { self.send(fs::new_error(f.id, err, -1)).await; @@ -987,7 +987,7 @@ impl Connection { } } } - Some(file_action::Union::send(s)) => { + Some(file_action::Union::Send(s)) => { let id = s.id; let od = can_enable_overwrite_detection(get_version_number(VERSION)); @@ -1012,7 +1012,7 @@ impl Connection { } } } - Some(file_action::Union::receive(r)) => { + Some(file_action::Union::Receive(r)) => { self.send_fs(ipc::FS::NewWrite { path: r.path, id: r.id, @@ -1025,31 +1025,31 @@ impl Connection { .collect(), }); } - Some(file_action::Union::remove_dir(d)) => { + Some(file_action::Union::RemoveDir(d)) => { self.send_fs(ipc::FS::RemoveDir { path: d.path, id: d.id, recursive: d.recursive, }); } - Some(file_action::Union::remove_file(f)) => { + Some(file_action::Union::RemoveFile(f)) => { self.send_fs(ipc::FS::RemoveFile { path: f.path, id: f.id, file_num: f.file_num, }); } - Some(file_action::Union::create(c)) => { + Some(file_action::Union::Create(c)) => { self.send_fs(ipc::FS::CreateDir { path: c.path, id: c.id, }); } - Some(file_action::Union::cancel(c)) => { + Some(file_action::Union::Cancel(c)) => { self.send_fs(ipc::FS::CancelWrite { id: c.id }); fs::remove_job(c.id, &mut self.read_jobs); } - Some(file_action::Union::send_confirm(r)) => { + Some(file_action::Union::SendConfirm(r)) => { if let Some(job) = fs::get_job(r.id, &mut self.read_jobs) { job.confirm(&r); } @@ -1058,8 +1058,8 @@ impl Connection { } } } - Some(message::Union::file_response(fr)) => match fr.union { - Some(file_response::Union::block(block)) => { + Some(message::Union::FileResponse(fr)) => match fr.union { + Some(file_response::Union::Block(block)) => { self.send_fs(ipc::FS::WriteBlock { id: block.id, file_num: block.file_num, @@ -1067,13 +1067,13 @@ impl Connection { compressed: block.compressed, }); } - Some(file_response::Union::done(d)) => { + Some(file_response::Union::Done(d)) => { self.send_fs(ipc::FS::WriteDone { id: d.id, file_num: d.file_num, }); } - Some(file_response::Union::digest(d)) => self.send_fs(ipc::FS::CheckDigest { + Some(file_response::Union::Digest(d)) => self.send_fs(ipc::FS::CheckDigest { id: d.id, file_num: d.file_num, file_size: d.file_size, @@ -1082,22 +1082,22 @@ impl Connection { }), _ => {} }, - Some(message::Union::misc(misc)) => match misc.union { - Some(misc::Union::switch_display(s)) => { + Some(message::Union::Misc(misc)) => match misc.union { + Some(misc::Union::SwitchDisplay(s)) => { video_service::switch_display(s.display); } - Some(misc::Union::chat_message(c)) => { + Some(misc::Union::ChatMessage(c)) => { self.send_to_cm(ipc::Data::ChatMessage { text: c.text }); } - Some(misc::Union::option(o)) => { + Some(misc::Union::Option(o)) => { self.update_option(&o).await; } - Some(misc::Union::refresh_video(r)) => { + Some(misc::Union::RefreshVideo(r)) => { if r { video_service::refresh(); } } - Some(misc::Union::video_received(_)) => { + Some(misc::Union::VideoReceived(_)) => { video_service::notify_video_frame_feched( self.inner.id, Some(Instant::now().into()), diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 8c5f3060b..3ec9c0f70 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -2,7 +2,7 @@ use super::*; #[cfg(target_os = "macos")] use dispatch::Queue; use enigo::{Enigo, Key, KeyboardControllable, MouseButton, MouseControllable}; -use hbb_common::{config::COMPRESS_LEVEL, protobuf::ProtobufEnumOrUnknown}; +use hbb_common::{config::COMPRESS_LEVEL, protobuf::EnumOrUnknown}; use std::{ convert::TryFrom, sync::atomic::{AtomicBool, Ordering}, @@ -68,7 +68,7 @@ impl Subscriber for MouseCursorSub { #[inline] fn send(&mut self, msg: Arc) { - if let Some(message::Union::cursor_data(cd)) = &msg.union { + if let Some(message::Union::CursorData(cd)) = &msg.union { if let Some(msg) = self.cached.get(&cd.id) { self.inner.send(msg.clone()); } else { @@ -299,12 +299,12 @@ fn fix_key_down_timeout(force: bool) { // e.g. current state of ctrl is down, but ctrl not in modifier, we should change ctrl to up, to make modifier state sync between remote and local #[inline] fn fix_modifier( - modifiers: &[ProtobufEnumOrUnknown], + modifiers: &[EnumOrUnknown], key0: ControlKey, key1: Key, en: &mut Enigo, ) { - if get_modifier_state(key1, en) && !modifiers.contains(&ProtobufEnumOrUnknown::new(key0)) { + if get_modifier_state(key1, en) && !modifiers.contains(&EnumOrUnknown::new(key0)) { #[cfg(windows)] if key0 == ControlKey::Control && get_modifier_state(Key::Alt, en) { // AltGr case @@ -315,7 +315,7 @@ fn fix_modifier( } } -fn fix_modifiers(modifiers: &[ProtobufEnumOrUnknown], en: &mut Enigo, ck: i32) { +fn fix_modifiers(modifiers: &[EnumOrUnknown], en: &mut Enigo, ck: i32) { if ck != ControlKey::Shift.value() { fix_modifier(modifiers, ControlKey::Shift, Key::Shift, en); } @@ -430,7 +430,7 @@ fn handle_mouse_(evt: &MouseEvent, conn: i32) { } pub fn is_enter(evt: &KeyEvent) -> bool { - if let Some(key_event::Union::control_key(ck)) = evt.union { + if let Some(key_event::Union::ControlKey(ck)) = evt.union { if ck.value() == ControlKey::Return.value() || ck.value() == ControlKey::NumpadEnter.value() { return true; @@ -598,7 +598,7 @@ fn handle_key_(evt: &KeyEvent) { #[cfg(windows)] let mut has_numlock = false; if evt.down { - let ck = if let Some(key_event::Union::control_key(ck)) = evt.union { + let ck = if let Some(key_event::Union::ControlKey(ck)) = evt.union { ck.value() } else { -1 @@ -653,7 +653,7 @@ fn handle_key_(evt: &KeyEvent) { } } match evt.union { - Some(key_event::Union::control_key(ck)) => { + Some(key_event::Union::ControlKey(ck)) => { if let Some(key) = KEY_MAP.get(&ck.value()) { #[cfg(windows)] if let Some(_) = NUMPAD_KEY_MAP.get(&ck.value()) { @@ -682,7 +682,7 @@ fn handle_key_(evt: &KeyEvent) { lock_screen(); } } - Some(key_event::Union::chr(chr)) => { + Some(key_event::Union::Chr(chr)) => { if evt.down { if en.key_down(get_layout(chr)).is_ok() { KEYS_DOWN @@ -708,12 +708,12 @@ fn handle_key_(evt: &KeyEvent) { .remove(&(chr as u64 + KEY_CHAR_START)); } } - Some(key_event::Union::unicode(chr)) => { + Some(key_event::Union::Unicode(chr)) => { if let Ok(chr) = char::try_from(chr) { en.key_sequence(&chr.to_string()); } } - Some(key_event::Union::seq(ref seq)) => { + Some(key_event::Union::Seq(ref seq)) => { en.key_sequence(&seq); } _ => {} diff --git a/src/ui.rs b/src/ui.rs index f0134aceb..a1f9093d0 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1045,7 +1045,7 @@ async fn check_id( if let Some(Ok(bytes)) = socket.next_timeout(3_000).await { if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(&bytes) { match msg_in.union { - Some(rendezvous_message::Union::register_pk_response(rpr)) => { + Some(rendezvous_message::Union::RegisterPkResponse(rpr)) => { match rpr.result.enum_value_or_default() { register_pk_response::Result::OK => { ok = true; diff --git a/src/ui/cm.rs b/src/ui/cm.rs index 90d066d62..c1acb6926 100644 --- a/src/ui/cm.rs +++ b/src/ui/cm.rs @@ -204,7 +204,7 @@ impl ConnectionManager { let mut req = FileTransferSendConfirmRequest { id, file_num, - union: Some(file_transfer_send_confirm_request::Union::offset_blk(0)), + union: Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)), ..Default::default() }; let digest = FileTransferDigest { diff --git a/src/ui/remote.rs b/src/ui/remote.rs index f85e37d04..fc83a09a5 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -1067,7 +1067,7 @@ impl Handler { fn get_char(&mut self, name: String, code: i32) -> String { if let Some(key_event) = self.get_key_event(1, &name, code) { match key_event.union { - Some(key_event::Union::chr(chr)) => { + Some(key_event::Union::Chr(chr)) => { if let Some(chr) = std::char::from_u32(chr as _) { return chr.to_string(); } @@ -1810,9 +1810,9 @@ impl Remote { id, file_num, union: if need_override { - Some(file_transfer_send_confirm_request::Union::offset_blk(0)) + Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) } else { - Some(file_transfer_send_confirm_request::Union::skip(true)) + Some(file_transfer_send_confirm_request::Union::Skip(true)) }, ..Default::default() }); @@ -1828,9 +1828,9 @@ impl Remote { id, file_num, union: if need_override { - Some(file_transfer_send_confirm_request::Union::offset_blk(0)) + Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) } else { - Some(file_transfer_send_confirm_request::Union::skip(true)) + Some(file_transfer_send_confirm_request::Union::Skip(true)) }, ..Default::default() }); @@ -2017,7 +2017,7 @@ impl Remote { async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool { if let Ok(msg_in) = Message::parse_from_bytes(&data) { match msg_in.union { - Some(message::Union::video_frame(vf)) => { + Some(message::Union::VideoFrame(vf)) => { if !self.first_frame { self.first_frame = true; self.handler.call2("closeSuccess", &make_args!()); @@ -2033,16 +2033,16 @@ impl Remote { }; self.video_sender.send(MediaData::VideoFrame(vf)).ok(); } - Some(message::Union::hash(hash)) => { + Some(message::Union::Hash(hash)) => { self.handler.handle_hash(hash, peer).await; } - Some(message::Union::login_response(lr)) => match lr.union { - Some(login_response::Union::error(err)) => { + Some(message::Union::LoginResponse(lr)) => match lr.union { + Some(login_response::Union::Error(err)) => { if !self.handler.handle_login_error(&err) { return false; } } - Some(login_response::Union::peer_info(pi)) => { + Some(login_response::Union::PeerInfo(pi)) => { self.handler.handle_peer_info(pi); self.check_clipboard_file_context(); if !(self.handler.is_file_transfer() @@ -2069,22 +2069,22 @@ impl Remote { } _ => {} }, - Some(message::Union::cursor_data(cd)) => { + Some(message::Union::CursorData(cd)) => { self.handler.set_cursor_data(cd); } - Some(message::Union::cursor_id(id)) => { + Some(message::Union::CursorId(id)) => { self.handler.set_cursor_id(id.to_string()); } - Some(message::Union::cursor_position(cp)) => { + Some(message::Union::CursorPosition(cp)) => { self.handler.set_cursor_position(cp); } - Some(message::Union::clipboard(cb)) => { + Some(message::Union::Clipboard(cb)) => { if !self.handler.lc.read().unwrap().disable_clipboard { update_clipboard(cb, Some(&self.old_clipboard)); } } #[cfg(windows)] - Some(message::Union::cliprdr(clip)) => { + Some(message::Union::Cliprdr(clip)) => { if !self.handler.lc.read().unwrap().disable_clipboard { if let Some(context) = &mut self.clipboard_file_context { if let Some(clip) = msg_2_clip(clip) { @@ -2093,9 +2093,9 @@ impl Remote { } } } - Some(message::Union::file_response(fr)) => { + Some(message::Union::FileResponse(fr)) => { match fr.union { - Some(file_response::Union::dir(fd)) => { + Some(file_response::Union::Dir(fd)) => { #[cfg(windows)] let entries = fd.entries.to_vec(); #[cfg(not(windows))] @@ -2118,7 +2118,7 @@ impl Remote { job.files = entries; } } - Some(file_response::Union::digest(digest)) => { + Some(file_response::Union::Digest(digest)) => { if digest.is_upload { if let Some(job) = fs::get_job(digest.id, &mut self.read_jobs) { if let Some(file) = job.files().get(digest.file_num as usize) { @@ -2129,9 +2129,9 @@ impl Remote { id: digest.id, file_num: digest.file_num, union: Some(if overwrite { - file_transfer_send_confirm_request::Union::offset_blk(0) + file_transfer_send_confirm_request::Union::OffsetBlk(0) } else { - file_transfer_send_confirm_request::Union::skip( + file_transfer_send_confirm_request::Union::Skip( true, ) }), @@ -2164,7 +2164,7 @@ impl Remote { let msg= new_send_confirm(FileTransferSendConfirmRequest { id: digest.id, file_num: digest.file_num, - union: Some(file_transfer_send_confirm_request::Union::skip(true)), + union: Some(file_transfer_send_confirm_request::Union::Skip(true)), ..Default::default() }); allow_err!(peer.send(&msg).await); @@ -2176,9 +2176,9 @@ impl Remote { id: digest.id, file_num: digest.file_num, union: Some(if overwrite { - file_transfer_send_confirm_request::Union::offset_blk(0) + file_transfer_send_confirm_request::Union::OffsetBlk(0) } else { - file_transfer_send_confirm_request::Union::skip(true) + file_transfer_send_confirm_request::Union::Skip(true) }), ..Default::default() }, @@ -2201,7 +2201,7 @@ impl Remote { FileTransferSendConfirmRequest { id: digest.id, file_num: digest.file_num, - union: Some(file_transfer_send_confirm_request::Union::offset_blk(0)), + union: Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)), ..Default::default() }, ); @@ -2216,7 +2216,7 @@ impl Remote { } } } - Some(file_response::Union::block(block)) => { + Some(file_response::Union::Block(block)) => { log::info!( "file response block, file id:{}, file num: {}", block.id, @@ -2229,27 +2229,27 @@ impl Remote { self.update_jobs_status(); } } - Some(file_response::Union::done(d)) => { + Some(file_response::Union::Done(d)) => { if let Some(job) = fs::get_job(d.id, &mut self.write_jobs) { job.modify_time(); fs::remove_job(d.id, &mut self.write_jobs); } self.handle_job_status(d.id, d.file_num, None); } - Some(file_response::Union::error(e)) => { + Some(file_response::Union::Error(e)) => { self.handle_job_status(e.id, e.file_num, Some(e.error)); } _ => {} } } - Some(message::Union::misc(misc)) => match misc.union { - Some(misc::Union::audio_format(f)) => { + Some(message::Union::Misc(misc)) => match misc.union { + Some(misc::Union::AudioFormat(f)) => { self.audio_sender.send(MediaData::AudioFormat(f)).ok(); } - Some(misc::Union::chat_message(c)) => { + Some(misc::Union::ChatMessage(c)) => { self.handler.call("newMessage", &make_args!(c.text)); } - Some(misc::Union::permission_info(p)) => { + Some(misc::Union::PermissionInfo(p)) => { log::info!("Change permission {:?} -> {}", p.permission, p.enabled); match p.permission.enum_value_or_default() { Permission::Keyboard => { @@ -2277,7 +2277,7 @@ impl Remote { } } } - Some(misc::Union::switch_display(s)) => { + Some(misc::Union::SwitchDisplay(s)) => { self.handler.call("switchDisplay", &make_args!(s.display)); self.video_sender.send(MediaData::Reset).ok(); if s.width > 0 && s.height > 0 { @@ -2293,27 +2293,27 @@ impl Remote { self.handler.set_display(s.x, s.y, s.width, s.height); } } - Some(misc::Union::close_reason(c)) => { + Some(misc::Union::CloseReason(c)) => { self.handler.msgbox("error", "Connection Error", &c); return false; } - Some(misc::Union::back_notification(notification)) => { + Some(misc::Union::BackNotification(notification)) => { if !self.handle_back_notification(notification).await { return false; } } _ => {} }, - Some(message::Union::test_delay(t)) => { + Some(message::Union::TestDelay(t)) => { self.handler.handle_test_delay(t, peer).await; } - Some(message::Union::audio_frame(frame)) => { + Some(message::Union::AudioFrame(frame)) => { if !self.handler.lc.read().unwrap().disable_audio { self.audio_sender.send(MediaData::AudioFrame(frame)).ok(); } } - Some(message::Union::file_action(action)) => match action.union { - Some(file_action::Union::send_confirm(c)) => { + Some(message::Union::FileAction(action)) => match action.union { + Some(file_action::Union::SendConfirm(c)) => { if let Some(job) = fs::get_job(c.id, &mut self.read_jobs) { job.confirm(&c); } @@ -2328,13 +2328,13 @@ impl Remote { async fn handle_back_notification(&mut self, notification: BackNotification) -> bool { match notification.union { - Some(back_notification::Union::block_input_state(state)) => { + Some(back_notification::Union::BlockInputState(state)) => { self.handle_back_msg_block_input( state.enum_value_or(back_notification::BlockInputState::StateUnknown), ) .await; } - Some(back_notification::Union::privacy_mode_state(state)) => { + Some(back_notification::Union::PrivacyModeState(state)) => { if !self .handle_back_msg_privacy_mode( state.enum_value_or(back_notification::PrivacyModeState::StateUnknown), From fb02fc11970bf9756a6e9180d1ba2d84b783c633 Mon Sep 17 00:00:00 2001 From: csf Date: Thu, 14 Jul 2022 17:44:37 +0800 Subject: [PATCH 14/50] [android] fix build; ignore battery optimizations --- .../android/app/src/main/AndroidManifest.xml | 1 + .../kotlin/com/carriez/flutter_hbb/common.kt | 26 ++++++- flutter/lib/common.dart | 7 +- flutter/lib/pages/settings_page.dart | 70 ++++++++++++++++--- src/common.rs | 3 + src/lang/cn.rs | 2 + src/lang/cs.rs | 2 + src/lang/da.rs | 2 + src/lang/de.rs | 2 + src/lang/eo.rs | 2 + src/lang/es.rs | 2 + src/lang/fr.rs | 2 + src/lang/hu.rs | 4 +- src/lang/id.rs | 2 + src/lang/it.rs | 2 + src/lang/ptbr.rs | 2 + src/lang/ru.rs | 2 + src/lang/sk.rs | 2 + src/lang/template.rs | 2 + src/lang/tr.rs | 2 + src/lang/tw.rs | 2 + src/server/video_qos.rs | 2 +- 22 files changed, 128 insertions(+), 15 deletions(-) diff --git a/flutter/android/app/src/main/AndroidManifest.xml b/flutter/android/app/src/main/AndroidManifest.xml index 1759a1ac0..04b2ccc9a 100644 --- a/flutter/android/app/src/main/AndroidManifest.xml +++ b/flutter/android/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ package="com.carriez.flutter_hbb"> + diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt index 7ce7d3ecc..3cb3ae581 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt @@ -2,20 +2,26 @@ package com.carriez.flutter_hbb import android.annotation.SuppressLint import android.content.Context +import android.content.Intent import android.media.AudioRecord import android.media.AudioRecord.READ_BLOCKING import android.media.MediaCodecList import android.media.MediaFormat +import android.net.Uri import android.os.Build import android.os.Handler import android.os.Looper -import android.util.Log +import android.os.PowerManager +import android.provider.Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS +import android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS import androidx.annotation.RequiresApi +import androidx.core.content.ContextCompat.getSystemService import com.hjq.permissions.Permission import com.hjq.permissions.XXPermissions import java.nio.ByteBuffer import java.util.* + @SuppressLint("ConstantLocale") val LOCAL_NAME = Locale.getDefault().toString() val SCREEN_INFO = Info(0, 0, 1, 200) @@ -38,8 +44,19 @@ fun testVP9Support(): Boolean { return res != null } +@RequiresApi(Build.VERSION_CODES.M) fun requestPermission(context: Context, type: String) { val permission = when (type) { + "ignore_battery_optimizations" -> { + try { + context.startActivity(Intent(ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply { + data = Uri.parse("package:" + context.packageName) + }) + } catch (e:Exception) { + e.printStackTrace() + } + return + } "audio" -> { Permission.RECORD_AUDIO } @@ -52,7 +69,7 @@ fun requestPermission(context: Context, type: String) { } XXPermissions.with(context) .permission(permission) - .request { permissions, all -> + .request { _, all -> if (all) { Handler(Looper.getMainLooper()).post { MainActivity.flutterMethodChannel.invokeMethod( @@ -64,8 +81,13 @@ fun requestPermission(context: Context, type: String) { } } +@RequiresApi(Build.VERSION_CODES.M) fun checkPermission(context: Context, type: String): Boolean { val permission = when (type) { + "ignore_battery_optimizations" -> { + val pw = context.getSystemService(Context.POWER_SERVICE) as PowerManager + return pw.isIgnoringBatteryOptimizations(context.packageName) + } "audio" -> { Permission.RECORD_AUDIO } diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index d6be51986..1b36c548b 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -260,7 +260,7 @@ class PermissionManager { static Timer? _timer; static var _current = ""; - static final permissions = ["audio", "file"]; + static final permissions = ["audio", "file", "ignore_battery_optimizations"]; static bool isWaitingFile() { if (_completer != null) { @@ -279,9 +279,12 @@ class PermissionManager { if (!permissions.contains(type)) return Future.error("Wrong permission!$type"); + FFI.invokeMethod("request_permission", type); + if (type == "ignore_battery_optimizations") { + return Future.value(false); + } _current = type; _completer = Completer(); - FFI.invokeMethod("request_permission", type); // timeout _timer?.cancel(); diff --git a/flutter/lib/pages/settings_page.dart b/flutter/lib/pages/settings_page.dart index 2c8b7fe9a..5bbc8dc96 100644 --- a/flutter/lib/pages/settings_page.dart +++ b/flutter/lib/pages/settings_page.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:settings_ui/settings_ui.dart'; import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -26,11 +28,69 @@ class SettingsPage extends StatefulWidget implements PageShape { class _SettingsState extends State { static const url = 'https://rustdesk.com/'; + var _showIgnoreBattery = false; + + @override + void initState() { + super.initState(); + if (androidVersion >= 26) { + () async { + final res = + await PermissionManager.check("ignore_battery_optimizations"); + if (_showIgnoreBattery != !res) { + setState(() { + _showIgnoreBattery = !res; + }); + } + }(); + } + } @override Widget build(BuildContext context) { Provider.of(context); final username = getUsername(); + final settingsTiles = [ + SettingsTile.navigation( + title: Text(translate('ID/Relay Server')), + leading: Icon(Icons.cloud), + onPressed: (context) { + showServerSettings(); + }, + ), + SettingsTile.navigation( + title: Text(translate('Enhancements')), + leading: Icon(Icons.tune), + onPressed: (context) {}, + ), + ]; + if (_showIgnoreBattery) { + settingsTiles.add(SettingsTile.navigation( + title: Text(translate('Keep RustDesk background service')), + description: Text('* ${translate('Ignore Battery Optimizations')}'), + leading: Icon(Icons.settings_backup_restore), + onPressed: (context) { + PermissionManager.request("ignore_battery_optimizations"); + var count = 0; + Timer.periodic(Duration(seconds: 1), (timer) async { + debugPrint("BatteryOpt Timer, count:$count"); + if (count > 5) { + count = 0; + timer.cancel(); + } + if (await PermissionManager.check( + "ignore_battery_optimizations")) { + count = 0; + timer.cancel(); + setState(() { + _showIgnoreBattery = false; + }); + } + count++; + }); + })); + } + return SettingsList( sections: [ SettingsSection( @@ -53,15 +113,7 @@ class _SettingsState extends State { ), SettingsSection( title: Text(translate("Settings")), - tiles: [ - SettingsTile.navigation( - title: Text(translate('ID/Relay Server')), - leading: Icon(Icons.cloud), - onPressed: (context) { - showServerSettings(); - }, - ), - ], + tiles: settingsTiles, ), SettingsSection( title: Text(translate("About")), diff --git a/src/common.rs b/src/common.rs index 5ef127f46..b40c1a686 100644 --- a/src/common.rs +++ b/src/common.rs @@ -316,6 +316,9 @@ async fn test_nat_type_() -> ResultType { } pub async fn get_rendezvous_server(ms_timeout: u64) -> (String, Vec, bool) { + #[cfg(any(target_os = "android", target_os = "ios"))] + let (mut a, mut b) = get_rendezvous_server_(ms_timeout); + #[cfg(not(any(target_os = "android", target_os = "ios")))] let (mut a, mut b) = get_rendezvous_server_(ms_timeout).await; let mut b: Vec = b .drain(..) diff --git a/src/lang/cn.rs b/src/lang/cn.rs index ba8e6d178..ba85d7f39 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "进入隐私模式"), ("Out privacy mode", "退出隐私模式"), ("Language", "语言"), + ("Keep RustDesk background service", "保持RustDesk后台服务"), + ("Ignore Battery Optimizations", "忽略电池优化"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 3c92ee71b..e12bc6075 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "v režimu soukromí"), ("Out privacy mode", "mimo režim soukromí"), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 16b98359b..f8c676376 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "I databeskyttelsestilstand"), ("Out privacy mode", "Databeskyttelsestilstand fra"), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index c2fd56002..8685e764d 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "im Datenschutzmodus"), ("Out privacy mode", "Datenschutzmodus aus"), ("Language", "Sprache"), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index c9701d63a..5952f51a1 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", ""), ("Out privacy mode", ""), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 60cc05341..9e12ed430 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "En modo de privacidad"), ("Out privacy mode", "Fuera del modo de privacidad"), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index f447c5acd..5e223b262 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "en mode privé"), ("Out privacy mode", "hors mode de confidentialité"), ("Language", "Langue"), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index e7a95870e..ab890766e 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -270,7 +270,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Overwrite", "Felülírás"), ("This file exists, skip or overwrite this file?", "Ez a fájl már létezik, skippeljünk, vagy felülírjuk ezt a fájlt?"), ("Quit", "Kilépés"), - ("doc_mac_permission", "https://rustdesk.com/docs/hu/manual/mac/#enable-permissions"), + ("doc_mac_permission", "https://rustdesk.com/docs/hu/manual/mac/#enable-permissions"), ("Help", "Segítség"), ("Failed", "Sikertelen"), ("Succeeded", "Sikeres"), @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "Belépés a privát módba"), ("Out privacy mode", "Kilépés a privát módból"), ("Language", "Nyelv"), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index b1c6aaa1e..e926ee4cf 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "Dalam mode privasi"), ("Out privacy mode", "Keluar dari mode privasi"), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index c756d2218..016363d4e 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "In modalità privacy"), ("Out privacy mode", "Fuori modalità privacy"), ("Language", "Linguaggio"), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 946e47d4c..e36672e5c 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "No modo de privacidade"), ("Out privacy mode", "Fora do modo de privacidade"), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index d421bd85e..d8f138223 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "В режиме конфиденциальности"), ("Out privacy mode", "Выход из режима конфиденциальности"), ("Language", "Язык"), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 8c60abaa9..4f2a4a29f 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "V režime súkromia"), ("Out privacy mode", "Mimo režimu súkromia"), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index ad963b323..8feac4d3b 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", ""), ("Out privacy mode", ""), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index d12b2881e..5bab8d8c9 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "Gizlilik modunda"), ("Out privacy mode", "Gizlilik modu dışında"), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 4f9813943..8bc1392bb 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -284,5 +284,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "開啟隱私模式"), ("Out privacy mode", "退出隱私模式"), ("Language", "語言"), + ("Keep RustDesk background service", "保持RustDesk後台服務"), + ("Ignore Battery Optimizations", "忽略電池優化"), ].iter().cloned().collect(); } diff --git a/src/server/video_qos.rs b/src/server/video_qos.rs index efad1f9d9..87ae1cf33 100644 --- a/src/server/video_qos.rs +++ b/src/server/video_qos.rs @@ -171,7 +171,7 @@ impl VideoQoS { #[cfg(target_os = "android")] { // fix when andorid screen shrinks - let fix = Display::fix_quality() as u32; + let fix = scrap::Display::fix_quality() as u32; log::debug!("Android screen, fix quality:{}", fix); let base_bitrate = base_bitrate * fix; self.target_bitrate = base_bitrate * self.current_image_quality / 100; From 55427bad2fecdcbb8ee1910ce98e7c1bed909bb2 Mon Sep 17 00:00:00 2001 From: csf Date: Thu, 14 Jul 2022 18:33:41 +0800 Subject: [PATCH 15/50] [android] add Enhancements settings --- flutter/lib/pages/settings_page.dart | 88 ++++++++++++++++------------ 1 file changed, 51 insertions(+), 37 deletions(-) diff --git a/flutter/lib/pages/settings_page.dart b/flutter/lib/pages/settings_page.dart index 5bbc8dc96..7506d849f 100644 --- a/flutter/lib/pages/settings_page.dart +++ b/flutter/lib/pages/settings_page.dart @@ -50,45 +50,51 @@ class _SettingsState extends State { Widget build(BuildContext context) { Provider.of(context); final username = getUsername(); - final settingsTiles = [ - SettingsTile.navigation( - title: Text(translate('ID/Relay Server')), - leading: Icon(Icons.cloud), - onPressed: (context) { - showServerSettings(); + final enableAbr = FFI.getByName("option", "enable-abr") != 'N'; + final enhancementsTiles = [ + SettingsTile.switchTile( + leading: Icon(Icons.more_horiz), + title: Text(translate('Adaptive Bitrate') + '(beta)'), + initialValue: enableAbr, + onToggle: (v) { + final msg = Map() + ..["name"] = "enable-abr" + ..["value"] = ""; + if (!v) { + msg["value"] = "N"; + } + FFI.setByName("option", json.encode(msg)); + setState(() {}); }, - ), - SettingsTile.navigation( - title: Text(translate('Enhancements')), - leading: Icon(Icons.tune), - onPressed: (context) {}, - ), + ) ]; if (_showIgnoreBattery) { - settingsTiles.add(SettingsTile.navigation( - title: Text(translate('Keep RustDesk background service')), - description: Text('* ${translate('Ignore Battery Optimizations')}'), - leading: Icon(Icons.settings_backup_restore), - onPressed: (context) { - PermissionManager.request("ignore_battery_optimizations"); - var count = 0; - Timer.periodic(Duration(seconds: 1), (timer) async { - debugPrint("BatteryOpt Timer, count:$count"); - if (count > 5) { - count = 0; - timer.cancel(); - } - if (await PermissionManager.check( - "ignore_battery_optimizations")) { - count = 0; - timer.cancel(); - setState(() { - _showIgnoreBattery = false; + enhancementsTiles.insert( + 0, + SettingsTile.navigation( + title: Text(translate('Keep RustDesk background service')), + description: + Text('* ${translate('Ignore Battery Optimizations')}'), + leading: Icon(Icons.battery_saver), + onPressed: (context) { + PermissionManager.request("ignore_battery_optimizations"); + var count = 0; + Timer.periodic(Duration(seconds: 1), (timer) async { + if (count > 5) { + count = 0; + timer.cancel(); + } + if (await PermissionManager.check( + "ignore_battery_optimizations")) { + count = 0; + timer.cancel(); + setState(() { + _showIgnoreBattery = false; + }); + } + count++; }); - } - count++; - }); - })); + })); } return SettingsList( @@ -111,9 +117,17 @@ class _SettingsState extends State { ), ], ), + SettingsSection(title: Text(translate("Settings")), tiles: [ + SettingsTile.navigation( + title: Text(translate('ID/Relay Server')), + leading: Icon(Icons.cloud), + onPressed: (context) { + showServerSettings(); + }) + ]), SettingsSection( - title: Text(translate("Settings")), - tiles: settingsTiles, + title: Text(translate("Enhancements")), + tiles: enhancementsTiles, ), SettingsSection( title: Text(translate("About")), From fdca9acbb78a031a071eca380057138af629db25 Mon Sep 17 00:00:00 2001 From: csf Date: Thu, 14 Jul 2022 18:36:54 +0800 Subject: [PATCH 16/50] [android] InputService mouse long press mode --- .../com/carriez/flutter_hbb/InputService.kt | 47 ++++++++++++------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt index 76068eee5..905a2734d 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt @@ -8,14 +8,14 @@ package com.carriez.flutter_hbb import android.accessibilityservice.AccessibilityService import android.accessibilityservice.GestureDescription -import android.content.Context import android.graphics.Path import android.os.Build import android.util.Log import android.view.accessibility.AccessibilityEvent -import androidx.annotation.Keep import androidx.annotation.RequiresApi import java.util.* +import kotlin.math.abs +import kotlin.math.max const val LIFT_DOWN = 9 const val LIFT_MOVE = 8 @@ -49,28 +49,40 @@ class InputService : AccessibilityService() { private val wheelActionsQueue = LinkedList() private var isWheelActionsPolling = false + private var isWaitingLongPress = false @RequiresApi(Build.VERSION_CODES.N) fun onMouseInput(mask: Int, _x: Int, _y: Int) { - val x = if (_x < 0) { - 0 - } else { - _x - } - - val y = if (_y < 0) { - 0 - } else { - _y - } + val x = max(0, _x) + val y = max(0, _y) if (mask == 0 || mask == LIFT_MOVE) { + val oldX = mouseX + val oldY = mouseY mouseX = x * SCREEN_INFO.scale mouseY = y * SCREEN_INFO.scale + if (isWaitingLongPress) { + val delta = abs(oldX - mouseX) + abs(oldY - mouseY) + Log.d(logTag,"delta:$delta") + if (delta > 8) { + isWaitingLongPress = false + } + } } // left button down ,was up if (mask == LIFT_DOWN) { + isWaitingLongPress = true + timer.schedule(object : TimerTask() { + override fun run() { + if (isWaitingLongPress) { + isWaitingLongPress = false + leftIsDown = false + endGesture(mouseX, mouseY) + } + } + }, LONG_TAP_DELAY * 4) + leftIsDown = true startGesture(mouseX, mouseY) return @@ -83,9 +95,12 @@ class InputService : AccessibilityService() { // left up ,was down if (mask == LIFT_UP) { - leftIsDown = false - endGesture(mouseX, mouseY) - return + if (leftIsDown) { + leftIsDown = false + isWaitingLongPress = false + endGesture(mouseX, mouseY) + return + } } if (mask == RIGHT_UP) { From 175ab0b58c26db24e3e809547ae250e73c96704f Mon Sep 17 00:00:00 2001 From: csf Date: Thu, 14 Jul 2022 21:46:40 +0800 Subject: [PATCH 17/50] remove log --- flutter/lib/pages/remote_page.dart | 3 ++- src/server/video_qos.rs | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/flutter/lib/pages/remote_page.dart b/flutter/lib/pages/remote_page.dart index c383bc361..7a3e489b0 100644 --- a/flutter/lib/pages/remote_page.dart +++ b/flutter/lib/pages/remote_page.dart @@ -262,7 +262,6 @@ class _RemotePageState extends State { : SafeArea(child: OrientationBuilder(builder: (ctx, orientation) { if (_currentOrientation != orientation) { - debugPrint("on orientation changed"); Timer(Duration(milliseconds: 200), () { resetMobileActionsOverlay(); _currentOrientation = orientation; @@ -1061,6 +1060,8 @@ void showOptions() { getRadio('Optimize reaction time', 'low', quality, setQuality), Divider(color: MyTheme.border), getToggle(setState, 'show-remote-cursor', 'Show remote cursor'), + getToggle( + setState, 'show-quality-monitor', 'Show quality monitor'), ] + more), actions: [], diff --git a/src/server/video_qos.rs b/src/server/video_qos.rs index 87ae1cf33..b0e06bc03 100644 --- a/src/server/video_qos.rs +++ b/src/server/video_qos.rs @@ -147,7 +147,6 @@ impl VideoQoS { // handle image_quality change from peer pub fn update_image_quality(&mut self, image_quality: i32) { let image_quality = Self::convert_quality(image_quality) as _; - log::debug!("VideoQoS update_image_quality: {}", image_quality); if self.current_image_quality != image_quality { self.current_image_quality = image_quality; let _ = self.generate_bitrate().ok(); From 9498b77c0a6649670519f46b6f266d1ba1d0380a Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 14 Jul 2022 23:26:03 +0800 Subject: [PATCH 18/50] minor version --- Cargo.lock | 62 ++++++++++++++++++-------------------- Cargo.toml | 2 +- libs/hbb_common/Cargo.toml | 4 +-- 3 files changed, 32 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 884df3c56..3a5c34861 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2144,7 +2144,7 @@ dependencies = [ "log", "mac_address", "protobuf", - "protobuf-codegen-pure", + "protobuf-codegen", "quinn", "rand 0.8.5", "regex", @@ -3526,60 +3526,56 @@ dependencies = [ [[package]] name = "protobuf" -version = "3.0.0-alpha.2" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5ef59c35c7472ce5e1b6c5924b87585143d1fc2cf39eae0009bba6c4df62f1" +checksum = "4ee4a7d8b91800c8f167a6268d1a1026607368e1adc84e98fe044aeb905302f7" +dependencies = [ + "bytes", + "once_cell", + "protobuf-support", + "thiserror", +] [[package]] name = "protobuf-codegen" -version = "3.0.0-alpha.2" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89100ee819f69b77a4cab389fec9dd155a305af4c615e6413ec1ef9341f333ef" +checksum = "07b893e5e7d3395545d5244f8c0d33674025bd566b26c03bfda49b82c6dec45e" dependencies = [ "anyhow", + "once_cell", "protobuf", "protobuf-parse", - "thiserror", -] - -[[package]] -name = "protobuf-codegen-pure" -version = "3.0.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79453e74d08190551e821533ee42c447f9e21ca26f83520e120e6e8af27f6879" -dependencies = [ - "anyhow", - "protobuf", - "protobuf-codegen", - "protobuf-parse", - "thiserror", -] - -[[package]] -name = "protobuf-parse" -version = "3.0.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c265ffc69976efc3056955b881641add3186ad0be893ef10622482d80d1d2b68" -dependencies = [ - "anyhow", - "protobuf", - "protoc", + "regex", "tempfile", "thiserror", ] [[package]] -name = "protoc" -version = "3.0.0-alpha.2" +name = "protobuf-parse" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f1f8b318a54d18fbe542513331e058f4f8ce6502e542e057c50c7e5e803fdab" +checksum = "9b1447dd751c434cc1b415579837ebd0411ed7d67d465f38010da5d7cd33af4d" dependencies = [ "anyhow", + "indexmap", "log", + "protobuf", + "protobuf-support", + "tempfile", "thiserror", "which 4.2.5", ] +[[package]] +name = "protobuf-support" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca157fe12fc7ee2e315f2f735e27df41b3d97cdd70ea112824dac1ffb08ee1c" +dependencies = [ + "thiserror", +] + [[package]] name = "quest" version = "0.3.0" diff --git a/Cargo.toml b/Cargo.toml index f270f7b30..55a9d0a2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -101,7 +101,7 @@ async-process = "1.3" [target.'cfg(target_os = "android")'.dependencies] android_logger = "0.11" -jni = "0.19.0" +jni = "0.19" [target.'cfg(any(target_os = "android", target_os = "ios"))'.dependencies] flutter_rust_bridge = "1.30.0" diff --git a/libs/hbb_common/Cargo.toml b/libs/hbb_common/Cargo.toml index 000cbae96..355be3684 100644 --- a/libs/hbb_common/Cargo.toml +++ b/libs/hbb_common/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -protobuf = { version = "3.1.0", features = ["with-bytes"] } +protobuf = { version = "3.1", features = ["with-bytes"] } tokio = { version = "1.15", features = ["full"] } tokio-util = { version = "0.6", features = ["full"] } futures = "0.3" @@ -38,7 +38,7 @@ mac_address = "1.1" quic = [] [build-dependencies] -protobuf-codegen = { version = "3.1.0" } +protobuf-codegen = { version = "3.1" } [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3", features = ["winuser"] } From d3fc6ccd9ca5412f3d49f5225e35d9e57fc6b891 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 15 Jul 2022 01:29:52 +0800 Subject: [PATCH 19/50] upgrade tokio --- Cargo.lock | 293 +++++++++++++++++++------------------ Cargo.toml | 4 +- libs/hbb_common/Cargo.toml | 4 +- src/port_forward.rs | 2 +- src/server/connection.rs | 2 +- 5 files changed, 154 insertions(+), 151 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3a5c34861..fb5c4cd12 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,6 +29,17 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "0.7.18" @@ -195,9 +206,9 @@ dependencies = [ [[package]] name = "async-task" -version = "4.2.0" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9" +checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" [[package]] name = "async-trait" @@ -218,7 +229,7 @@ checksum = "2c3d816ce6f0e2909a96830d6911c2aff044370b1ef92d7f267b43bae5addedd" dependencies = [ "atk-sys", "bitflags", - "glib 0.15.11", + "glib 0.15.12", "libc", ] @@ -277,9 +288,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.65" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61" +checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" dependencies = [ "addr2line", "cc", @@ -362,9 +373,9 @@ checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" [[package]] name = "bytemuck" -version = "1.9.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdead85bdec19c194affaeeb670c0e41fe23de31459efd1c174d049269cf02cc" +checksum = "c53dfa917ec274df8ed3c572698f381a24eef2efba9492d797301b72b6db408a" [[package]] name = "byteorder" @@ -386,13 +397,13 @@ checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" [[package]] name = "cairo-rs" -version = "0.15.11" +version = "0.15.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62be3562254e90c1c6050a72aa638f6315593e98c5cdaba9017cedbabf0a5dee" +checksum = "c76ee391b03d35510d9fa917357c7f1855bd9a6659c95a1b392e33f49b3369bc" dependencies = [ "bitflags", "cairo-sys-rs", - "glib 0.15.11", + "glib 0.15.12", "libc", "thiserror", ] @@ -424,7 +435,7 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "869119e97797867fd90f5e22af7d0bd274bd4635ebb9eb68c04f3f513ae6c412" dependencies = [ - "serde 1.0.137", + "serde 1.0.139", ] [[package]] @@ -433,7 +444,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" dependencies = [ - "serde 1.0.137", + "serde 1.0.139", ] [[package]] @@ -444,25 +455,25 @@ checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" dependencies = [ "camino", "cargo-platform", - "semver 1.0.10", - "serde 1.0.137", - "serde_json 1.0.81", + "semver 1.0.12", + "serde 1.0.139", + "serde_json 1.0.82", ] [[package]] name = "cbindgen" -version = "0.24.3" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6358dedf60f4d9b8db43ad187391afe959746101346fe51bb978126bec61dfb" +checksum = "5b6d248e3ca02f3fbfabcb9284464c596baec223a26d91bbf44a5a62ddb0d900" dependencies = [ - "clap 3.2.6", + "clap 3.2.12", "heck 0.4.0", "indexmap", "log", "proc-macro2", "quote", - "serde 1.0.137", - "serde_json 1.0.81", + "serde 1.0.139", + "serde_json 1.0.82", "syn", "tempfile", "toml", @@ -553,9 +564,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.6" +version = "3.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1fe12880bae935d142c8702d500c63a4e8634b6c3c57ad72bf978fc7b6249a" +checksum = "ab8b79fe3946ceb4a0b1c080b4018992b8d27e9ff363644c1c9b6387c854614d" dependencies = [ "atty", "bitflags", @@ -568,9 +579,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87eba3c8c7f42ef17f6c659fc7416d0f4758cd3e58861ee63c5fa4a4dde649e4" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" dependencies = [ "os_str_bytes", ] @@ -582,7 +593,7 @@ dependencies = [ "cc", "hbb_common", "lazy_static", - "serde 1.0.137", + "serde 1.0.139", "serde_derive", "thiserror", ] @@ -693,7 +704,7 @@ version = "0.4.0" source = "git+https://github.com/open-trade/confy#630cc28a396cb7d01eefdd9f3824486fe4d8554b" dependencies = [ "directories-next", - "serde 1.0.137", + "serde 1.0.139", "thiserror", "toml", ] @@ -931,9 +942,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +checksum = "2ccfd8c0ee4cce11e45b3fd6f9d5e69e0cc62912aa6a0cb1bf4617b0eba5a12f" dependencies = [ "generic-array", "typenum", @@ -1156,9 +1167,9 @@ dependencies = [ [[package]] name = "dbus" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de0a745c25b32caa56b82a3950f5fec7893a960f4c10ca3b02060b0c38d8c2ce" +checksum = "6f8bcdd56d2e5c4ed26a529c5a9029f5db8290d433497506f958eae3be148eb6" dependencies = [ "libc", "libdbus-sys", @@ -1268,7 +1279,7 @@ checksum = "7f3f119846c823f9eafcf953a8f6ffb6ed69bf6240883261a7f13b634579a51f" dependencies = [ "lazy_static", "regex", - "serde 1.0.137", + "serde 1.0.139", "strsim 0.10.0", ] @@ -1295,9 +1306,9 @@ dependencies = [ [[package]] name = "either" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" [[package]] name = "encoding_rs" @@ -1317,7 +1328,7 @@ dependencies = [ "log", "objc", "pkg-config", - "serde 1.0.137", + "serde 1.0.139", "serde_derive", "unicode-segmentation", "winapi 0.3.9", @@ -1418,14 +1429,14 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0408e2626025178a6a7f7ffc05a25bc47103229f19c113755de7bf63816290c" +checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c" dependencies = [ "cfg-if 1.0.0", "libc", "redox_syscall", - "winapi 0.3.9", + "windows-sys 0.36.1", ] [[package]] @@ -1460,9 +1471,9 @@ dependencies = [ [[package]] name = "flutter_rust_bridge" -version = "1.34.2" +version = "1.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56ac4e92d08407968b7efba9cda734935f6ebbffd521f9255b8f516d8c2cede" +checksum = "b7e7e4af55d6a36aad9573737a12fba774999e4d6dd5e668e29c25bb473f85f3" dependencies = [ "allo-isolate", "anyhow", @@ -1474,9 +1485,9 @@ dependencies = [ [[package]] name = "flutter_rust_bridge_codegen" -version = "1.34.2" +version = "1.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2bd4b68e21ce08b9afe3332c37c1eef2799bc36c0521890f5aaa303942b7df2" +checksum = "3209735fd687b06b8d770ec008874119b91f7f46b4a73d17226d5c337435bb74" dependencies = [ "anyhow", "cargo_metadata", @@ -1489,7 +1500,7 @@ dependencies = [ "pathdiff", "quote", "regex", - "serde 1.0.137", + "serde 1.0.139", "serde_yaml", "structopt", "syn", @@ -1500,9 +1511,9 @@ dependencies = [ [[package]] name = "flutter_rust_bridge_macros" -version = "1.34.2" +version = "1.37.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d4540ab97380ed5af0212f8b18ff84a5f32acc8247f8f731311516dd105363f" +checksum = "4b78a1a69afcf28951d0801e9be838b236734c8827a0eadbb71fb651aa2e9fed" [[package]] name = "fnv" @@ -1681,7 +1692,7 @@ dependencies = [ "gdk-pixbuf", "gdk-sys", "gio", - "glib 0.15.11", + "glib 0.15.12", "libc", "pango", ] @@ -1695,7 +1706,7 @@ dependencies = [ "bitflags", "gdk-pixbuf-sys", "gio", - "glib 0.15.11", + "glib 0.15.12", "libc", ] @@ -1768,16 +1779,16 @@ checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" [[package]] name = "gio" -version = "0.15.11" +version = "0.15.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f132be35e05d9662b9fa0fee3f349c6621f7782e0105917f4cc73c1bf47eceb" +checksum = "68fdbc90312d462781a395f7a16d96a2b379bb6ef8cd6310a2df272771c4283b" dependencies = [ "bitflags", "futures-channel", "futures-core", "futures-io", "gio-sys", - "glib 0.15.11", + "glib 0.15.12", "libc", "once_cell", "thiserror", @@ -1817,9 +1828,9 @@ dependencies = [ [[package]] name = "glib" -version = "0.15.11" +version = "0.15.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd124026a2fa8c33a3d17a3fe59c103f2d9fa5bd92c19e029e037736729abeab" +checksum = "edb0306fbad0ab5428b0ca674a23893db909a98582969c9b537be4ced78c505d" dependencies = [ "bitflags", "futures-channel", @@ -2061,7 +2072,7 @@ dependencies = [ "gdk", "gdk-pixbuf", "gio", - "glib 0.15.11", + "glib 0.15.12", "gtk-sys", "gtk3-macros", "libc", @@ -2117,15 +2128,18 @@ dependencies = [ "indexmap", "slab", "tokio", - "tokio-util 0.7.3", + "tokio-util", "tracing", ] [[package]] name = "hashbrown" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" +checksum = "607c8a29735385251a339424dd462993c0fed8fa09d378f259377df08c126022" +dependencies = [ + "ahash", +] [[package]] name = "hbb_common" @@ -2148,14 +2162,14 @@ dependencies = [ "quinn", "rand 0.8.5", "regex", - "serde 1.0.137", + "serde 1.0.139", "serde_derive", - "serde_json 1.0.81", + "serde_json 1.0.82", "socket2 0.3.19", "sodiumoxide", "tokio", "tokio-socks", - "tokio-util 0.6.10", + "tokio-util", "toml", "winapi 0.3.9", "zstd", @@ -2234,21 +2248,21 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hwcodec" version = "0.1.0" -source = "git+https://github.com/21pages/hwcodec#bfc558d2375928b0a59336cfc72336415db27066" +source = "git+https://github.com/21pages/hwcodec#adb15a681d69955048f85f5684525378289ab780" dependencies = [ "bindgen", "cc", "log", - "serde 1.0.137", + "serde 1.0.139", "serde_derive", - "serde_json 1.0.81", + "serde_json 1.0.82", ] [[package]] name = "hyper" -version = "0.14.19" +version = "0.14.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f" +checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" dependencies = [ "bytes", "futures-channel", @@ -2456,11 +2470,11 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libappindicator" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b29fab3280d59f3d06725f75da9ef9a1b001b2c748b1abfebd1c966c61d7de" +checksum = "db2d3cb96d092b4824cb306c9e544c856a4cb6210c1081945187f7f1924b47e8" dependencies = [ - "glib 0.15.11", + "glib 0.15.12", "gtk", "gtk-sys", "libappindicator-sys", @@ -2469,9 +2483,9 @@ dependencies = [ [[package]] name = "libappindicator-sys" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0e019ae1a736a858f4c52b58af2ca6e797f27d7fe534e8a56776d74a8f2535" +checksum = "f1b3b6681973cea8cc3bce7391e6d7d5502720b80a581c9a95c9cbaf592826aa" dependencies = [ "gtk-sys", "libloading 0.7.3", @@ -3160,9 +3174,9 @@ dependencies = [ [[package]] name = "object" -version = "0.28.4" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" dependencies = [ "memchr", ] @@ -3192,9 +3206,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" [[package]] name = "openssl-probe" @@ -3230,7 +3244,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22e4045548659aee5313bde6c582b0d83a627b7904dd20dc2d9ef0895d414e4f" dependencies = [ "bitflags", - "glib 0.15.11", + "glib 0.15.12", "libc", "once_cell", "pango-sys", @@ -3250,8 +3264,8 @@ dependencies = [ [[package]] name = "parity-tokio-ipc" -version = "0.7.3" -source = "git+https://github.com/open-trade/parity-tokio-ipc#64d5b6b11464d01bfe75b3b79a49bd455b79e352" +version = "0.7.3-1" +source = "git+https://github.com/open-trade/parity-tokio-ipc#20b2895910161605210657f3e751edd55321f698" dependencies = [ "futures", "libc", @@ -3390,18 +3404,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" +checksum = "78203e83c48cffbe01e4a2d35d566ca4de445d79a85372fc64e378bfc812a260" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" +checksum = "710faf75e1b33345361201d36d04e98ac1ed8909151a017ed384700836104c74" dependencies = [ "proc-macro2", "quote", @@ -3832,8 +3846,8 @@ dependencies = [ [[package]] name = "rdev" -version = "0.5.0" -source = "git+https://github.com/open-trade/rdev#fbbefd0b5d87095a7349965aec9ecd33de7035ac" +version = "0.5.0-1" +source = "git+https://github.com/open-trade/rdev#a9b6ea462956f289b4a48e81f2ea7dda33cd8047" dependencies = [ "cocoa 0.22.0", "core-foundation 0.7.0", @@ -3885,9 +3899,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.5.6" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" dependencies = [ "aho-corasick", "memchr", @@ -3896,9 +3910,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.26" +version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "remove_dir_all" @@ -3944,8 +3958,8 @@ dependencies = [ "pin-project-lite", "rustls", "rustls-pemfile 1.0.0", - "serde 1.0.137", - "serde_json 1.0.81", + "serde 1.0.139", + "serde_json 1.0.82", "serde_urlencoded", "tokio", "tokio-rustls", @@ -3991,8 +4005,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bf099a1888612545b683d2661a1940089f6c2e5a8e38979b2159da876bfd956" dependencies = [ "libc", - "serde 1.0.137", - "serde_json 1.0.81", + "serde 1.0.139", + "serde_json 1.0.82", "winapi 0.3.9", ] @@ -4058,7 +4072,7 @@ dependencies = [ "base64", "cc", "cfg-if 1.0.0", - "clap 3.2.6", + "clap 3.2.12", "clipboard", "cocoa 0.24.0", "core-foundation 0.9.3", @@ -4095,9 +4109,9 @@ dependencies = [ "samplerate", "sciter-rs", "scrap", - "serde 1.0.137", + "serde 1.0.139", "serde_derive", - "serde_json 1.0.81", + "serde_json 1.0.82", "sha2", "simple_rc", "sys-locale", @@ -4265,8 +4279,8 @@ dependencies = [ "num_cpus", "quest", "repng", - "serde 1.0.137", - "serde_json 1.0.81", + "serde 1.0.139", + "serde_json 1.0.82", "target_build_utils", "tracing", "webm", @@ -4317,11 +4331,11 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.10" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41d061efea015927ac527063765e73601444cdc344ba855bc7bd44578b25e1c" +checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1" dependencies = [ - "serde 1.0.137", + "serde 1.0.139", ] [[package]] @@ -4341,18 +4355,18 @@ checksum = "34b623917345a631dc9608d5194cc206b3fe6c3554cd1c75b937e55e285254af" [[package]] name = "serde" -version = "1.0.137" +version = "1.0.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +checksum = "0171ebb889e45aa68b44aee0859b3eede84c6f5f5c228e6f140c0b2a0a46cad6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.137" +version = "1.0.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" +checksum = "dc1d3230c1de7932af58ad8ffbe1d784bd55efd5a9d84ac24f69c72d83543dfb" dependencies = [ "proc-macro2", "quote", @@ -4373,13 +4387,13 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" dependencies = [ "itoa 1.0.2", "ryu", - "serde 1.0.137", + "serde 1.0.139", ] [[package]] @@ -4391,18 +4405,18 @@ dependencies = [ "form_urlencoded", "itoa 1.0.2", "ryu", - "serde 1.0.137", + "serde 1.0.139", ] [[package]] name = "serde_yaml" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707d15895415db6628332b737c838b88c598522e4dc70647e59b72312924aebc" +checksum = "1ec0091e1f5aa338283ce049bd9dfefd55e1f168ac233e85c1ffe0038fb48cbe" dependencies = [ "indexmap", "ryu", - "serde 1.0.137", + "serde 1.0.139", "yaml-rust", ] @@ -4454,7 +4468,7 @@ version = "0.1.0" dependencies = [ "confy", "hbb_common", - "serde 1.0.137", + "serde 1.0.139", "serde_derive", "walkdir", ] @@ -4473,9 +4487,9 @@ checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" [[package]] name = "smallvec" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc88c725d61fc6c3132893370cac4a0200e3fedf5da8331c570664b1987f5ca2" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" [[package]] name = "smithay-client-toolkit" @@ -4526,7 +4540,7 @@ dependencies = [ "ed25519", "libc", "libsodium-sys", - "serde 1.0.137", + "serde 1.0.139", ] [[package]] @@ -4827,10 +4841,11 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.19.2" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" +checksum = "57aec3cfa4c296db7255446efb4928a6be304b431a806216105542a67b6ca82e" dependencies = [ + "autocfg 1.1.0", "bytes", "libc", "memchr", @@ -4869,8 +4884,8 @@ dependencies = [ [[package]] name = "tokio-socks" -version = "0.5.1" -source = "git+https://github.com/open-trade/tokio-socks#c34272f219b24dc6508f13fa81eff9850e616ce2" +version = "0.5.1-1" +source = "git+https://github.com/open-trade/tokio-socks#7034e79263ce25c348be072808d7601d82cd892d" dependencies = [ "bytes", "either", @@ -4880,23 +4895,7 @@ dependencies = [ "pin-project", "thiserror", "tokio", - "tokio-util 0.6.10", -] - -[[package]] -name = "tokio-util" -version = "0.6.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" -dependencies = [ - "bytes", - "futures-core", - "futures-io", - "futures-sink", - "log", - "pin-project-lite", - "slab", - "tokio", + "tokio-util", ] [[package]] @@ -4907,8 +4906,12 @@ checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", + "futures-util", + "hashbrown", "pin-project-lite", + "slab", "tokio", "tracing", ] @@ -4919,7 +4922,7 @@ version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" dependencies = [ - "serde 1.0.137", + "serde 1.0.139", ] [[package]] @@ -4942,9 +4945,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c" +checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" dependencies = [ "proc-macro2", "quote", @@ -5018,9 +5021,9 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "ucd-trie" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" +checksum = "89570599c4fe5585de2b388aab47e99f7fa4e9238a1399f707a02e356058141c" [[package]] name = "unicode-bidi" @@ -5036,9 +5039,9 @@ checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" [[package]] name = "unicode-normalization" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dee68f85cab8cf68dec42158baf3a79a1cdc065a8b103025965d6ccb7f6cbd" +checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" dependencies = [ "tinyvec", ] @@ -5119,7 +5122,7 @@ dependencies = [ "cc", "hbb_common", "lazy_static", - "serde 1.0.137", + "serde 1.0.139", "serde_derive", "thiserror", ] @@ -5336,18 +5339,18 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.22.3" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d8de8415c823c8abd270ad483c6feeac771fad964890779f9a8cb24fbbc1bf" +checksum = "f1c760f0d366a6c24a02ed7816e23e691f5d92291f94d15e836006fd11b04daf" dependencies = [ "webpki", ] [[package]] name = "weezl" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c97e489d8f836838d497091de568cf16b117486d529ec5579233521065bd5e4" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" [[package]] name = "wepoll-ffi" diff --git a/Cargo.toml b/Cargo.toml index 55a9d0a2a..ff17ac858 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -104,7 +104,7 @@ android_logger = "0.11" jni = "0.19" [target.'cfg(any(target_os = "android", target_os = "ios"))'.dependencies] -flutter_rust_bridge = "1.30.0" +flutter_rust_bridge = "=1.30.0" [workspace] members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display", "libs/simple_rc"] @@ -122,7 +122,7 @@ winapi = { version = "0.3", features = [ "winnt" ] } cc = "1.0" hbb_common = { path = "libs/hbb_common" } simple_rc = { path = "libs/simple_rc", optional = true } -flutter_rust_bridge_codegen = "1.30.0" +flutter_rust_bridge_codegen = "=1.30.0" [dev-dependencies] hound = "3.4" diff --git a/libs/hbb_common/Cargo.toml b/libs/hbb_common/Cargo.toml index 355be3684..acb93c78b 100644 --- a/libs/hbb_common/Cargo.toml +++ b/libs/hbb_common/Cargo.toml @@ -8,8 +8,8 @@ edition = "2018" [dependencies] protobuf = { version = "3.1", features = ["with-bytes"] } -tokio = { version = "1.15", features = ["full"] } -tokio-util = { version = "0.6", features = ["full"] } +tokio = { version = "1.20", features = ["full"] } +tokio-util = { version = "0.7", features = ["full"] } futures = "0.3" bytes = "1.1" log = "0.4" diff --git a/src/port_forward.rs b/src/port_forward.rs index 36d011b37..6d7a25b68 100644 --- a/src/port_forward.rs +++ b/src/port_forward.rs @@ -183,7 +183,7 @@ async fn run_forward(forward: Framed, stream: Stream) -> }, res = stream.next() => { if let Some(Ok(bytes)) = res { - allow_err!(forward.send(bytes.into()).await); + allow_err!(forward.send(bytes).await); } else { break; } diff --git a/src/server/connection.rs b/src/server/connection.rs index e8344c4a7..dbf1545ae 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -492,7 +492,7 @@ impl Connection { res = self.stream.next() => { if let Some(res) = res { last_recv_time = Instant::now(); - timeout(SEND_TIMEOUT_OTHER, forward.send(res?.into())).await??; + timeout(SEND_TIMEOUT_OTHER, forward.send(res?)).await??; } else { bail!("Stream reset by the peer"); } From 7e0f7be95c6df7636c07d9445d477422420d78b5 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 12 Jul 2022 22:34:05 +0800 Subject: [PATCH 20/50] lan_discovery_WOL: mid commit Signed-off-by: fufesou --- Cargo.lock | 83 +++++++++++++++ Cargo.toml | 1 + src/rendezvous_mediator.rs | 209 ++++++++++++++++++++++++++++++------- src/ui/ab.tis | 4 +- 4 files changed, 256 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fb5c4cd12..e581e842e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1176,6 +1176,18 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "default-net" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e70d471b0ba4e722c85651b3bb04b6880dfdb1224a43ade80c1295314db646" +dependencies = [ + "libc", + "memalloc", + "system-configuration", + "windows", +] + [[package]] name = "deflate" version = "0.8.6" @@ -2673,6 +2685,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +[[package]] +name = "memalloc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df39d232f5c40b0891c10216992c2f250c054105cb1e56f0fc9032db6203ecc1" + [[package]] name = "memchr" version = "2.5.0" @@ -4080,6 +4098,7 @@ dependencies = [ "cpal", "ctrlc", "dasp", + "default-net", "dispatch", "enigo", "flexi_logger", @@ -4680,6 +4699,27 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "system-configuration" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75182f12f490e953596550b65ee31bda7c8e043d9386174b353bda50838c3fd" +dependencies = [ + "bitflags", + "core-foundation 0.9.3", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys 0.8.3", + "libc", +] + [[package]] name = "system-deps" version = "1.3.2" @@ -5450,6 +5490,19 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b749ebd2304aa012c5992d11a25d07b406bdbe5f79d371cb7a918ce501a19eb0" +dependencies = [ + "windows_aarch64_msvc 0.30.0", + "windows_i686_gnu 0.30.0", + "windows_i686_msvc 0.30.0", + "windows_x86_64_gnu 0.30.0", + "windows_x86_64_msvc 0.30.0", +] + [[package]] name = "windows-service" version = "0.4.0" @@ -5494,6 +5547,12 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52695a41e536859d5308cc613b4a022261a274390b25bd29dfff4bf08505f3c2" +[[package]] +name = "windows_aarch64_msvc" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29277a4435d642f775f63c7d1faeb927adba532886ce0287bd985bffb16b6bca" + [[package]] name = "windows_aarch64_msvc" version = "0.36.1" @@ -5506,6 +5565,12 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f54725ac23affef038fecb177de6c9bf065787c2f432f79e3c373da92f3e1d8a" +[[package]] +name = "windows_i686_gnu" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1145e1989da93956c68d1864f32fb97c8f561a8f89a5125f6a2b7ea75524e4b8" + [[package]] name = "windows_i686_gnu" version = "0.36.1" @@ -5518,6 +5583,12 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d5158a43cc43623c0729d1ad6647e62fa384a3d135fd15108d37c683461f64" +[[package]] +name = "windows_i686_msvc" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a09e3a0d4753b73019db171c1339cd4362c8c44baf1bcea336235e955954a6" + [[package]] name = "windows_i686_msvc" version = "0.36.1" @@ -5530,6 +5601,12 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc31f409f565611535130cfe7ee8e6655d3fa99c1c61013981e491921b5ce954" +[[package]] +name = "windows_x86_64_gnu" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca64fcb0220d58db4c119e050e7af03c69e6f4f415ef69ec1773d9aab422d5a" + [[package]] name = "windows_x86_64_gnu" version = "0.36.1" @@ -5542,6 +5619,12 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f2b8c7cbd3bfdddd9ab98769f9746a7fad1bca236554cd032b78d768bc0e89f" +[[package]] +name = "windows_x86_64_msvc" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08cabc9f0066848fef4bc6a1c1668e6efce38b661d2aeec75d18d8617eebb5f1" + [[package]] name = "windows_x86_64_msvc" version = "0.36.1" diff --git a/Cargo.toml b/Cargo.toml index ff17ac858..14cae5f9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,7 @@ rpassword = "6.0" base64 = "0.13" sysinfo = "0.23" num_cpus = "1.13" +default-net = "0.11.0" [target.'cfg(not(target_os = "linux"))'.dependencies] reqwest = { version = "0.11", features = ["json", "rustls-tls"], default-features=false } diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index 28f2dbc3f..42b8c49f2 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -1,4 +1,7 @@ -use crate::server::{check_zombie, new as new_server, ServerPtr}; +use crate::{ + ipc::get_id, + server::{check_zombie, new as new_server, ServerPtr}, +}; use hbb_common::{ allow_err, anyhow::bail, @@ -15,8 +18,9 @@ use hbb_common::{ udp::FramedSocket, AddrMangle, IntoTargetAddr, ResultType, TargetAddr, }; +use serde_derive::{Deserialize, Serialize}; use std::{ - net::SocketAddr, + net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs, UdpSocket}, sync::{ atomic::{AtomicBool, Ordering}, Arc, Mutex, @@ -62,11 +66,9 @@ impl RendezvousMediator { direct_server(server_cloned).await; }); #[cfg(not(any(target_os = "android", target_os = "ios")))] - if crate::platform::is_installed() { - std::thread::spawn(move || { - allow_err!(lan_discovery()); - }); - } + std::thread::spawn(move || { + allow_err!(lan_discovery()); + }); loop { Config::reset_online(); if Config::get_option("stop-service").is_empty() { @@ -546,9 +548,19 @@ pub fn get_broadcast_port() -> u16 { (RENDEZVOUS_PORT + 3) as _ } -pub fn get_mac() -> String { +#[derive(Default, Serialize, Deserialize, Clone)] +pub struct DiscoveryPeer { + id: String, + mac: String, + ip: String, + username: String, + hostname: String, + platform: String, +} + +pub fn get_mac(ip: &IpAddr) -> String { #[cfg(not(any(target_os = "android", target_os = "ios")))] - if let Ok(Some(mac)) = mac_address::get_mac_address() { + if let Ok(mac) = get_mac_by_ip(ip) { mac.to_string() } else { "".to_owned() @@ -557,6 +569,56 @@ pub fn get_mac() -> String { "".to_owned() } +fn get_all_ipv4s() -> ResultType> { + let mut ipv4s = Vec::new(); + for interface in default_net::get_interfaces() { + for ipv4 in &interface.ipv4 { + ipv4s.push(ipv4.addr.clone()); + } + } + Ok(ipv4s) +} + +fn get_mac_by_ip(ip: &IpAddr) -> ResultType { + for interface in default_net::get_interfaces() { + match ip { + IpAddr::V4(local_ipv4) => { + if interface.ipv4.iter().any(|x| x.addr == *local_ipv4) { + if let Some(mac_addr) = interface.mac_addr { + return Ok(mac_addr.address()); + } + } + } + IpAddr::V6(local_ipv6) => { + if interface.ipv6.iter().any(|x| x.addr == *local_ipv6) { + if let Some(mac_addr) = interface.mac_addr { + return Ok(mac_addr.address()); + } + } + } + } + } + bail!("No interface found for ip: {:?}", ip); +} + +// Mainly from https://github.com/shellrow/default-net/blob/cf7ca24e7e6e8e566ed32346c9cfddab3f47e2d6/src/interface/shared.rs#L4 +pub fn get_ipaddr_by_peer(peer: A) -> Option { + let socket = match UdpSocket::bind("0.0.0.0:0") { + Ok(s) => s, + Err(_) => return None, + }; + + match socket.connect(peer) { + Ok(()) => (), + Err(_) => return None, + }; + + match socket.local_addr() { + Ok(addr) => return Some(addr.ip()), + Err(_) => return None, + }; +} + fn lan_discovery() -> ResultType<()> { let addr = SocketAddr::from(([0, 0, 0, 0], get_broadcast_port())); let socket = std::net::UdpSocket::bind(addr)?; @@ -569,18 +631,20 @@ fn lan_discovery() -> ResultType<()> { match msg_in.union { Some(rendezvous_message::Union::PeerDiscovery(p)) => { if p.cmd == "ping" { - let mut msg_out = Message::new(); - let peer = PeerDiscovery { - cmd: "pong".to_owned(), - mac: get_mac(), - id: Config::get_id(), - hostname: whoami::hostname(), - username: crate::platform::get_active_username(), - platform: whoami::platform().to_string(), - ..Default::default() - }; - msg_out.set_peer_discovery(peer); - socket.send_to(&msg_out.write_to_bytes()?, addr).ok(); + if let Some(self_addr) = get_ipaddr_by_peer(&addr) { + let mut msg_out = Message::new(); + let peer = PeerDiscovery { + cmd: "pong".to_owned(), + mac: get_mac(&self_addr), + id: Config::get_id(), + hostname: whoami::hostname(), + username: crate::platform::get_active_username(), + platform: whoami::platform().to_string(), + ..Default::default() + }; + msg_out.set_peer_discovery(peer); + socket.send_to(&msg_out.write_to_bytes()?, addr).ok(); + } } } _ => {} @@ -590,10 +654,25 @@ fn lan_discovery() -> ResultType<()> { } } -pub fn discover() -> ResultType<()> { - let addr = SocketAddr::from(([0, 0, 0, 0], 0)); - let socket = std::net::UdpSocket::bind(addr)?; - socket.set_broadcast(true)?; +fn create_broadcast_sockets() -> ResultType> { + let mut sockets = Vec::new(); + for v4_addr in get_all_ipv4s()? { + if v4_addr.is_private() { + let s = UdpSocket::bind(SocketAddr::from((v4_addr, 0)))?; + s.set_broadcast(true)?; + log::debug!("Bind socket to {}", &v4_addr); + sockets.push(s) + } + } + Ok(sockets) +} + +fn send_query() -> ResultType> { + let sockets = create_broadcast_sockets()?; + if sockets.is_empty() { + bail!("Found no ipv4 addresses"); + } + let mut msg_out = Message::new(); let peer = PeerDiscovery { cmd: "ping".to_owned(), @@ -601,25 +680,49 @@ pub fn discover() -> ResultType<()> { }; msg_out.set_peer_discovery(peer); let maddr = SocketAddr::from(([255, 255, 255, 255], get_broadcast_port())); - socket.send_to(&msg_out.write_to_bytes()?, maddr)?; + for socket in &sockets { + socket.send_to(&msg_out.write_to_bytes()?, maddr)?; + } log::info!("discover ping sent"); + Ok(sockets) +} + +fn wait_response( + socket: UdpSocket, + timeout: Option, +) -> ResultType> { let mut last_recv_time = Instant::now(); - let mut last_write_time = Instant::now(); - let mut last_write_n = 0; - // to-do: load saved peers, and update incrementally (then we can see offline) let mut peers = Vec::new(); - let mac = get_mac(); - socket.set_read_timeout(Some(std::time::Duration::from_millis(10)))?; + + socket.set_read_timeout(timeout)?; loop { let mut buf = [0; 2048]; - if let Ok((len, _)) = socket.recv_from(&mut buf) { + if let Ok((len, addr)) = socket.recv_from(&mut buf) { if let Ok(msg_in) = Message::parse_from_bytes(&buf[0..len]) { match msg_in.union { Some(rendezvous_message::Union::PeerDiscovery(p)) => { last_recv_time = Instant::now(); if p.cmd == "pong" { - if p.mac != mac { - peers.push((p.id, p.username, p.hostname, p.platform)); + let mac = if let Some(self_addr) = get_ipaddr_by_peer(&addr) { + get_mac(&self_addr) + } else { + "".to_owned() + }; + + let is_self = if !mac.is_empty() { + p.mac == mac + } else { + p.id == get_id() + }; + if !is_self { + peers.push(DiscoveryPeer { + id: p.id.clone(), + mac: p.mac.clone(), + ip: addr.to_string(), + username: p.username.clone(), + hostname: p.hostname.clone(), + platform: p.platform.clone(), + }); } } } @@ -627,15 +730,43 @@ pub fn discover() -> ResultType<()> { } } } - if last_write_time.elapsed().as_millis() > 300 && last_write_n != peers.len() { - config::LanPeers::store(serde_json::to_string(&peers)?); - last_write_time = Instant::now(); - last_write_n = peers.len(); - } if last_recv_time.elapsed().as_millis() > 3_000 { break; } } + Ok(peers) +} + +pub fn discover() -> ResultType<()> { + let sockets = send_query()?; + let mut join_handles = Vec::new(); + for socket in sockets { + let handle = std::thread::spawn(move || { + wait_response(socket, Some(std::time::Duration::from_millis(10))) + }); + join_handles.push(handle); + } + + // to-do: load saved peers, and update incrementally (then we can see offline) + let mut peers: Vec = match serde_json::from_str(&config::LanPeers::load().peers) + { + Ok(p) => p, + _ => Vec::new(), + }; + + for handle in join_handles { + match handle.join() { + Ok(Ok(mut tmp)) => { + peers.append(&mut tmp); + } + Ok(Err(e)) => { + log::error!("Failed lan discove {e}"); + } + Err(e) => { + log::error!("Failed join lan discove thread {e:?}"); + } + } + } log::info!("discover ping done"); config::LanPeers::store(serde_json::to_string(&peers)?); Ok(()) diff --git a/src/ui/ab.tis b/src/ui/ab.tis index 716ff1ba6..f2c8746d4 100644 --- a/src/ui/ab.tis +++ b/src/ui/ab.tis @@ -526,7 +526,7 @@ class MultipleSessions: Reactor.Component {
{translate('Recent Sessions')} {translate('Favorites')} - {handler.is_installed() && {translate('Discovered')}} + {{translate('Discovered')}} {translate('Address Book')}
{!this.hidden && } @@ -534,7 +534,7 @@ class MultipleSessions: Reactor.Component { {!this.hidden && ((type == "fav" && ) || - (type == "lan" && handler.is_installed() && ) || + (type == "lan" && ) || (type == "ab" && ) || )} ; From cbb34fb0210baa70ca3b5accb80d5b262e47bc78 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 13 Jul 2022 11:18:53 +0800 Subject: [PATCH 21/50] lan_discovery_WOL: lan discovery almost done Signed-off-by: fufesou --- libs/hbb_common/src/udp.rs | 1 + src/rendezvous_mediator.rs | 122 +++++++++++++++++++++++++------------ 2 files changed, 85 insertions(+), 38 deletions(-) diff --git a/libs/hbb_common/src/udp.rs b/libs/hbb_common/src/udp.rs index 3532dd1e0..c2d5057f2 100644 --- a/libs/hbb_common/src/udp.rs +++ b/libs/hbb_common/src/udp.rs @@ -37,6 +37,7 @@ fn new_socket(addr: SocketAddr, reuse: bool, buf_size: usize) -> Result u16 { (RENDEZVOUS_PORT + 3) as _ } -#[derive(Default, Serialize, Deserialize, Clone)] +#[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct DiscoveryPeer { id: String, - mac: String, - ip: String, + mac_ips: HashMap, username: String, hostname: String, platform: String, + online: bool, +} + +impl DiscoveryPeer { + fn is_same_peer(&self, other: &DiscoveryPeer) -> bool { + self.id == other.id && self.username == other.username + } } pub fn get_mac(ip: &IpAddr) -> String { @@ -690,9 +695,9 @@ fn send_query() -> ResultType> { fn wait_response( socket: UdpSocket, timeout: Option, -) -> ResultType> { + tx: UnboundedSender, +) -> ResultType<()> { let mut last_recv_time = Instant::now(); - let mut peers = Vec::new(); socket.set_read_timeout(timeout)?; loop { @@ -709,20 +714,18 @@ fn wait_response( "".to_owned() }; - let is_self = if !mac.is_empty() { - p.mac == mac - } else { - p.id == get_id() - }; - if !is_self { - peers.push(DiscoveryPeer { + if mac != p.mac { + allow_err!(tx.send(DiscoveryPeer { id: p.id.clone(), - mac: p.mac.clone(), - ip: addr.to_string(), + mac_ips: HashMap::from([( + p.mac.clone(), + addr.ip().to_string() + )]), username: p.username.clone(), hostname: p.hostname.clone(), platform: p.platform.clone(), - }); + online: true, + })); } } } @@ -734,40 +737,83 @@ fn wait_response( break; } } - Ok(peers) + Ok(()) } -pub fn discover() -> ResultType<()> { - let sockets = send_query()?; - let mut join_handles = Vec::new(); +fn spawn_wait_responses(sockets: Vec) -> UnboundedReceiver { + let (tx, rx) = unbounded_channel::<_>(); for socket in sockets { - let handle = std::thread::spawn(move || { - wait_response(socket, Some(std::time::Duration::from_millis(10))) + let tx_clone = tx.clone(); + std::thread::spawn(move || { + allow_err!(wait_response( + socket, + Some(std::time::Duration::from_millis(10)), + tx_clone + )); }); - join_handles.push(handle); } + rx +} - // to-do: load saved peers, and update incrementally (then we can see offline) +async fn handle_received_peers(mut rx: UnboundedReceiver) -> ResultType<()> { let mut peers: Vec = match serde_json::from_str(&config::LanPeers::load().peers) { Ok(p) => p, _ => Vec::new(), }; + peers.iter_mut().for_each(|peer| { + peer.online = false; + }); - for handle in join_handles { - match handle.join() { - Ok(Ok(mut tmp)) => { - peers.append(&mut tmp); - } - Ok(Err(e)) => { - log::error!("Failed lan discove {e}"); - } - Err(e) => { - log::error!("Failed join lan discove thread {e:?}"); + // handle received peers + let mut response_set = HashSet::new(); + let mut last_write_time = Instant::now() - std::time::Duration::from_secs(4); + loop { + tokio::select! { + data = rx.recv() => match data { + Some(peer) => { + let in_response_set = !response_set.insert(peer.id.clone()); + let mut pre_found = false; + for peer1 in &mut peers { + if peer1.is_same_peer(&peer) { + if in_response_set { + peer1.mac_ips.extend(peer.mac_ips.clone()); + peer1.hostname = peer.hostname.clone(); + peer1.platform = peer.platform.clone(); + peer1.online = true; + } else { + *peer1 = peer.clone(); + } + pre_found = true; + break + } + } + if !pre_found { + peers.push(peer); + } + + if last_write_time.elapsed().as_millis() > 300 { + config::LanPeers::store(serde_json::to_string(&peers)?); + last_write_time = Instant::now(); + } + } + None => { + break + } } } } - log::info!("discover ping done"); + config::LanPeers::store(serde_json::to_string(&peers)?); Ok(()) } + +#[tokio::main(flavor = "current_thread")] +pub async fn discover() -> ResultType<()> { + let sockets = send_query()?; + let rx = spawn_wait_responses(sockets); + handle_received_peers(rx).await?; + + log::info!("discover ping done"); + Ok(()) +} From 897d2b8e5703de6671fd853fe6f5f0ec7e2c69dd Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 13 Jul 2022 18:06:19 +0800 Subject: [PATCH 22/50] lan_discovery_WOL: remove discovered peer Signed-off-by: fufesou --- Cargo.lock | 23 +++++++++++++++++++++++ libs/hbb_common/Cargo.toml | 1 + libs/hbb_common/src/config.rs | 34 ++++++++++++++++++++++++++++++---- src/rendezvous_mediator.rs | 35 +++++++---------------------------- src/ui.rs | 9 ++++++++- src/ui/ab.tis | 5 ++++- 6 files changed, 73 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e581e842e..894382389 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2177,6 +2177,7 @@ dependencies = [ "serde 1.0.139", "serde_derive", "serde_json 1.0.82", + "serde_with", "socket2 0.3.19", "sodiumoxide", "tokio", @@ -4427,6 +4428,28 @@ dependencies = [ "serde 1.0.139", ] +[[package]] +name = "serde_with" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" +dependencies = [ + "serde 1.0.139", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" +dependencies = [ + "darling 0.13.4", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "serde_yaml" version = "0.8.25" diff --git a/libs/hbb_common/Cargo.toml b/libs/hbb_common/Cargo.toml index acb93c78b..6fec67193 100644 --- a/libs/hbb_common/Cargo.toml +++ b/libs/hbb_common/Cargo.toml @@ -23,6 +23,7 @@ directories-next = "2.0" rand = "0.8" serde_derive = "1.0" serde = "1.0" +serde_with = "1.14.0" lazy_static = "1.4" confy = { git = "https://github.com/open-trade/confy" } dirs-next = "2.0" diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index b4d8eaff3..ee8433770 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -856,10 +856,34 @@ impl LocalConfig { } } +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct DiscoveryPeer { + pub id: String, + #[serde(with = "serde_with::rust::map_as_tuple_list")] + pub mac_ips: HashMap, + pub username: String, + pub hostname: String, + pub platform: String, + pub online: bool, +} + +impl DiscoveryPeer { + pub fn is_same_peer(&self, other: &DiscoveryPeer) -> bool { + self.id == other.id && self.username == other.username + } +} + #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct LanPeers { - #[serde(default)] - pub peers: String, + // #[serde(default)] + // pub peers: String, + pub peers: Vec, +} + +#[derive(Serialize, Deserialize)] +struct MyConfig { + version: u8, + api_key: String, } impl LanPeers { @@ -874,8 +898,10 @@ impl LanPeers { } } - pub fn store(peers: String) { - let f = LanPeers { peers }; + pub fn store(peers: &Vec) { + let f = LanPeers { + peers: peers.clone(), + }; if let Err(err) = confy::store_path(Config::file_("_lan_peers"), f) { log::error!("Failed to store lan peers: {}", err); } diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index 9471ad4b7..d87357366 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -16,7 +16,6 @@ use hbb_common::{ udp::FramedSocket, AddrMangle, IntoTargetAddr, ResultType, TargetAddr, }; -use serde_derive::{Deserialize, Serialize}; use std::{ collections::{HashMap, HashSet}, net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs, UdpSocket}, @@ -547,22 +546,6 @@ pub fn get_broadcast_port() -> u16 { (RENDEZVOUS_PORT + 3) as _ } -#[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct DiscoveryPeer { - id: String, - mac_ips: HashMap, - username: String, - hostname: String, - platform: String, - online: bool, -} - -impl DiscoveryPeer { - fn is_same_peer(&self, other: &DiscoveryPeer) -> bool { - self.id == other.id && self.username == other.username - } -} - pub fn get_mac(ip: &IpAddr) -> String { #[cfg(not(any(target_os = "android", target_os = "ios")))] if let Ok(mac) = get_mac_by_ip(ip) { @@ -695,7 +678,7 @@ fn send_query() -> ResultType> { fn wait_response( socket: UdpSocket, timeout: Option, - tx: UnboundedSender, + tx: UnboundedSender, ) -> ResultType<()> { let mut last_recv_time = Instant::now(); @@ -715,7 +698,7 @@ fn wait_response( }; if mac != p.mac { - allow_err!(tx.send(DiscoveryPeer { + allow_err!(tx.send(config::DiscoveryPeer { id: p.id.clone(), mac_ips: HashMap::from([( p.mac.clone(), @@ -740,7 +723,7 @@ fn wait_response( Ok(()) } -fn spawn_wait_responses(sockets: Vec) -> UnboundedReceiver { +fn spawn_wait_responses(sockets: Vec) -> UnboundedReceiver { let (tx, rx) = unbounded_channel::<_>(); for socket in sockets { let tx_clone = tx.clone(); @@ -755,12 +738,8 @@ fn spawn_wait_responses(sockets: Vec) -> UnboundedReceiver) -> ResultType<()> { - let mut peers: Vec = match serde_json::from_str(&config::LanPeers::load().peers) - { - Ok(p) => p, - _ => Vec::new(), - }; +async fn handle_received_peers(mut rx: UnboundedReceiver) -> ResultType<()> { + let mut peers = config::LanPeers::load().peers; peers.iter_mut().for_each(|peer| { peer.online = false; }); @@ -793,7 +772,7 @@ async fn handle_received_peers(mut rx: UnboundedReceiver) -> Resu } if last_write_time.elapsed().as_millis() > 300 { - config::LanPeers::store(serde_json::to_string(&peers)?); + config::LanPeers::store(&peers); last_write_time = Instant::now(); } } @@ -804,7 +783,7 @@ async fn handle_received_peers(mut rx: UnboundedReceiver) -> Resu } } - config::LanPeers::store(serde_json::to_string(&peers)?); + config::LanPeers::store(&peers); Ok(()) } diff --git a/src/ui.rs b/src/ui.rs index a1f9093d0..4bf201c0e 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -541,6 +541,12 @@ impl UI { PeerConfig::remove(&id); } + fn remove_discovered(&mut self, id: String) { + let mut peers = config::LanPeers::load().peers; + peers.retain(|x| x.id != id); + config::LanPeers::store(&peers); + } + fn new_remote(&mut self, id: String, remote_type: String) { let mut lock = self.0.lock().unwrap(); let args = vec![format!("--{}", remote_type), id.clone()]; @@ -690,7 +696,7 @@ impl UI { } fn get_lan_peers(&self) -> String { - config::LanPeers::load().peers + serde_json::to_string(&config::LanPeers::load().peers).unwrap_or_default() } fn get_uuid(&self) -> String { @@ -781,6 +787,7 @@ impl sciter::EventHandler for UI { fn get_size(); fn new_remote(String, bool); fn remove_peer(String); + fn remove_discovered(String); fn get_connect_status(); fn get_mouse_time(); fn check_mouse_time(); diff --git a/src/ui/ab.tis b/src/ui/ab.tis index f2c8746d4..03768c6da 100644 --- a/src/ui/ab.tis +++ b/src/ui/ab.tis @@ -320,7 +320,7 @@ class SessionList: Reactor.Component {
  • RDP
  • {translate('Rename')}
  • - {this.type != "fav" && this.type != "lan" &&
  • {translate('Remove')}
  • } + {this.type != "fav" &&
  • {translate('Remove')}
  • } {is_win &&
  • {translate('Create Desktop Shortcut')}
  • }
  • {translate('Unremember Password')}
  • {(!this.type || this.type == "fav") &&
  • {translate('Add to Favorites')}
  • } @@ -429,6 +429,9 @@ class SessionList: Reactor.Component { break; } } + } else if (this.type == "lan") { + handler.remove_discovered(id); + app.update(); } else { handler.remove_peer(id); app.update(); From 7c323c86ac4f84a600d6f4f23030816fd4b98fae Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 13 Jul 2022 18:14:50 +0800 Subject: [PATCH 23/50] lan_discovery_WOL: remove ununsed action for lan menu Signed-off-by: fufesou --- src/ui/ab.tis | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ui/ab.tis b/src/ui/ab.tis index 03768c6da..2fda2a5e1 100644 --- a/src/ui/ab.tis +++ b/src/ui/ab.tis @@ -319,9 +319,9 @@ class SessionList: Reactor.Component { {false && !handler.using_public_server() &&
  • {svg_checkmark}{translate('Always connect via relay')}
  • }
  • RDP
  • -
  • {translate('Rename')}
  • + {this.type != "lan" &&
  • {translate('Rename')}
  • } {this.type != "fav" &&
  • {translate('Remove')}
  • } - {is_win &&
  • {translate('Create Desktop Shortcut')}
  • } + {is_win && this.type != "lan" &&
  • {translate('Create Desktop Shortcut')}
  • }
  • {translate('Unremember Password')}
  • {(!this.type || this.type == "fav") &&
  • {translate('Add to Favorites')}
  • } {(!this.type || this.type == "fav") &&
  • {translate('Remove from Favorites')}
  • } From 8fd4830710151aff5c7aa1795f554a75a4570743 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 14 Jul 2022 10:34:56 +0800 Subject: [PATCH 24/50] lan_discovery_WOL: Win10 test done Signed-off-by: fufesou --- Cargo.lock | 25 +++++++++++++++++++++ Cargo.toml | 1 + libs/hbb_common/src/config.rs | 2 +- src/rendezvous_mediator.rs | 42 ++++++++++++++++++++++++++++++----- src/ui.rs | 5 +++++ src/ui/ab.tis | 5 ++++- 6 files changed, 72 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 894382389..aa4c87dd8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -570,13 +570,28 @@ checksum = "ab8b79fe3946ceb4a0b1c080b4018992b8d27e9ff363644c1c9b6387c854614d" dependencies = [ "atty", "bitflags", + "clap_derive", "clap_lex", "indexmap", + "once_cell", "strsim 0.10.0", "termcolor", "textwrap 0.15.0", ] +[[package]] +name = "clap_derive" +version = "3.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed6db9e867166a43a53f7199b5e4d1f522a1e5bd626654be263c999ce59df39a" +dependencies = [ + "heck 0.4.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "clap_lex" version = "0.2.4" @@ -4146,6 +4161,7 @@ dependencies = [ "winit", "winreg 0.10.1", "winres", + "wol-rs", ] [[package]] @@ -5713,6 +5729,15 @@ dependencies = [ "toml", ] +[[package]] +name = "wol-rs" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7f97e69b28b256ccfb02472c25057132e234aa8368fea3bb0268def564ce1f2" +dependencies = [ + "clap 3.2.6", +] + [[package]] name = "ws2_32-sys" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index 14cae5f9d..c008f7828 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,7 @@ base64 = "0.13" sysinfo = "0.23" num_cpus = "1.13" default-net = "0.11.0" +wol-rs = "0.9.1" [target.'cfg(not(target_os = "linux"))'.dependencies] reqwest = { version = "0.11", features = ["json", "rustls-tls"], default-features=false } diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index ee8433770..8d2437c30 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -860,7 +860,7 @@ impl LocalConfig { pub struct DiscoveryPeer { pub id: String, #[serde(with = "serde_with::rust::map_as_tuple_list")] - pub mac_ips: HashMap, + pub ip_mac: HashMap, pub username: String, pub hostname: String, pub platform: String, diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index d87357366..f4f4fb15c 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -700,10 +700,9 @@ fn wait_response( if mac != p.mac { allow_err!(tx.send(config::DiscoveryPeer { id: p.id.clone(), - mac_ips: HashMap::from([( - p.mac.clone(), - addr.ip().to_string() - )]), + ip_mac: HashMap::from([ + (addr.ip().to_string(), p.mac.clone(),) + ]), username: p.username.clone(), hostname: p.hostname.clone(), platform: p.platform.clone(), @@ -744,7 +743,6 @@ async fn handle_received_peers(mut rx: UnboundedReceiver) peer.online = false; }); - // handle received peers let mut response_set = HashSet::new(); let mut last_write_time = Instant::now() - std::time::Duration::from_secs(4); loop { @@ -753,20 +751,24 @@ async fn handle_received_peers(mut rx: UnboundedReceiver) Some(peer) => { let in_response_set = !response_set.insert(peer.id.clone()); let mut pre_found = false; + // Try find and update peer for peer1 in &mut peers { if peer1.is_same_peer(&peer) { if in_response_set { - peer1.mac_ips.extend(peer.mac_ips.clone()); + // Merge ip_mac and update other infos + peer1.ip_mac.extend(peer.ip_mac.clone()); peer1.hostname = peer.hostname.clone(); peer1.platform = peer.platform.clone(); peer1.online = true; } else { + // Update all peer infos *peer1 = peer.clone(); } pre_found = true; break } } + // Push if not found if !pre_found { peers.push(peer); } @@ -796,3 +798,31 @@ pub async fn discover() -> ResultType<()> { log::info!("discover ping done"); Ok(()) } + +pub fn send_wol(id: String) { + let interfaces = default_net::get_interfaces(); + for peer in &config::LanPeers::load().peers { + if peer.id == id { + for (ip, mac) in peer.ip_mac.iter() { + if let Ok(mac_addr) = mac.parse() { + if let Ok(IpAddr::V4(ip)) = ip.parse() { + for interface in &interfaces { + for ipv4 in &interface.ipv4 { + if (u32::from(ipv4.addr) & u32::from(ipv4.netmask)) + == (u32::from(ip) & u32::from(ipv4.netmask)) + { + allow_err!(wol::send_wol( + mac_addr, + None, + Some(IpAddr::V4(ipv4.addr)) + )); + } + } + } + } + } + } + break; + } + } +} diff --git a/src/ui.rs b/src/ui.rs index 4bf201c0e..084422f6b 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -547,6 +547,10 @@ impl UI { config::LanPeers::store(&peers); } + fn send_wol(&mut self, id: String) { + crate::rendezvous_mediator::send_wol(id) + } + fn new_remote(&mut self, id: String, remote_type: String) { let mut lock = self.0.lock().unwrap(); let args = vec![format!("--{}", remote_type), id.clone()]; @@ -786,6 +790,7 @@ impl sciter::EventHandler for UI { fn closing(i32, i32, i32, i32); fn get_size(); fn new_remote(String, bool); + fn send_wol(String); fn remove_peer(String); fn remove_discovered(String); fn get_connect_status(); diff --git a/src/ui/ab.tis b/src/ui/ab.tis index 2fda2a5e1..5b822ed22 100644 --- a/src/ui/ab.tis +++ b/src/ui/ab.tis @@ -318,10 +318,11 @@ class SessionList: Reactor.Component {
  • {translate('TCP Tunneling')}
  • {false && !handler.using_public_server() &&
  • {svg_checkmark}{translate('Always connect via relay')}
  • }
  • RDP
  • +
  • {translate('WOL')}
  • {this.type != "lan" &&
  • {translate('Rename')}
  • } {this.type != "fav" &&
  • {translate('Remove')}
  • } - {is_win && this.type != "lan" &&
  • {translate('Create Desktop Shortcut')}
  • } + {is_win &&
  • {translate('Create Desktop Shortcut')}
  • }
  • {translate('Unremember Password')}
  • {(!this.type || this.type == "fav") &&
  • {translate('Add to Favorites')}
  • } {(!this.type || this.type == "fav") &&
  • {translate('Remove from Favorites')}
  • } @@ -419,6 +420,8 @@ class SessionList: Reactor.Component { createNewConnect(id, "connect"); } else if (action == "transfer") { createNewConnect(id, "file-transfer"); + } else if (action == "wol") { + handler.send_wol(id); } else if (action == "remove") { if (this.type == "ab") { for (var i = 0; i < ab.peers.length; ++i) { From eda08555061a73e12b66246dd4428047794096ba Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 14 Jul 2022 03:35:00 +0800 Subject: [PATCH 25/50] lan_discovery_WOL: lan discovery test done Signed-off-by: fufesou --- src/rendezvous_mediator.rs | 8 +++++--- src/ui/ab.tis | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index f4f4fb15c..db087aa68 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -64,9 +64,11 @@ impl RendezvousMediator { direct_server(server_cloned).await; }); #[cfg(not(any(target_os = "android", target_os = "ios")))] - std::thread::spawn(move || { - allow_err!(lan_discovery()); - }); + if crate::platform::is_installed() { + std::thread::spawn(move || { + allow_err!(lan_discovery()); + }); + } loop { Config::reset_online(); if Config::get_option("stop-service").is_empty() { diff --git a/src/ui/ab.tis b/src/ui/ab.tis index 5b822ed22..28fa62352 100644 --- a/src/ui/ab.tis +++ b/src/ui/ab.tis @@ -532,7 +532,7 @@ class MultipleSessions: Reactor.Component {
    {translate('Recent Sessions')} {translate('Favorites')} - {{translate('Discovered')}} + {handler.is_installed() && {translate('Discovered')}} {translate('Address Book')}
    {!this.hidden && } @@ -540,7 +540,7 @@ class MultipleSessions: Reactor.Component {
    {!this.hidden && ((type == "fav" && ) || - (type == "lan" && ) || + (type == "lan" && handler.is_installed() && ) || (type == "ab" && ) || )}
    ; From 4955807dd081cb518da00be529973c2b80ab59c9 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 15 Jul 2022 11:35:23 +0800 Subject: [PATCH 26/50] lan_discovery_WOL: Update Cargo.lock Signed-off-by: fufesou --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aa4c87dd8..adc152440 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -581,9 +581,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.2.6" +version = "3.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6db9e867166a43a53f7199b5e4d1f522a1e5bd626654be263c999ce59df39a" +checksum = "759bf187376e1afa7b85b959e6a664a3e7a95203415dba952ad19139e798f902" dependencies = [ "heck 0.4.0", "proc-macro-error", @@ -5735,7 +5735,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7f97e69b28b256ccfb02472c25057132e234aa8368fea3bb0268def564ce1f2" dependencies = [ - "clap 3.2.6", + "clap 3.2.12", ] [[package]] From 13406cd359d3fd1c3902d4e5b9813d77c8fcf77b Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 15 Jul 2022 12:16:15 +0800 Subject: [PATCH 27/50] lan_discovery_WOL: sort by online status Signed-off-by: fufesou --- libs/hbb_common/src/config.rs | 2 -- rust-toolchain.toml | 2 ++ src/rendezvous_mediator.rs | 29 +++++++---------------------- 3 files changed, 9 insertions(+), 24 deletions(-) create mode 100644 rust-toolchain.toml diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 8d2437c30..d05831c03 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -875,8 +875,6 @@ impl DiscoveryPeer { #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct LanPeers { - // #[serde(default)] - // pub peers: String, pub peers: Vec, } diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 000000000..05dfa3270 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.62.0" diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index db087aa68..8da1937a7 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -750,31 +750,16 @@ async fn handle_received_peers(mut rx: UnboundedReceiver) loop { tokio::select! { data = rx.recv() => match data { - Some(peer) => { + Some(mut peer) => { let in_response_set = !response_set.insert(peer.id.clone()); - let mut pre_found = false; - // Try find and update peer - for peer1 in &mut peers { - if peer1.is_same_peer(&peer) { - if in_response_set { - // Merge ip_mac and update other infos - peer1.ip_mac.extend(peer.ip_mac.clone()); - peer1.hostname = peer.hostname.clone(); - peer1.platform = peer.platform.clone(); - peer1.online = true; - } else { - // Update all peer infos - *peer1 = peer.clone(); - } - pre_found = true; - break + if let Some(pos) = peers.iter().position(|x| x.is_same_peer(&peer) ) { + let peer1 = peers.remove(pos); + if in_response_set { + peer.ip_mac.extend(peer1.ip_mac); + peer.online = true; } } - // Push if not found - if !pre_found { - peers.push(peer); - } - + peers.insert(0, peer); if last_write_time.elapsed().as_millis() > 300 { config::LanPeers::store(&peers); last_write_time = Instant::now(); From 3613f27afc79c42ed4762a43670770f2079b438e Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 15 Jul 2022 20:39:42 +0800 Subject: [PATCH 28/50] lan_discovery_WOL: fix udp set_nonblocking twice and lan refactor Signed-off-by: fufesou --- libs/hbb_common/src/udp.rs | 1 - src/lan.rs | 291 +++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + src/rendezvous_mediator.rs | 279 +---------------------------------- src/ui.rs | 4 +- 5 files changed, 298 insertions(+), 279 deletions(-) create mode 100644 src/lan.rs diff --git a/libs/hbb_common/src/udp.rs b/libs/hbb_common/src/udp.rs index c2d5057f2..3532dd1e0 100644 --- a/libs/hbb_common/src/udp.rs +++ b/libs/hbb_common/src/udp.rs @@ -37,7 +37,6 @@ fn new_socket(addr: SocketAddr, reuse: bool, buf_size: usize) -> Result ResultType<()> { + let addr = SocketAddr::from(([0, 0, 0, 0], get_broadcast_port())); + let socket = std::net::UdpSocket::bind(addr)?; + socket.set_read_timeout(Some(std::time::Duration::from_millis(1000)))?; + log::info!("lan discovery listener started"); + loop { + let mut buf = [0; 2048]; + if let Ok((len, addr)) = socket.recv_from(&mut buf) { + if let Ok(msg_in) = Message::parse_from_bytes(&buf[0..len]) { + match msg_in.union { + Some(rendezvous_message::Union::PeerDiscovery(p)) => { + if p.cmd == "ping" { + if let Some(self_addr) = get_ipaddr_by_peer(&addr) { + let mut msg_out = Message::new(); + let peer = PeerDiscovery { + cmd: "pong".to_owned(), + mac: get_mac(&self_addr), + id: Config::get_id(), + hostname: whoami::hostname(), + username: crate::platform::get_active_username(), + platform: whoami::platform().to_string(), + ..Default::default() + }; + msg_out.set_peer_discovery(peer); + socket.send_to(&msg_out.write_to_bytes()?, addr).ok(); + } + } + } + _ => {} + } + } + } + } +} + +#[tokio::main(flavor = "current_thread")] +pub async fn discover() -> ResultType<()> { + let sockets = send_query()?; + let rx = spawn_wait_responses(sockets); + handle_received_peers(rx).await?; + + log::info!("discover ping done"); + Ok(()) +} + +pub fn send_wol(id: String) { + let interfaces = default_net::get_interfaces(); + for peer in &config::LanPeers::load().peers { + if peer.id == id { + for (ip, mac) in peer.ip_mac.iter() { + if let Ok(mac_addr) = mac.parse() { + if let Ok(IpAddr::V4(ip)) = ip.parse() { + for interface in &interfaces { + for ipv4 in &interface.ipv4 { + if (u32::from(ipv4.addr) & u32::from(ipv4.netmask)) + == (u32::from(ip) & u32::from(ipv4.netmask)) + { + allow_err!(wol::send_wol( + mac_addr, + None, + Some(IpAddr::V4(ipv4.addr)) + )); + } + } + } + } + } + } + break; + } + } +} + +#[inline] +fn get_broadcast_port() -> u16 { + (RENDEZVOUS_PORT + 3) as _ +} + +fn get_mac(ip: &IpAddr) -> String { + #[cfg(not(any(target_os = "android", target_os = "ios")))] + if let Ok(mac) = get_mac_by_ip(ip) { + mac.to_string() + } else { + "".to_owned() + } + #[cfg(any(target_os = "android", target_os = "ios"))] + "".to_owned() +} + +fn get_all_ipv4s() -> ResultType> { + let mut ipv4s = Vec::new(); + for interface in default_net::get_interfaces() { + for ipv4 in &interface.ipv4 { + ipv4s.push(ipv4.addr.clone()); + } + } + Ok(ipv4s) +} + +fn get_mac_by_ip(ip: &IpAddr) -> ResultType { + for interface in default_net::get_interfaces() { + match ip { + IpAddr::V4(local_ipv4) => { + if interface.ipv4.iter().any(|x| x.addr == *local_ipv4) { + if let Some(mac_addr) = interface.mac_addr { + return Ok(mac_addr.address()); + } + } + } + IpAddr::V6(local_ipv6) => { + if interface.ipv6.iter().any(|x| x.addr == *local_ipv6) { + if let Some(mac_addr) = interface.mac_addr { + return Ok(mac_addr.address()); + } + } + } + } + } + bail!("No interface found for ip: {:?}", ip); +} + +// Mainly from https://github.com/shellrow/default-net/blob/cf7ca24e7e6e8e566ed32346c9cfddab3f47e2d6/src/interface/shared.rs#L4 +fn get_ipaddr_by_peer(peer: A) -> Option { + let socket = match UdpSocket::bind("0.0.0.0:0") { + Ok(s) => s, + Err(_) => return None, + }; + + match socket.connect(peer) { + Ok(()) => (), + Err(_) => return None, + }; + + match socket.local_addr() { + Ok(addr) => return Some(addr.ip()), + Err(_) => return None, + }; +} + +fn create_broadcast_sockets() -> ResultType> { + let mut sockets = Vec::new(); + for v4_addr in get_all_ipv4s()? { + if v4_addr.is_private() { + let s = UdpSocket::bind(SocketAddr::from((v4_addr, 0)))?; + s.set_broadcast(true)?; + log::debug!("Bind socket to {}", &v4_addr); + sockets.push(s) + } + } + Ok(sockets) +} + +fn send_query() -> ResultType> { + let sockets = create_broadcast_sockets()?; + if sockets.is_empty() { + bail!("Found no ipv4 addresses"); + } + + let mut msg_out = Message::new(); + let peer = PeerDiscovery { + cmd: "ping".to_owned(), + ..Default::default() + }; + msg_out.set_peer_discovery(peer); + let maddr = SocketAddr::from(([255, 255, 255, 255], get_broadcast_port())); + for socket in &sockets { + socket.send_to(&msg_out.write_to_bytes()?, maddr)?; + } + log::info!("discover ping sent"); + Ok(sockets) +} + +fn wait_response( + socket: UdpSocket, + timeout: Option, + tx: UnboundedSender, +) -> ResultType<()> { + let mut last_recv_time = Instant::now(); + + socket.set_read_timeout(timeout)?; + loop { + let mut buf = [0; 2048]; + if let Ok((len, addr)) = socket.recv_from(&mut buf) { + if let Ok(msg_in) = Message::parse_from_bytes(&buf[0..len]) { + match msg_in.union { + Some(rendezvous_message::Union::PeerDiscovery(p)) => { + last_recv_time = Instant::now(); + if p.cmd == "pong" { + let mac = if let Some(self_addr) = get_ipaddr_by_peer(&addr) { + get_mac(&self_addr) + } else { + "".to_owned() + }; + + if mac != p.mac { + allow_err!(tx.send(config::DiscoveryPeer { + id: p.id.clone(), + ip_mac: HashMap::from([ + (addr.ip().to_string(), p.mac.clone(),) + ]), + username: p.username.clone(), + hostname: p.hostname.clone(), + platform: p.platform.clone(), + online: true, + })); + } + } + } + _ => {} + } + } + } + if last_recv_time.elapsed().as_millis() > 3_000 { + break; + } + } + Ok(()) +} + +fn spawn_wait_responses(sockets: Vec) -> UnboundedReceiver { + let (tx, rx) = unbounded_channel::<_>(); + for socket in sockets { + let tx_clone = tx.clone(); + std::thread::spawn(move || { + allow_err!(wait_response( + socket, + Some(std::time::Duration::from_millis(10)), + tx_clone + )); + }); + } + rx +} + +async fn handle_received_peers(mut rx: UnboundedReceiver) -> ResultType<()> { + let mut peers = config::LanPeers::load().peers; + peers.iter_mut().for_each(|peer| { + peer.online = false; + }); + + let mut response_set = HashSet::new(); + let mut last_write_time = Instant::now() - std::time::Duration::from_secs(4); + loop { + tokio::select! { + data = rx.recv() => match data { + Some(mut peer) => { + let in_response_set = !response_set.insert(peer.id.clone()); + if let Some(pos) = peers.iter().position(|x| x.is_same_peer(&peer) ) { + let peer1 = peers.remove(pos); + if in_response_set { + peer.ip_mac.extend(peer1.ip_mac); + peer.online = true; + } + } + peers.insert(0, peer); + if last_write_time.elapsed().as_millis() > 300 { + config::LanPeers::store(&peers); + last_write_time = Instant::now(); + } + } + None => { + break + } + } + } + } + + config::LanPeers::store(&peers); + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index 93cd67738..715a59b5c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,8 @@ mod client; #[cfg(not(any(target_os = "ios")))] mod rendezvous_mediator; #[cfg(not(any(target_os = "ios")))] +mod lan; +#[cfg(not(any(target_os = "ios")))] pub use self::rendezvous_mediator::*; /// cbindgen:ignore pub mod common; diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index 8da1937a7..6dc7a11c9 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -2,7 +2,7 @@ use crate::server::{check_zombie, new as new_server, ServerPtr}; use hbb_common::{ allow_err, anyhow::bail, - config::{self, Config, REG_INTERVAL, RENDEZVOUS_PORT, RENDEZVOUS_TIMEOUT}, + config::{Config, REG_INTERVAL, RENDEZVOUS_PORT, RENDEZVOUS_TIMEOUT}, futures::future::join_all, log, protobuf::Message as _, @@ -10,15 +10,13 @@ use hbb_common::{ sleep, socket_client, tokio::{ self, select, - sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, time::{interval, Duration}, }, udp::FramedSocket, AddrMangle, IntoTargetAddr, ResultType, TargetAddr, }; use std::{ - collections::{HashMap, HashSet}, - net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs, UdpSocket}, + net::SocketAddr, sync::{ atomic::{AtomicBool, Ordering}, Arc, Mutex, @@ -66,7 +64,7 @@ impl RendezvousMediator { #[cfg(not(any(target_os = "android", target_os = "ios")))] if crate::platform::is_installed() { std::thread::spawn(move || { - allow_err!(lan_discovery()); + allow_err!(super::lan::start_listening()); }); } loop { @@ -542,274 +540,3 @@ async fn direct_server(server: ServerPtr) { } } } - -#[inline] -pub fn get_broadcast_port() -> u16 { - (RENDEZVOUS_PORT + 3) as _ -} - -pub fn get_mac(ip: &IpAddr) -> String { - #[cfg(not(any(target_os = "android", target_os = "ios")))] - if let Ok(mac) = get_mac_by_ip(ip) { - mac.to_string() - } else { - "".to_owned() - } - #[cfg(any(target_os = "android", target_os = "ios"))] - "".to_owned() -} - -fn get_all_ipv4s() -> ResultType> { - let mut ipv4s = Vec::new(); - for interface in default_net::get_interfaces() { - for ipv4 in &interface.ipv4 { - ipv4s.push(ipv4.addr.clone()); - } - } - Ok(ipv4s) -} - -fn get_mac_by_ip(ip: &IpAddr) -> ResultType { - for interface in default_net::get_interfaces() { - match ip { - IpAddr::V4(local_ipv4) => { - if interface.ipv4.iter().any(|x| x.addr == *local_ipv4) { - if let Some(mac_addr) = interface.mac_addr { - return Ok(mac_addr.address()); - } - } - } - IpAddr::V6(local_ipv6) => { - if interface.ipv6.iter().any(|x| x.addr == *local_ipv6) { - if let Some(mac_addr) = interface.mac_addr { - return Ok(mac_addr.address()); - } - } - } - } - } - bail!("No interface found for ip: {:?}", ip); -} - -// Mainly from https://github.com/shellrow/default-net/blob/cf7ca24e7e6e8e566ed32346c9cfddab3f47e2d6/src/interface/shared.rs#L4 -pub fn get_ipaddr_by_peer(peer: A) -> Option { - let socket = match UdpSocket::bind("0.0.0.0:0") { - Ok(s) => s, - Err(_) => return None, - }; - - match socket.connect(peer) { - Ok(()) => (), - Err(_) => return None, - }; - - match socket.local_addr() { - Ok(addr) => return Some(addr.ip()), - Err(_) => return None, - }; -} - -fn lan_discovery() -> ResultType<()> { - let addr = SocketAddr::from(([0, 0, 0, 0], get_broadcast_port())); - let socket = std::net::UdpSocket::bind(addr)?; - socket.set_read_timeout(Some(std::time::Duration::from_millis(1000)))?; - log::info!("lan discovery listener started"); - loop { - let mut buf = [0; 2048]; - if let Ok((len, addr)) = socket.recv_from(&mut buf) { - if let Ok(msg_in) = Message::parse_from_bytes(&buf[0..len]) { - match msg_in.union { - Some(rendezvous_message::Union::PeerDiscovery(p)) => { - if p.cmd == "ping" { - if let Some(self_addr) = get_ipaddr_by_peer(&addr) { - let mut msg_out = Message::new(); - let peer = PeerDiscovery { - cmd: "pong".to_owned(), - mac: get_mac(&self_addr), - id: Config::get_id(), - hostname: whoami::hostname(), - username: crate::platform::get_active_username(), - platform: whoami::platform().to_string(), - ..Default::default() - }; - msg_out.set_peer_discovery(peer); - socket.send_to(&msg_out.write_to_bytes()?, addr).ok(); - } - } - } - _ => {} - } - } - } - } -} - -fn create_broadcast_sockets() -> ResultType> { - let mut sockets = Vec::new(); - for v4_addr in get_all_ipv4s()? { - if v4_addr.is_private() { - let s = UdpSocket::bind(SocketAddr::from((v4_addr, 0)))?; - s.set_broadcast(true)?; - log::debug!("Bind socket to {}", &v4_addr); - sockets.push(s) - } - } - Ok(sockets) -} - -fn send_query() -> ResultType> { - let sockets = create_broadcast_sockets()?; - if sockets.is_empty() { - bail!("Found no ipv4 addresses"); - } - - let mut msg_out = Message::new(); - let peer = PeerDiscovery { - cmd: "ping".to_owned(), - ..Default::default() - }; - msg_out.set_peer_discovery(peer); - let maddr = SocketAddr::from(([255, 255, 255, 255], get_broadcast_port())); - for socket in &sockets { - socket.send_to(&msg_out.write_to_bytes()?, maddr)?; - } - log::info!("discover ping sent"); - Ok(sockets) -} - -fn wait_response( - socket: UdpSocket, - timeout: Option, - tx: UnboundedSender, -) -> ResultType<()> { - let mut last_recv_time = Instant::now(); - - socket.set_read_timeout(timeout)?; - loop { - let mut buf = [0; 2048]; - if let Ok((len, addr)) = socket.recv_from(&mut buf) { - if let Ok(msg_in) = Message::parse_from_bytes(&buf[0..len]) { - match msg_in.union { - Some(rendezvous_message::Union::PeerDiscovery(p)) => { - last_recv_time = Instant::now(); - if p.cmd == "pong" { - let mac = if let Some(self_addr) = get_ipaddr_by_peer(&addr) { - get_mac(&self_addr) - } else { - "".to_owned() - }; - - if mac != p.mac { - allow_err!(tx.send(config::DiscoveryPeer { - id: p.id.clone(), - ip_mac: HashMap::from([ - (addr.ip().to_string(), p.mac.clone(),) - ]), - username: p.username.clone(), - hostname: p.hostname.clone(), - platform: p.platform.clone(), - online: true, - })); - } - } - } - _ => {} - } - } - } - if last_recv_time.elapsed().as_millis() > 3_000 { - break; - } - } - Ok(()) -} - -fn spawn_wait_responses(sockets: Vec) -> UnboundedReceiver { - let (tx, rx) = unbounded_channel::<_>(); - for socket in sockets { - let tx_clone = tx.clone(); - std::thread::spawn(move || { - allow_err!(wait_response( - socket, - Some(std::time::Duration::from_millis(10)), - tx_clone - )); - }); - } - rx -} - -async fn handle_received_peers(mut rx: UnboundedReceiver) -> ResultType<()> { - let mut peers = config::LanPeers::load().peers; - peers.iter_mut().for_each(|peer| { - peer.online = false; - }); - - let mut response_set = HashSet::new(); - let mut last_write_time = Instant::now() - std::time::Duration::from_secs(4); - loop { - tokio::select! { - data = rx.recv() => match data { - Some(mut peer) => { - let in_response_set = !response_set.insert(peer.id.clone()); - if let Some(pos) = peers.iter().position(|x| x.is_same_peer(&peer) ) { - let peer1 = peers.remove(pos); - if in_response_set { - peer.ip_mac.extend(peer1.ip_mac); - peer.online = true; - } - } - peers.insert(0, peer); - if last_write_time.elapsed().as_millis() > 300 { - config::LanPeers::store(&peers); - last_write_time = Instant::now(); - } - } - None => { - break - } - } - } - } - - config::LanPeers::store(&peers); - Ok(()) -} - -#[tokio::main(flavor = "current_thread")] -pub async fn discover() -> ResultType<()> { - let sockets = send_query()?; - let rx = spawn_wait_responses(sockets); - handle_received_peers(rx).await?; - - log::info!("discover ping done"); - Ok(()) -} - -pub fn send_wol(id: String) { - let interfaces = default_net::get_interfaces(); - for peer in &config::LanPeers::load().peers { - if peer.id == id { - for (ip, mac) in peer.ip_mac.iter() { - if let Ok(mac_addr) = mac.parse() { - if let Ok(IpAddr::V4(ip)) = ip.parse() { - for interface in &interfaces { - for ipv4 in &interface.ipv4 { - if (u32::from(ipv4.addr) & u32::from(ipv4.netmask)) - == (u32::from(ip) & u32::from(ipv4.netmask)) - { - allow_err!(wol::send_wol( - mac_addr, - None, - Some(IpAddr::V4(ipv4.addr)) - )); - } - } - } - } - } - } - break; - } - } -} diff --git a/src/ui.rs b/src/ui.rs index 084422f6b..2b7ffcc29 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -548,7 +548,7 @@ impl UI { } fn send_wol(&mut self, id: String) { - crate::rendezvous_mediator::send_wol(id) + crate::lan::send_wol(id) } fn new_remote(&mut self, id: String, remote_type: String) { @@ -695,7 +695,7 @@ impl UI { fn discover(&self) { std::thread::spawn(move || { - allow_err!(crate::rendezvous_mediator::discover()); + allow_err!(crate::lan::discover()); }); } From 88fef77980fcb27d44aabefd6da8fdef7ad1dfbe Mon Sep 17 00:00:00 2001 From: csf Date: Sat, 16 Jul 2022 22:31:44 +0800 Subject: [PATCH 29/50] android opt:add disable ignore_battery_optimizations --- .../com/carriez/flutter_hbb/MainActivity.kt | 1 - .../kotlin/com/carriez/flutter_hbb/common.kt | 12 ++ flutter/lib/common.dart | 7 +- flutter/lib/pages/settings_page.dart | 104 ++++++++++++------ src/lang/cn.rs | 1 + src/lang/en.rs | 1 + src/mobile_ffi.rs | 3 + 7 files changed, 93 insertions(+), 36 deletions(-) diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt index 3cc105bfa..fd340f7ed 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt @@ -192,7 +192,6 @@ class MainActivity : FlutterActivity() { override fun onResume() { super.onResume() val inputPer = InputService.isOpen - Log.d(logTag, "onResume inputPer:$inputPer") activity.runOnUiThread { flutterMethodChannel.invokeMethod( "on_state_changed", diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt index 3cb3ae581..4bf244a06 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt @@ -57,6 +57,18 @@ fun requestPermission(context: Context, type: String) { } return } + "application_details_settings" -> { + try { + context.startActivity(Intent().apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + action = "android.settings.APPLICATION_DETAILS_SETTINGS" + data = Uri.parse("package:" + context.packageName) + }) + } catch (e:Exception) { + e.printStackTrace() + } + return + } "audio" -> { Permission.RECORD_AUDIO } diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 1b36c548b..a7c5dfea0 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -260,7 +260,12 @@ class PermissionManager { static Timer? _timer; static var _current = ""; - static final permissions = ["audio", "file", "ignore_battery_optimizations"]; + static final permissions = [ + "audio", + "file", + "ignore_battery_optimizations", + "application_details_settings" + ]; static bool isWaitingFile() { if (_completer != null) { diff --git a/flutter/lib/pages/settings_page.dart b/flutter/lib/pages/settings_page.dart index 7506d849f..55a7c8719 100644 --- a/flutter/lib/pages/settings_page.dart +++ b/flutter/lib/pages/settings_page.dart @@ -26,23 +26,42 @@ class SettingsPage extends StatefulWidget implements PageShape { _SettingsState createState() => _SettingsState(); } -class _SettingsState extends State { +class _SettingsState extends State with WidgetsBindingObserver { static const url = 'https://rustdesk.com/'; - var _showIgnoreBattery = false; + final _hasIgnoreBattery = androidVersion >= 26; + var _ignoreBatteryOpt = false; @override void initState() { super.initState(); - if (androidVersion >= 26) { - () async { - final res = - await PermissionManager.check("ignore_battery_optimizations"); - if (_showIgnoreBattery != !res) { - setState(() { - _showIgnoreBattery = !res; - }); - } - }(); + WidgetsBinding.instance.addObserver(this); + if (_hasIgnoreBattery) { + updateIgnoreBatteryStatus(); + } + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (state == AppLifecycleState.resumed) { + updateIgnoreBatteryStatus(); + } + } + + Future updateIgnoreBatteryStatus() async { + final res = await PermissionManager.check("ignore_battery_optimizations"); + if (_ignoreBatteryOpt != res) { + setState(() { + _ignoreBatteryOpt = res; + }); + return true; + } else { + return false; } } @@ -53,7 +72,6 @@ class _SettingsState extends State { final enableAbr = FFI.getByName("option", "enable-abr") != 'N'; final enhancementsTiles = [ SettingsTile.switchTile( - leading: Icon(Icons.more_horiz), title: Text(translate('Adaptive Bitrate') + '(beta)'), initialValue: enableAbr, onToggle: (v) { @@ -68,32 +86,37 @@ class _SettingsState extends State { }, ) ]; - if (_showIgnoreBattery) { + if (_hasIgnoreBattery) { enhancementsTiles.insert( 0, - SettingsTile.navigation( + SettingsTile.switchTile( + initialValue: _ignoreBatteryOpt, title: Text(translate('Keep RustDesk background service')), description: Text('* ${translate('Ignore Battery Optimizations')}'), - leading: Icon(Icons.battery_saver), - onPressed: (context) { - PermissionManager.request("ignore_battery_optimizations"); - var count = 0; - Timer.periodic(Duration(seconds: 1), (timer) async { - if (count > 5) { - count = 0; - timer.cancel(); + onToggle: (v) async { + if (v) { + PermissionManager.request("ignore_battery_optimizations"); + } else { + final res = await DialogManager.show( + (setState, close) => CustomAlertDialog( + title: Text(translate("Open System Setting")), + content: Text(translate( + "android_open_battery_optimizations_tip")), + actions: [ + TextButton( + onPressed: () => close(), + child: Text(translate("Cancel"))), + ElevatedButton( + onPressed: () => close(true), + child: + Text(translate("Open System Setting"))), + ], + )); + if (res == true) { + PermissionManager.request("application_details_settings"); } - if (await PermissionManager.check( - "ignore_battery_optimizations")) { - count = 0; - timer.cancel(); - setState(() { - _showIgnoreBattery = false; - }); - } - count++; - }); + } })); } @@ -123,7 +146,13 @@ class _SettingsState extends State { leading: Icon(Icons.cloud), onPressed: (context) { showServerSettings(); - }) + }), + SettingsTile.navigation( + title: Text(translate('Language')), + leading: Icon(Icons.translate), + onPressed: (context) { + showLanguageSettings(); + }), ]), SettingsSection( title: Text(translate("Enhancements")), @@ -162,6 +191,13 @@ void showServerSettings() { showServerSettingsWithValue(id, relay, key, api); } +void showLanguageSettings() { + try { + final langs = json.decode(FFI.getByName('langs')) as Map; + debugPrint("langs:$langs"); + } catch (e) {} +} + void showAbout() { DialogManager.show((setState, close) { return CustomAlertDialog( diff --git a/src/lang/cn.rs b/src/lang/cn.rs index ba85d7f39..58a3a6cd7 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -286,5 +286,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Language", "语言"), ("Keep RustDesk background service", "保持RustDesk后台服务"), ("Ignore Battery Optimizations", "忽略电池优化"), + ("android_open_battery_optimizations_tip", "如需关闭此功能,请在接下来的RustDesk应用设置页面中,找到并进入 [电源] 页面,取消勾选 [不受限制]"), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index a4d11d415..1818a619a 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -27,5 +27,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"), ("doc_fix_wayland", "https://rustdesk.com/docs/en/manual/linux/#x11-required"), ("server_not_support", "Not yet supported by the server"), + ("android_open_battery_optimizations_tip", "If you want to disable this feature, please go to the next RustDesk application settings page, find and enter [Battery] ,Uncheck [Unrestricted]"), ].iter().cloned().collect(); } diff --git a/src/mobile_ffi.rs b/src/mobile_ffi.rs index 6a0b71a5e..a0f0490e2 100644 --- a/src/mobile_ffi.rs +++ b/src/mobile_ffi.rs @@ -130,6 +130,9 @@ unsafe extern "C" fn get_by_name(name: *const c_char, arg: *const c_char) -> *co .clone() .to_string(); } + "langs" => { + res = crate::lang::LANGS.to_string(); + } // File Action "get_home_dir" => { res = fs::get_home_as_string(); From 6cb3f3182cc6abcfe3b662e1d84df0cc59feb186 Mon Sep 17 00:00:00 2001 From: Daniel Jorge Csich <67213069+danielcshn@users.noreply.github.com> Date: Sun, 17 Jul 2022 00:43:35 -0300 Subject: [PATCH 30/50] minor README fixes --- README-DE.md | 2 +- README-EO.md | 2 +- README-FA.md | 2 +- README-FI.md | 2 +- README-FR.md | 2 +- README-ID.md | 2 +- README-IT.md | 2 +- README-ML.md | 2 +- README-NL.md | 2 +- README-PL.md | 2 +- README-PTBR.md | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README-DE.md b/README-DE.md index ab76ea478..0df001e3f 100644 --- a/README-DE.md +++ b/README-DE.md @@ -9,7 +9,7 @@ Wir brauchen deine Hilfe um diese README Datei zu verbessern und aktualisieren

    -Rede mit uns: [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk) +Rede mit uns: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) diff --git a/README-EO.md b/README-EO.md index f70f54ff6..b532aa4de 100644 --- a/README-EO.md +++ b/README-EO.md @@ -9,7 +9,7 @@ Ni bezonas helpon traduki tiun README kaj la interfacon al via denaska lingvo

    -Babili kun ni: [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk) +Babili kun ni: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) diff --git a/README-FA.md b/README-FA.md index 033709a80..0aac205a8 100644 --- a/README-FA.md +++ b/README-FA.md @@ -9,7 +9,7 @@ ‫برای ترجمه این RustDesk UI ،README و Doc به زبان مادری شما به کمکتون نیاز داریم

    -با ما گپ بزنید: [Reddit](https://www.reddit.com/r/rustdesk) | [Twitter](https://twitter.com/rustdesk) | [Discord](https://discord.gg/nDceKgxnkV) +با ما گپ بزنید: [Reddit](https://www.reddit.com/r/rustdesk) | [Twitter](https://twitter.com/rustdesk) | [Discord](https://discord.gg/nDceKgxnkV) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) diff --git a/README-FI.md b/README-FI.md index 27ccb75b6..ca846007f 100644 --- a/README-FI.md +++ b/README-FI.md @@ -9,7 +9,7 @@ Tarvitsemme apua tämän README-tiedoston kääntämiseksi äidinkielellesi

    -Juttele meidän kanssa: [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk) +Juttele meidän kanssa: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) diff --git a/README-FR.md b/README-FR.md index 1709c3274..5d421b97d 100644 --- a/README-FR.md +++ b/README-FR.md @@ -9,7 +9,7 @@ Nous avons besoin de votre aide pour traduire ce README dans votre langue maternelle.

    -Chattez avec nous : [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk) +Chattez avec nous : [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) diff --git a/README-ID.md b/README-ID.md index 632271cd4..6dc00f6fd 100644 --- a/README-ID.md +++ b/README-ID.md @@ -9,7 +9,7 @@ Kami membutuhkan bantuan Anda untuk menerjemahkan README ini dan RustDesk UI ke bahasa asli anda

    -Birbincang bersama kami: [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk) +Birbincang bersama kami: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) diff --git a/README-IT.md b/README-IT.md index 05491af31..6d3aaf5ee 100644 --- a/README-IT.md +++ b/README-IT.md @@ -9,7 +9,7 @@ Abbiamo bisogno del tuo aiuto per tradurre questo README e la RustDesk UI nella tua lingua nativa

    -Chatta con noi: [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk) +Chatta con noi: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) diff --git a/README-ML.md b/README-ML.md index fba4822b1..d72d14c02 100644 --- a/README-ML.md +++ b/README-ML.md @@ -9,7 +9,7 @@ ഈ README നിങ്ങളുടെ മാതൃഭാഷയിലേക്ക് വിവർത്തനം ചെയ്യാൻ ഞങ്ങൾക്ക് നിങ്ങളുടെ സഹായം ആവശ്യമാണ്

    -ഞങ്ങളുമായി ചാറ്റ് ചെയ്യുക: [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk) +ഞങ്ങളുമായി ചാറ്റ് ചെയ്യുക: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) diff --git a/README-NL.md b/README-NL.md index 11ecc622c..cce863b6d 100644 --- a/README-NL.md +++ b/README-NL.md @@ -9,7 +9,7 @@ We hebben je hulp nodig om deze README te vertalen naar jouw moedertaal

    -Praat met ons: [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk) +Praat met ons: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) diff --git a/README-PL.md b/README-PL.md index d525f65c1..d21461fee 100644 --- a/README-PL.md +++ b/README-PL.md @@ -9,7 +9,7 @@ Potrzebujemy twojej pomocy w tłumaczeniu README na twój ojczysty język

    -Porozmawiaj z nami na: [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk) +Porozmawiaj z nami na: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) diff --git a/README-PTBR.md b/README-PTBR.md index bb6a25cbf..11986df55 100644 --- a/README-PTBR.md +++ b/README-PTBR.md @@ -9,7 +9,7 @@ Precisamos de sua ajuda para traduzir este README e a UI do RustDesk para sua língua nativa

    -Converse conosco: [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk) +Converse conosco: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) From f587bdee5a227229e034749261e97096e69393a5 Mon Sep 17 00:00:00 2001 From: csf Date: Sun, 17 Jul 2022 16:47:19 +0800 Subject: [PATCH 31/50] android fix:protobuf 3.1.0 --- flutter/lib/pages/settings_page.dart | 15 +----- src/mobile.rs | 72 ++++++++++++++-------------- src/mobile_ffi.rs | 3 -- 3 files changed, 37 insertions(+), 53 deletions(-) diff --git a/flutter/lib/pages/settings_page.dart b/flutter/lib/pages/settings_page.dart index 55a7c8719..30eb88b7b 100644 --- a/flutter/lib/pages/settings_page.dart +++ b/flutter/lib/pages/settings_page.dart @@ -146,13 +146,7 @@ class _SettingsState extends State with WidgetsBindingObserver { leading: Icon(Icons.cloud), onPressed: (context) { showServerSettings(); - }), - SettingsTile.navigation( - title: Text(translate('Language')), - leading: Icon(Icons.translate), - onPressed: (context) { - showLanguageSettings(); - }), + }) ]), SettingsSection( title: Text(translate("Enhancements")), @@ -191,13 +185,6 @@ void showServerSettings() { showServerSettingsWithValue(id, relay, key, api); } -void showLanguageSettings() { - try { - final langs = json.decode(FFI.getByName('langs')) as Map; - debugPrint("langs:$langs"); - } catch (e) {} -} - void showAbout() { DialogManager.show((setState, close) { return CustomAlertDialog( diff --git a/src/mobile.rs b/src/mobile.rs index 942510b17..d3adaaa79 100644 --- a/src/mobile.rs +++ b/src/mobile.rs @@ -599,7 +599,7 @@ impl Connection { async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool { if let Ok(msg_in) = Message::parse_from_bytes(&data) { match msg_in.union { - Some(message::Union::video_frame(vf)) => { + Some(message::Union::VideoFrame(vf)) => { if !self.first_frame { self.first_frame = true; } @@ -610,21 +610,21 @@ impl Connection { s.add(ZeroCopyBuffer(self.video_handler.rgb.clone())); } } - Some(message::Union::hash(hash)) => { + Some(message::Union::Hash(hash)) => { self.session.handle_hash(hash, peer).await; } - Some(message::Union::login_response(lr)) => match lr.union { - Some(login_response::Union::error(err)) => { + Some(message::Union::LoginResponse(lr)) => match lr.union { + Some(login_response::Union::Error(err)) => { if !self.session.handle_login_error(&err) { return false; } } - Some(login_response::Union::peer_info(pi)) => { + Some(login_response::Union::PeerInfo(pi)) => { self.session.handle_peer_info(pi); } _ => {} }, - Some(message::Union::clipboard(cb)) => { + Some(message::Union::Clipboard(cb)) => { if !self.session.lc.read().unwrap().disable_clipboard { let content = if cb.compress { decompress(&cb.content) @@ -637,7 +637,7 @@ impl Connection { } } } - Some(message::Union::cursor_data(cd)) => { + Some(message::Union::CursorData(cd)) => { let colors = hbb_common::compress::decompress(&cd.colors); self.session.push_event( "cursor_data", @@ -654,18 +654,18 @@ impl Connection { ], ); } - Some(message::Union::cursor_id(id)) => { + Some(message::Union::CursorId(id)) => { self.session .push_event("cursor_id", vec![("id", &id.to_string())]); } - Some(message::Union::cursor_position(cp)) => { + Some(message::Union::CursorPosition(cp)) => { self.session.push_event( "cursor_position", vec![("x", &cp.x.to_string()), ("y", &cp.y.to_string())], ); } - Some(message::Union::file_response(fr)) => match fr.union { - Some(file_response::Union::dir(fd)) => { + Some(message::Union::FileResponse(fr)) => match fr.union { + Some(file_response::Union::Dir(fd)) => { let mut entries = fd.entries.to_vec(); if self.session.peer_platform() == "Windows" { fs::transform_windows_path(&mut entries); @@ -679,7 +679,7 @@ impl Connection { job.set_files(entries); } } - Some(file_response::Union::block(block)) => { + Some(file_response::Union::Block(block)) => { if let Some(job) = fs::get_job(block.id, &mut self.write_jobs) { if let Err(_err) = job.write(block, None).await { // to-do: add "skip" for writing job @@ -687,17 +687,17 @@ impl Connection { self.update_jobs_status(); } } - Some(file_response::Union::done(d)) => { + Some(file_response::Union::Done(d)) => { if let Some(job) = fs::get_job(d.id, &mut self.write_jobs) { job.modify_time(); fs::remove_job(d.id, &mut self.write_jobs); } self.handle_job_status(d.id, d.file_num, None); } - Some(file_response::Union::error(e)) => { + Some(file_response::Union::Error(e)) => { self.handle_job_status(e.id, e.file_num, Some(e.error)); } - Some(file_response::Union::digest(digest)) => { + Some(file_response::Union::Digest(digest)) => { if digest.is_upload { if let Some(job) = fs::get_job(digest.id, &mut self.read_jobs) { if let Some(file) = job.files().get(digest.file_num as usize) { @@ -708,9 +708,9 @@ impl Connection { id: digest.id, file_num: digest.file_num, union: Some(if overwrite { - file_transfer_send_confirm_request::Union::offset_blk(0) + file_transfer_send_confirm_request::Union::OffsetBlk(0) } else { - file_transfer_send_confirm_request::Union::skip( + file_transfer_send_confirm_request::Union::Skip( true, ) }), @@ -740,7 +740,7 @@ impl Connection { let msg= new_send_confirm(FileTransferSendConfirmRequest { id: digest.id, file_num: digest.file_num, - union: Some(file_transfer_send_confirm_request::Union::skip(true)), + union: Some(file_transfer_send_confirm_request::Union::Skip(true)), ..Default::default() }); self.session.send_msg(msg); @@ -752,9 +752,9 @@ impl Connection { id: digest.id, file_num: digest.file_num, union: Some(if overwrite { - file_transfer_send_confirm_request::Union::offset_blk(0) + file_transfer_send_confirm_request::Union::OffsetBlk(0) } else { - file_transfer_send_confirm_request::Union::skip(true) + file_transfer_send_confirm_request::Union::Skip(true) }), ..Default::default() }, @@ -774,7 +774,7 @@ impl Connection { FileTransferSendConfirmRequest { id: digest.id, file_num: digest.file_num, - union: Some(file_transfer_send_confirm_request::Union::offset_blk(0)), + union: Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)), ..Default::default() }, ); @@ -791,15 +791,15 @@ impl Connection { } _ => {} }, - Some(message::Union::misc(misc)) => match misc.union { - Some(misc::Union::audio_format(f)) => { + Some(message::Union::Misc(misc)) => match misc.union { + Some(misc::Union::AudioFormat(f)) => { self.audio_handler.handle_format(f); // } - Some(misc::Union::chat_message(c)) => { + Some(misc::Union::ChatMessage(c)) => { self.session .push_event("chat_client_mode", vec![("text", &c.text)]); } - Some(misc::Union::permission_info(p)) => { + Some(misc::Union::PermissionInfo(p)) => { log::info!("Change permission {:?} -> {}", p.permission, p.enabled); use permission_info::Permission; self.session.push_event( @@ -815,7 +815,7 @@ impl Connection { )], ); } - Some(misc::Union::switch_display(s)) => { + Some(misc::Union::SwitchDisplay(s)) => { self.video_handler.reset(); self.session.push_event( "switch_display", @@ -828,22 +828,22 @@ impl Connection { ], ); } - Some(misc::Union::close_reason(c)) => { + Some(misc::Union::CloseReason(c)) => { self.session.msgbox("error", "Connection Error", &c); return false; } _ => {} }, - Some(message::Union::test_delay(t)) => { + Some(message::Union::TestDelay(t)) => { self.session.handle_test_delay(t, peer).await; } - Some(message::Union::audio_frame(frame)) => { + Some(message::Union::AudioFrame(frame)) => { if !self.session.lc.read().unwrap().disable_audio { self.audio_handler.handle_frame(frame); } } - Some(message::Union::file_action(action)) => match action.union { - Some(file_action::Union::send_confirm(c)) => { + Some(message::Union::FileAction(action)) => match action.union { + Some(file_action::Union::SendConfirm(c)) => { if let Some(job) = fs::get_job(c.id, &mut self.read_jobs) { job.confirm(&c); } @@ -1029,9 +1029,9 @@ impl Connection { id, file_num, union: if need_override { - Some(file_transfer_send_confirm_request::Union::offset_blk(0)) + Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) } else { - Some(file_transfer_send_confirm_request::Union::skip(true)) + Some(file_transfer_send_confirm_request::Union::Skip(true)) }, ..Default::default() }); @@ -1047,9 +1047,9 @@ impl Connection { id, file_num, union: if need_override { - Some(file_transfer_send_confirm_request::Union::offset_blk(0)) + Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) } else { - Some(file_transfer_send_confirm_request::Union::skip(true)) + Some(file_transfer_send_confirm_request::Union::Skip(true)) }, ..Default::default() }); @@ -1451,7 +1451,7 @@ pub mod connection_manager { let mut req = FileTransferSendConfirmRequest { id, file_num, - union: Some(file_transfer_send_confirm_request::Union::offset_blk(0)), + union: Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)), ..Default::default() }; let digest = FileTransferDigest { diff --git a/src/mobile_ffi.rs b/src/mobile_ffi.rs index a0f0490e2..6a0b71a5e 100644 --- a/src/mobile_ffi.rs +++ b/src/mobile_ffi.rs @@ -130,9 +130,6 @@ unsafe extern "C" fn get_by_name(name: *const c_char, arg: *const c_char) -> *co .clone() .to_string(); } - "langs" => { - res = crate::lang::LANGS.to_string(); - } // File Action "get_home_dir" => { res = fs::get_home_as_string(); From 52f4f274b24ca9f0302482ff2201b1a230542ffb Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 17 Jul 2022 22:46:55 +0800 Subject: [PATCH 32/50] lan_discovery_WOL: remove unused struct MyConfig Signed-off-by: fufesou --- libs/hbb_common/src/config.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index d05831c03..340a725f8 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -878,12 +878,6 @@ pub struct LanPeers { pub peers: Vec, } -#[derive(Serialize, Deserialize)] -struct MyConfig { - version: u8, - api_key: String, -} - impl LanPeers { pub fn load() -> LanPeers { let _ = CONFIG.read().unwrap(); // for lock From 5c27f1c383c4330684e3d57f950037bbee748a4c Mon Sep 17 00:00:00 2001 From: csf Date: Mon, 18 Jul 2022 10:34:46 +0800 Subject: [PATCH 33/50] gen other langs --- src/lang/cs.rs | 1 + src/lang/da.rs | 1 + src/lang/de.rs | 1 + src/lang/eo.rs | 1 + src/lang/es.rs | 1 + src/lang/fr.rs | 1 + src/lang/hu.rs | 1 + src/lang/id.rs | 1 + src/lang/it.rs | 1 + src/lang/ptbr.rs | 1 + src/lang/ru.rs | 1 + src/lang/sk.rs | 1 + src/lang/template.rs | 1 + src/lang/tr.rs | 1 + src/lang/tw.rs | 1 + 15 files changed, 15 insertions(+) diff --git a/src/lang/cs.rs b/src/lang/cs.rs index e12bc6075..c5c432d46 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -286,5 +286,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Language", ""), ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index f8c676376..d13cd8550 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -286,5 +286,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Language", ""), ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index 8685e764d..b7f471992 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -286,5 +286,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Language", "Sprache"), ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 5952f51a1..4719a3be3 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -286,5 +286,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Language", ""), ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 9e12ed430..adf214b97 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -286,5 +286,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Language", ""), ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 5e223b262..e92069993 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -286,5 +286,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Language", "Langue"), ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index ab890766e..b0d1bfda0 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -286,5 +286,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Language", "Nyelv"), ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index e926ee4cf..4cdaf8b95 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -286,5 +286,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Language", ""), ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 016363d4e..3a0c2dc2a 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -286,5 +286,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Language", "Linguaggio"), ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index e36672e5c..e3cadd204 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -286,5 +286,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Language", ""), ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index d8f138223..7735e3db3 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -286,5 +286,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Language", "Язык"), ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 4f2a4a29f..ea8d22aa8 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -286,5 +286,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Language", ""), ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 8feac4d3b..9ea7146e1 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -286,5 +286,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Language", ""), ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 5bab8d8c9..1d30425dc 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -286,5 +286,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Language", ""), ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 8bc1392bb..30c8f5a76 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -286,5 +286,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Language", "語言"), ("Keep RustDesk background service", "保持RustDesk後台服務"), ("Ignore Battery Optimizations", "忽略電池優化"), + ("android_open_battery_optimizations_tip", "如需關閉此功能,請在接下來的RustDesk應用設置頁面中,找到並進入 [電源] 頁面,取消勾選 [不受限制]"), ].iter().cloned().collect(); } From b74a01a3b3180d0e921bd56b27411087cdbebf9e Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 18 Jul 2022 12:15:10 +0800 Subject: [PATCH 34/50] update lock --- Cargo.lock | 388 ++++++++++++----------------------------------------- Cargo.toml | 9 +- 2 files changed, 90 insertions(+), 307 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index adc152440..62d1c9d84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "ab_glyph_rasterizer" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13739d7177fbd22bb0ed28badfff9f372f8bef46c863db4e1c6248f6b223b6e" - [[package]] name = "addr2line" version = "0.17.0" @@ -80,19 +74,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "andrew" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c4afb09dd642feec8408e33f92f3ffc4052946f6b20f32fb99c1f58cd4fa7cf" -dependencies = [ - "bitflags", - "rusttype", - "walkdir", - "xdg", - "xml-rs", -] - [[package]] name = "android_log-sys" version = "0.2.0" @@ -421,12 +402,12 @@ dependencies = [ [[package]] name = "calloop" -version = "0.6.5" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b036167e76041694579972c28cf4877b4f92da222560ddb49008937b6a6727c" +checksum = "bf2eec61efe56aa1e813f5126959296933cf0700030e4314786c48779a66ab82" dependencies = [ "log", - "nix 0.18.0", + "nix 0.22.3", ] [[package]] @@ -544,7 +525,7 @@ checksum = "5a050e2153c5be08febd6734e29298e844fdb0fa21aeddd63b4eb7baa106c69b" dependencies = [ "glob", "libc", - "libloading 0.7.3", + "libloading", ] [[package]] @@ -615,9 +596,9 @@ dependencies = [ [[package]] name = "clipboard-win" -version = "4.4.1" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f3e1238132dc01f081e1cbb9dace14e5ef4c3a51ee244bd982275fb514605db" +checksum = "c4ab1b92798304eedc095b53942963240037c0516452cb11aeba709d420b2219" dependencies = [ "error-code", "str-buf", @@ -706,9 +687,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "1.2.2" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" +checksum = "83827793632c72fa4f73c2edb31e7a997527dd8ffe7077344621fc62c5478157" dependencies = [ "cache-padded", ] @@ -886,20 +867,6 @@ dependencies = [ "cfg-if 1.0.0", ] -[[package]] -name = "crossbeam" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - [[package]] name = "crossbeam-channel" version = "0.5.5" @@ -957,9 +924,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ccfd8c0ee4cce11e45b3fd6f9d5e69e0cc62912aa6a0cb1bf4617b0eba5a12f" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", @@ -981,7 +948,7 @@ version = "3.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b37feaa84e6861e00a1f5e5aa8da3ee56d605c9992d33e082786754828e20865" dependencies = [ - "nix 0.24.1", + "nix 0.24.2", "winapi 0.3.9", ] @@ -991,38 +958,14 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" -[[package]] -name = "darling" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" -dependencies = [ - "darling_core 0.10.2", - "darling_macro 0.10.2", -] - [[package]] name = "darling" version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" dependencies = [ - "darling_core 0.13.4", - "darling_macro 0.13.4", -] - -[[package]] -name = "darling_core" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.9.3", - "syn", + "darling_core", + "darling_macro", ] [[package]] @@ -1039,24 +982,13 @@ dependencies = [ "syn", ] -[[package]] -name = "darling_macro" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" -dependencies = [ - "darling_core 0.10.2", - "quote", - "syn", -] - [[package]] name = "darling_macro" version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ - "darling_core 0.13.4", + "darling_core", "quote", "syn", ] @@ -1233,15 +1165,6 @@ dependencies = [ "dirs-sys-next", ] -[[package]] -name = "dirs" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" -dependencies = [ - "dirs-sys", -] - [[package]] name = "dirs-next" version = "2.0.0" @@ -1252,17 +1175,6 @@ dependencies = [ "dirs-sys-next", ] -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi 0.3.9", -] - [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -1280,22 +1192,13 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" -[[package]] -name = "dlib" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b11f15d1e3268f140f68d390637d5e76d849782d971ae7063e0da69fe9709a76" -dependencies = [ - "libloading 0.6.7", -] - [[package]] name = "dlib" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" dependencies = [ - "libloading 0.7.3", + "libloading", ] [[package]] @@ -1538,9 +1441,9 @@ dependencies = [ [[package]] name = "flutter_rust_bridge_macros" -version = "1.37.2" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b78a1a69afcf28951d0801e9be838b236734c8827a0eadbb71fb651aa2e9fed" +checksum = "13652b9b71bc3bf4ea3bbb5cadc9bc2350fe0fba5145f6a949309fc452576d6d" [[package]] name = "fnv" @@ -1800,9 +1703,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" [[package]] name = "gio" @@ -2161,9 +2064,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "607c8a29735385251a339424dd462993c0fed8fa09d378f259377df08c126022" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ "ahash", ] @@ -2276,7 +2179,7 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hwcodec" version = "0.1.0" -source = "git+https://github.com/21pages/hwcodec#adb15a681d69955048f85f5684525378289ab780" +source = "git+https://github.com/21pages/hwcodec#91d1cd327c88490f917457072aeef0676ddb2be7" dependencies = [ "bindgen", "cc", @@ -2392,6 +2295,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", ] [[package]] @@ -2516,7 +2422,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1b3b6681973cea8cc3bce7391e6d7d5502720b80a581c9a95c9cbaf592826aa" dependencies = [ "gtk-sys", - "libloading 0.7.3", + "libloading", "once_cell", ] @@ -2535,16 +2441,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "libloading" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883" -dependencies = [ - "cfg-if 1.0.0", - "winapi 0.3.9", -] - [[package]] name = "libloading" version = "0.7.3" @@ -2715,9 +2611,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memmap2" -version = "0.1.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b70ca2a6103ac8b665dc150b142ef0e4e89df640c9e6cf295d189c3caebe5a" +checksum = "00b6c2ebff6180198788f5db08d7ce3bc1d0b617176678831a7510825973e357" dependencies = [ "libc", ] @@ -2790,19 +2686,6 @@ dependencies = [ "winapi 0.2.8", ] -[[package]] -name = "mio" -version = "0.7.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" -dependencies = [ - "libc", - "log", - "miow 0.3.7", - "ntapi", - "winapi 0.3.9", -] - [[package]] name = "mio" version = "0.8.4" @@ -2815,18 +2698,6 @@ dependencies = [ "windows-sys 0.36.1", ] -[[package]] -name = "mio-misc" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b47412f3a52115b936ff2a229b803498c7b4d332adeb87c2f1498c9da54c398c" -dependencies = [ - "crossbeam", - "crossbeam-queue", - "log", - "mio 0.7.14", -] - [[package]] name = "mio-named-pipes" version = "0.1.7" @@ -2877,10 +2748,11 @@ checksum = "0419348c027fa7be448d2ae7ea0e4e04c2334c31dc4e74ab29f00a2a7ca69204" [[package]] name = "ndk" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8794322172319b972f528bf90c6b467be0079f1fa82780ffb431088e741a73ab" +checksum = "96d868f654c72e75f8687572699cdabe755f03effbb62542768e995d5b8d699d" dependencies = [ + "bitflags", "jni-sys", "ndk-sys 0.2.2", "num_enum", @@ -2908,15 +2780,16 @@ checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" [[package]] name = "ndk-glue" -version = "0.3.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5caf0c24d51ac1c905c27d4eda4fa0635bbe0de596b8f79235e0b17a4d29385" +checksum = "c71bee8ea72d685477e28bd004cfe1bf99c754d688cd78cad139eae4089484d4" dependencies = [ "lazy_static", "libc", "log", - "ndk 0.3.0", - "ndk-macro 0.2.0", + "ndk 0.5.0", + "ndk-context", + "ndk-macro", "ndk-sys 0.2.2", ] @@ -2931,30 +2804,17 @@ dependencies = [ "log", "ndk 0.6.0", "ndk-context", - "ndk-macro 0.3.0", + "ndk-macro", "ndk-sys 0.3.0", ] -[[package]] -name = "ndk-macro" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d1c6307dc424d0f65b9b06e94f88248e6305726b14729fd67a5e47b2dc481d" -dependencies = [ - "darling 0.10.2", - "proc-macro-crate 0.1.5", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "ndk-macro" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" dependencies = [ - "darling 0.13.4", + "darling", "proc-macro-crate 1.1.3", "proc-macro2", "quote", @@ -2987,30 +2847,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "nix" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055" -dependencies = [ - "bitflags", - "cc", - "cfg-if 0.1.10", - "libc", -] - -[[package]] -name = "nix" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" -dependencies = [ - "bitflags", - "cc", - "cfg-if 1.0.0", - "libc", -] - [[package]] name = "nix" version = "0.22.3" @@ -3039,9 +2875,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f17df307904acd05aa8e32e97bb20f2a0df1728bbc2d771ae8f9a90463441e9" +checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" dependencies = [ "bitflags", "cfg-if 1.0.0", @@ -3252,18 +3088,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "os_str_bytes" -version = "6.1.0" +version = "6.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" - -[[package]] -name = "owned_ttf_parser" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f923fb806c46266c02ab4a5b239735c144bdeda724a50ed058e5226f594cde3" -dependencies = [ - "ttf-parser", -] +checksum = "648001efe5d5c0102d8cea768e348da85d90af8ba91f0bea908f157951493cd4" [[package]] name = "padlock" @@ -3835,16 +3662,6 @@ dependencies = [ "rand_core 0.3.1", ] -[[package]] -name = "raw-window-handle" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28f55143d0548dad60bb4fbdc835a3d7ac6acc3324506450c5fdd6e42903a76" -dependencies = [ - "libc", - "raw-window-handle 0.4.3", -] - [[package]] name = "raw-window-handle" version = "0.4.3" @@ -4034,13 +3851,11 @@ dependencies = [ [[package]] name = "rpassword" -version = "6.0.1" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf099a1888612545b683d2661a1940089f6c2e5a8e38979b2159da876bfd956" +checksum = "26b763cb66df1c928432cc35053f8bd4cec3335d8559fc16010017d16b3c1680" dependencies = [ "libc", - "serde 1.0.139", - "serde_json 1.0.82", "winapi 0.3.9", ] @@ -4137,7 +3952,7 @@ dependencies = [ "rdev", "repng", "reqwest", - "rpassword 6.0.1", + "rpassword 7.0.0", "rubato", "runas", "rust-pulsectl", @@ -4220,21 +4035,11 @@ dependencies = [ "base64", ] -[[package]] -name = "rusttype" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc7c727aded0be18c5b80c1640eae0ac8e396abf6fa8477d96cb37d18ee5ec59" -dependencies = [ - "ab_glyph_rasterizer", - "owned_ttf_parser", -] - [[package]] name = "rustversion" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0a5f7c728f5d284929a1cccb5bc19884422bfe6ef4d6c409da2c41838983fcf" +checksum = "24c8ad4f0c00e1eb5bc7614d236a7f1300e3dbd76b68cac8e06fb00b015ad8d8" [[package]] name = "ryu" @@ -4460,7 +4265,7 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" dependencies = [ - "darling 0.13.4", + "darling", "proc-macro2", "quote", "syn", @@ -4468,9 +4273,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec0091e1f5aa338283ce049bd9dfefd55e1f168ac233e85c1ffe0038fb48cbe" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" dependencies = [ "indexmap", "ryu", @@ -4551,18 +4356,18 @@ checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" [[package]] name = "smithay-client-toolkit" -version = "0.12.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4750c76fd5d3ac95fa3ed80fe667d6a3d8590a960e5b575b98eea93339a80b80" +checksum = "8a28f16a97fa0e8ce563b2774d1e732dd5d4025d2772c5dba0a41a0f90a29da3" dependencies = [ - "andrew", "bitflags", "calloop", - "dlib 0.4.2", + "dlib", "lazy_static", "log", "memmap2", - "nix 0.18.0", + "nix 0.22.3", + "pkg-config", "wayland-client", "wayland-cursor", "wayland-protocols", @@ -4631,12 +4436,6 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -[[package]] -name = "strsim" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" - [[package]] name = "strsim" version = "0.10.0" @@ -4725,9 +4524,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.23.13" +version = "0.24.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3977ec2e0520829be45c8a2df70db2bf364714d8a748316a10c3c35d4d2b01c9" +checksum = "0b6e19da72a8d75be4d40e4dd4686afca31507f26c3ffdf6bd3073278d9de0a0" dependencies = [ "cfg-if 1.0.0", "core-foundation-sys 0.8.3", @@ -5072,9 +4871,8 @@ dependencies = [ [[package]] name = "trayicon" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c367fd7cdcdf19234aa104f7e03abe1be526018e4282af9f275bf436b9c9ad23" +version = "0.1.3-1" +source = "git+https://github.com/open-trade/trayicon-rs#8d9c4489287752cc5be4a35c103198f7111112f9" dependencies = [ "winapi 0.3.9", "winit", @@ -5086,12 +4884,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" -[[package]] -name = "ttf-parser" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc" - [[package]] name = "typenum" version = "1.15.0" @@ -5112,9 +4904,9 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-ident" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" [[package]] name = "unicode-normalization" @@ -5307,14 +5099,14 @@ checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" [[package]] name = "wayland-client" -version = "0.28.6" +version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ab332350e502f159382201394a78e3cc12d0f04db863429260164ea40e0355" +checksum = "91223460e73257f697d9e23d401279123d36039a3f7a449e983f123292d4458f" dependencies = [ "bitflags", "downcast-rs", "libc", - "nix 0.20.0", + "nix 0.22.3", "scoped-tls", "wayland-commons", "wayland-scanner", @@ -5323,11 +5115,11 @@ dependencies = [ [[package]] name = "wayland-commons" -version = "0.28.6" +version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21817947c7011bbd0a27e11b17b337bfd022e8544b071a2641232047966fbda" +checksum = "94f6e5e340d7c13490eca867898c4cec5af56c27a5ffe5c80c6fc4708e22d33e" dependencies = [ - "nix 0.20.0", + "nix 0.22.3", "once_cell", "smallvec", "wayland-sys", @@ -5335,20 +5127,20 @@ dependencies = [ [[package]] name = "wayland-cursor" -version = "0.28.6" +version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be610084edd1586d45e7bdd275fe345c7c1873598caa464c4fb835dee70fa65a" +checksum = "c52758f13d5e7861fc83d942d3d99bf270c83269575e52ac29e5b73cb956a6bd" dependencies = [ - "nix 0.20.0", + "nix 0.22.3", "wayland-client", "xcursor", ] [[package]] name = "wayland-protocols" -version = "0.28.6" +version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "286620ea4d803bacf61fa087a4242ee316693099ee5a140796aaba02b29f861f" +checksum = "60147ae23303402e41fe034f74fb2c35ad0780ee88a1c40ac09a3be1e7465741" dependencies = [ "bitflags", "wayland-client", @@ -5358,9 +5150,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.28.6" +version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce923eb2deb61de332d1f356ec7b6bf37094dc5573952e1c8936db03b54c03f1" +checksum = "39a1ed3143f7a143187156a2ab52742e89dac33245ba505c17224df48939f9e0" dependencies = [ "proc-macro2", "quote", @@ -5369,11 +5161,11 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.28.6" +version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d841fca9aed7febf9bed2e9796c49bf58d4152ceda8ac949ebe00868d8f0feb8" +checksum = "d9341df79a8975679188e37dab3889bfa57c44ac2cb6da166f519a81cbe452d4" dependencies = [ - "dlib 0.5.0", + "dlib", "lazy_static", "pkg-config", ] @@ -5672,9 +5464,9 @@ checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" [[package]] name = "winit" -version = "0.25.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79610794594d5e86be473ef7763f604f2159cbac8c94debd00df8fb41e86c2f8" +checksum = "9b43cc931d58b99461188607efd7acb2a093e65fc621f54cad78517a6063e73a" dependencies = [ "bitflags", "cocoa 0.24.0", @@ -5686,18 +5478,19 @@ dependencies = [ "lazy_static", "libc", "log", - "mio 0.7.14", - "mio-misc", - "ndk 0.3.0", - "ndk-glue 0.3.0", + "mio 0.8.4", + "ndk 0.5.0", + "ndk-glue 0.5.2", "ndk-sys 0.2.2", "objc", "parking_lot 0.11.2", "percent-encoding", - "raw-window-handle 0.3.4", - "scopeguard", + "raw-window-handle", "smithay-client-toolkit", + "wasm-bindgen", "wayland-client", + "wayland-protocols", + "web-sys", "winapi 0.3.9", "x11-dl", ] @@ -5790,15 +5583,6 @@ dependencies = [ "nom", ] -[[package]] -name = "xdg" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4583db5cbd4c4c0303df2d15af80f0539db703fa1c68802d4cbbd2dd0f88f6" -dependencies = [ - "dirs", -] - [[package]] name = "xml-rs" version = "0.8.4" diff --git a/Cargo.toml b/Cargo.toml index c008f7828..87fc0f169 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,9 +51,9 @@ samplerate = { version = "0.2", optional = true } async-trait = "0.1" uuid = { version = "1.0", features = ["v4"] } clap = "3.0" -rpassword = "6.0" +rpassword = "7.0" base64 = "0.13" -sysinfo = "0.23" +sysinfo = "0.24" num_cpus = "1.13" default-net = "0.11.0" wol-rs = "0.9.1" @@ -78,9 +78,8 @@ arboard = "2.0" [target.'cfg(target_os = "windows")'.dependencies] #systray = { git = "https://github.com/open-trade/systray-rs" } -trayicon = { version = "0.1", features = ["winit"] } -# > 0.25 not work with trayicon -winit = "0.25" +trayicon = { git = "https://github.com/open-trade/trayicon-rs", features = ["winit"] } +winit = "0.26" winapi = { version = "0.3", features = ["winuser"] } winreg = "0.10" windows-service = "0.4" From 2851d71290799034b3c853aa20f21308ba297c31 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 18 Jul 2022 13:33:12 +0800 Subject: [PATCH 35/50] fix mac compile --- src/ui/macos.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ui/macos.rs b/src/ui/macos.rs index bb11e2750..63387fecb 100644 --- a/src/ui/macos.rs +++ b/src/ui/macos.rs @@ -258,10 +258,10 @@ pub fn check_main_window() { let app = format!("/Applications/{}.app", crate::get_app_name()); let my_uid = sys .process((std::process::id() as i32).into()) - .map(|x| x.uid) + .map(|x| x.user_id()) .unwrap_or_default(); for (_, p) in sys.processes().iter() { - if p.cmd().len() == 1 && p.uid == my_uid && p.cmd()[0].contains(&app) { + if p.cmd().len() == 1 && p.user_id() == my_uid && p.cmd()[0].contains(&app) { return; } } From e46019a17182f81691efa3e535e60b3211f73537 Mon Sep 17 00:00:00 2001 From: 21pages Date: Mon, 20 Jun 2022 10:41:46 +0800 Subject: [PATCH 36/50] password: safe/random personal password Signed-off-by: 21pages --- Cargo.lock | 1 + libs/hbb_common/Cargo.toml | 1 + libs/hbb_common/src/config.rs | 30 ++- libs/hbb_common/src/lib.rs | 12 +- libs/hbb_common/src/password_security.rs | 330 +++++++++++++++++++++++ src/common.rs | 8 - src/ipc.rs | 207 ++++++++++++-- src/lang/cn.rs | 13 + src/lang/cs.rs | 13 + src/lang/da.rs | 13 + src/lang/de.rs | 13 + src/lang/eo.rs | 13 + src/lang/es.rs | 13 + src/lang/fr.rs | 13 + src/lang/hu.rs | 13 + src/lang/id.rs | 13 + src/lang/it.rs | 13 + src/lang/ptbr.rs | 13 + src/lang/ru.rs | 13 + src/lang/sk.rs | 13 + src/lang/template.rs | 13 + src/lang/tr.rs | 13 + src/lang/tw.rs | 13 + src/main.rs | 2 +- src/rendezvous_mediator.rs | 2 +- src/server/connection.rs | 46 +++- src/ui.rs | 99 ++++++- src/ui/common.css | 2 +- src/ui/common.tis | 2 +- src/ui/index.css | 15 ++ src/ui/index.tis | 189 ++++++++++--- 31 files changed, 1060 insertions(+), 94 deletions(-) create mode 100644 libs/hbb_common/src/password_security.rs diff --git a/Cargo.lock b/Cargo.lock index 62d1c9d84..bbd8edfba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2087,6 +2087,7 @@ dependencies = [ "lazy_static", "log", "mac_address", + "machine-uid", "protobuf", "protobuf-codegen", "quinn", diff --git a/libs/hbb_common/Cargo.toml b/libs/hbb_common/Cargo.toml index 6fec67193..b8db8d508 100644 --- a/libs/hbb_common/Cargo.toml +++ b/libs/hbb_common/Cargo.toml @@ -34,6 +34,7 @@ tokio-socks = { git = "https://github.com/open-trade/tokio-socks" } [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] mac_address = "1.1" +machine-uid = "0.2" [features] quic = [] diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 340a725f8..d8502151f 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -1,4 +1,8 @@ -use crate::log; +use crate::{ + log, + password_security::config::{decrypt_str_or_original, encrypt_str_or_original}, +}; +use anyhow::Result; use directories_next::ProjectDirs; use rand::Rng; use serde_derive::{Deserialize, Serialize}; @@ -17,6 +21,7 @@ pub const CONNECT_TIMEOUT: u64 = 18_000; pub const REG_INTERVAL: i64 = 12_000; pub const COMPRESS_LEVEL: i32 = 3; const SERIAL: i32 = 3; +const PASSWORD_ENC_VERSION: &'static str = "00"; // 128x128 #[cfg(target_os = "macos")] // 128x128 on 160x160 canvas, then shrink to 128, mac looks better with padding pub const ICON: &str = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAyVBMVEUAAAAAcf8Acf8Acf8Acv8Acf8Acf8Acf8Acf8AcP8Acf8Ab/8AcP8Acf////8AaP/z+f/o8v/k7v/5/v/T5f8AYP/u9v/X6f+hx/+Kuv95pP8Aef/B1/+TwP9xoP8BdP/g6P+Irv9ZmP8Bgf/E3f98q/9sn/+01f+Es/9nm/9Jif8hhv8off/M4P+syP+avP86iP/c7f+xy/9yqf9Om/9hk/9Rjv+60P99tv9fpf88lv8yjf8Tgf8deP+kvP8BiP8NeP8hkP80gP8oj2VLAAAADXRSTlMA7o7qLvnaxZ1FOxYPjH9HWgAABHJJREFUeNrtm+tW4jAQgBfwuu7MtIUWsOUiCCioIIgLiqvr+z/UHq/LJKVkmwTcc/r9E2nzlU4mSTP9lpGRkZGR8VX5cZjfL+yCEXYL+/nDH//U/Pd8DgyTy39Xbv7oIAcWyB0cqbW/sweW2NtRaj8H1sgpGOwUIAH7Bkd7YJW9dXFwAJY5WNP/cmCZQnJvzIN18on5LwfWySXlxEPYAIcad8D6PdiHDbCfIFCADVBIENiFDbCbIACKPPXrZ+cP8E6/0znvP4EymgIEravIRcTxu8HxNSJ60a8W0AYECKrlAN+YwAthCd9wm1Ug6wKzIn5SgRduXfwkqDasCjx0XFzi9PV6zwNcIuhcWBOg+ikySq8C9UD4dEKWBCoOcspvAuLHTo9sCDQiFPHotRM48j8G5gVur1FdAN2uaYEuiz7xFsgEJ2RUoMUakXuBTHHoGxQYOBhHjeUBAefEnMAowFhaLBOKuOemBBbxLRQrH2PBCgMvNCPQGMeevTb9zLrPxz2Mo+QbEaijzPUcOOHMQZkKGRAIPem39+bypREMPTkQW/oCfk866zAkiIFG4yIKRE/aAnfiSd0WrORY6pFdXQEqi9mvAQm0RIOSnoCcZ8vJoz3diCnjRk+g8VP4/fuQDJ2Lxr6WwG0gXs9aTpDzW0vgDBlVUpixR8gYk44AD8FrUKHr8JQJGgIDnoDqoALxmWPQSi9AVVzm8gKUuEPGr/QCvptwJkbSYT/TC4S8C96DGjTj86aHtAI0x2WaBIq0eSYYpRa4EsdWVVwWu9O0Aj6f6dyBMnwEraeOgSYu0wZlauzA47QCbT7DgAQSE+hZWoEBF/BBmWOewNMK3BsSqKUW4MGcWqCSVmDkbvkXGKQOwg6PAUO9oL3xXhA20yaiCjuwYygRVQlUOTWTCf2SuNJTxeFjgaHByGuAIvd8ItdPLTDhS7IuqEE1YSKVOgbayLhSFQhMzYh8hwfBs1r7c505YVIQYEdNoKwxK06MJiyrpUFHiF0NAfCQUVHoiRclIXJIR6C2fqG37pBHvcWpgwzvAtYwkR5UGV2e42UISdBJETl3mg8ouo54Rcnti1/vaT+iuUQBt500Cgo4U10BeHSkk57FB0JjWkKRMWgLUA0lLodtImAQdaMiiri3+gIAPZQoutHNsgKF1aaDMhMyIdBf8Th+Bh8MTjGWCpl5Wv43tDmnF+IUVMrcZgRoiAxhtrloYizNkZaAnF5leglbNhj0wYCAbCDvGb0mP4nib7O7ZlcYQ2m1gPtIZgVgGNNMeaVAaWR+57TrqgtUnm3sHQ+kYeE6fufUubG1ez50FXbPnWgBlgSABmN3TTcsRl2yWkHRrwbiunvk/W2+Mg1hPZplPDeXRbZzStFH15s1QIVd3UImP5z/bHpeeQLvRJ7XLFUffQIlCvqlXETQbgN9/rlYABGosv+Vi9m2Xs639YLGrZd0br+odetlvdsvbN56abfd4vbCzv9Q3v/ygoOV21A4OPpfXvH4Ai+5ZGRkZGRkbJA/t/I0QMzoMiEAAAAASUVORK5CYII= @@ -267,11 +272,19 @@ impl Config { } fn load() -> Config { - Config::load_::("") + let mut config = Config::load_::(""); + let (password, store) = decrypt_str_or_original(&config.password, PASSWORD_ENC_VERSION); + config.password = password; + if store { + config.store(); + } + config } fn store(&self) { - Config::store_(self, ""); + let mut config = self.clone(); + config.password = encrypt_str_or_original(&config.password, PASSWORD_ENC_VERSION); + Config::store_(&config, ""); } pub fn file() -> PathBuf { @@ -627,7 +640,7 @@ impl Config { log::info!("id updated from {} to {}", id, new_id); } - pub fn set_password(password: &str) { + pub fn set_security_password(password: &str) { let mut config = CONFIG.write().unwrap(); if password == config.password { return; @@ -636,13 +649,8 @@ impl Config { config.store(); } - pub fn get_password() -> String { - let mut password = CONFIG.read().unwrap().password.clone(); - if password.is_empty() { - password = Config::get_auto_password(); - Config::set_password(&password); - } - password + pub fn get_security_password() -> String { + CONFIG.read().unwrap().password.clone() } pub fn set_salt(salt: &str) { diff --git a/libs/hbb_common/src/lib.rs b/libs/hbb_common/src/lib.rs index 07c6a6868..fdd32c4c7 100644 --- a/libs/hbb_common/src/lib.rs +++ b/libs/hbb_common/src/lib.rs @@ -3,6 +3,7 @@ pub mod protos; pub use protos::message as message_proto; pub use protos::rendezvous as rendezvous_proto; pub use bytes; +use config::Config; pub use futures; pub use protobuf; use std::{ @@ -26,6 +27,7 @@ pub use anyhow::{self, bail}; pub use futures_util; pub mod config; pub mod fs; +pub use lazy_static; #[cfg(not(any(target_os = "android", target_os = "ios")))] pub use mac_address; pub use rand; @@ -34,7 +36,7 @@ pub use sodiumoxide; pub use tokio_socks; pub use tokio_socks::IntoTargetAddr; pub use tokio_socks::TargetAddr; -pub use lazy_static; +pub mod password_security; #[cfg(feature = "quic")] pub type Stream = quic::Connection; @@ -199,6 +201,14 @@ pub fn get_modified_time(path: &std::path::Path) -> SystemTime { .unwrap_or(UNIX_EPOCH) } +pub fn get_uuid() -> Vec { + #[cfg(not(any(target_os = "android", target_os = "ios")))] + if let Ok(id) = machine_uid::get() { + return id.into(); + } + Config::get_key_pair().1 +} + #[cfg(test)] mod tests { use super::*; diff --git a/libs/hbb_common/src/password_security.rs b/libs/hbb_common/src/password_security.rs new file mode 100644 index 000000000..ed6376ff9 --- /dev/null +++ b/libs/hbb_common/src/password_security.rs @@ -0,0 +1,330 @@ +pub mod password { + use crate::config::Config; + use std::{ + fmt::Display, + str::FromStr, + sync::{Arc, RwLock}, + }; + + lazy_static::lazy_static! { + pub static ref RANDOM_PASSWORD:Arc> = Arc::new(RwLock::new(Config::get_auto_password())); + } + + const SECURITY_ENABLED: &'static str = "security-password-enabled"; + const RANDOM_ENABLED: &'static str = "random-password-enabled"; + const ONETIME_ENABLED: &'static str = "onetime-password-enabled"; + const ONETIME_ACTIVATED: &'static str = "onetime-password-activated"; + const UPDATE_METHOD: &'static str = "random-password-update-method"; + + #[derive(Debug, Clone, PartialEq, Eq)] + pub enum UpdateMethod { + KEEP, + UPDATE, + DISABLE, + } + + impl FromStr for UpdateMethod { + type Err = (); + + fn from_str(s: &str) -> Result { + if s == "KEEP" { + Ok(Self::KEEP) + } else if s == "UPDATE" { + Ok(Self::UPDATE) + } else if s == "DISABLE" { + Ok(Self::DISABLE) + } else { + Err(()) + } + } + } + + impl Display for UpdateMethod { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + UpdateMethod::KEEP => write!(f, "KEEP"), + UpdateMethod::UPDATE => write!(f, "UPDATE"), + UpdateMethod::DISABLE => write!(f, "DISABLE"), + } + } + } + + pub fn set_random_password(password: &str) { + *RANDOM_PASSWORD.write().unwrap() = password.to_owned(); + } + + pub fn random_password() -> String { + let mut password = RANDOM_PASSWORD.read().unwrap().clone(); + if password.is_empty() { + password = Config::get_auto_password(); + set_random_password(&password); + } + password + } + + pub fn random_password_valid() -> bool { + if random_enabled() { + onetime_password_activated() || !onetime_password_enabled() + } else { + false + } + } + + pub fn passwords() -> Vec { + let mut v = vec![]; + if random_password_valid() { + v.push(random_password()); + } + if security_enabled() { + v.push(Config::get_security_password()); + } + v + } + + pub fn after_session(authorized: bool) { + if authorized && random_enabled() { + UpdateMethod::from_str(&update_method()) + .map(|method| match method { + UpdateMethod::KEEP => {} + UpdateMethod::UPDATE => set_random_password(&Config::get_auto_password()), + UpdateMethod::DISABLE => set_random_enabled(false), + }) + .ok(); + } + } + + pub fn update_method() -> String { + let mut method = Config::get_option(UPDATE_METHOD); + if UpdateMethod::from_str(&method).is_err() { + method = UpdateMethod::KEEP.to_string(); // default is keep + set_update_method(&method); + } + method + } + + pub fn set_update_method(method: &str) { + Config::set_option(UPDATE_METHOD.to_owned(), method.to_owned()); + } + + pub fn random_enabled() -> bool { + str2bool(RANDOM_ENABLED, true, || { + set_onetime_password_activated(false); + set_random_password(&Config::get_auto_password()); + }) + } + + pub fn set_random_enabled(enabled: bool) { + if enabled != random_enabled() { + Config::set_option(RANDOM_ENABLED.to_owned(), bool2str(enabled)); + set_onetime_password_activated(false); + if enabled { + set_random_password(&Config::get_auto_password()); + } + } + } + + pub fn security_enabled() -> bool { + str2bool(SECURITY_ENABLED, true, || {}) + } + + pub fn set_security_enabled(enabled: bool) { + if enabled != security_enabled() { + Config::set_option(SECURITY_ENABLED.to_owned(), bool2str(enabled)); + } + } + + pub fn onetime_password_enabled() -> bool { + str2bool(ONETIME_ENABLED, false, || { + set_onetime_password_activated(false); + set_random_password(&Config::get_auto_password()); + }) + } + + pub fn set_onetime_password_enabled(enabled: bool) { + if enabled != onetime_password_enabled() { + Config::set_option(ONETIME_ENABLED.to_owned(), bool2str(enabled)); + set_onetime_password_activated(false); + set_random_password(&Config::get_auto_password()); + } + } + + pub fn onetime_password_activated() -> bool { + str2bool(ONETIME_ACTIVATED, false, || {}) + } + + pub fn set_onetime_password_activated(activated: bool) { + if activated != onetime_password_activated() { + Config::set_option(ONETIME_ACTIVATED.to_owned(), bool2str(activated)); + if activated { + set_random_password(&Config::get_auto_password()); + } + } + } + + // notice: Function nesting + fn str2bool(key: &str, default: bool, default_set: impl Fn()) -> bool { + let option = Config::get_option(key); + if option == "Y" { + true + } else if option == "N" { + false + } else { + Config::set_option(key.to_owned(), bool2str(default)); + default_set(); + default + } + } + + fn bool2str(option: bool) -> String { + if option { "Y" } else { "N" }.to_owned() + } +} + +pub mod config { + use super::base64::decrypt as decrypt00; + use super::base64::encrypt as encrypt00; + + const VERSION_LEN: usize = 2; + + pub fn encrypt_str_or_original(s: &str, version: &str) -> String { + if version.len() == VERSION_LEN { + if version == "00" { + if let Ok(s) = encrypt00(s.as_bytes()) { + return version.to_owned() + &s; + } + } + } + + s.to_owned() + } + + // bool: whether should store to re-encrypt when load + pub fn decrypt_str_or_original(s: &str, current_version: &str) -> (String, bool) { + if s.len() > VERSION_LEN { + let version = &s[..VERSION_LEN]; + if version == "00" { + if let Ok(v) = decrypt00(&s[VERSION_LEN..].as_bytes()) { + return ( + String::from_utf8_lossy(&v).to_string(), + version != current_version, + ); + } + } + } + + (s.to_owned(), !s.is_empty()) + } + + pub fn encrypt_vec_or_original(v: &[u8], version: &str) -> Vec { + if version.len() == VERSION_LEN { + if version == "00" { + if let Ok(s) = encrypt00(v) { + let mut version = version.to_owned().into_bytes(); + version.append(&mut s.into_bytes()); + return version; + } + } + } + + v.to_owned() + } + + // bool: whether should store to re-encrypt when load + pub fn decrypt_vec_or_original(v: &[u8], current_version: &str) -> (Vec, bool) { + if v.len() > VERSION_LEN { + let version = String::from_utf8_lossy(&v[..VERSION_LEN]); + if version == "00" { + if let Ok(v) = decrypt00(&v[VERSION_LEN..]) { + return (v, version != current_version); + } + } + } + + (v.to_owned(), !v.is_empty()) + } + + mod test { + + #[test] + fn test() { + use crate::password_security::config::*; + + println!("test str"); + let data = "Hello World"; + let encrypted = encrypt_str_or_original(data, "00"); + let (decrypted, store) = decrypt_str_or_original(&encrypted, "00"); + println!("data: {}", data); + println!("encrypted: {}", encrypted); + println!("decrypted: {}", decrypted); + assert_eq!(data, decrypted); + assert_eq!("00", &encrypted[..2]); + assert_eq!(store, false); + let (_, store2) = decrypt_str_or_original(&encrypted, "01"); + assert_eq!(store2, true); + + println!("test vec"); + let data: Vec = vec![1, 2, 3, 4]; + let encrypted = encrypt_vec_or_original(&data, "00"); + let (decrypted, store) = decrypt_vec_or_original(&encrypted, "00"); + println!("data: {:?}", data); + println!("encrypted: {:?}", encrypted); + println!("decrypted: {:?}", decrypted); + assert_eq!(data, decrypted); + assert_eq!("00".as_bytes(), &encrypted[..2]); + assert_eq!(store, false); + let (_, store2) = decrypt_vec_or_original(&encrypted, "01"); + assert_eq!(store2, true); + + println!("test old"); + let data = "00Hello World"; + let (decrypted, store) = decrypt_str_or_original(&data, "00"); + assert_eq!(data, decrypted); + assert_eq!(store, true); + let data: Vec = vec!['0' as u8, '0' as u8, 1, 2, 3, 4]; + let (decrypted, store) = decrypt_vec_or_original(&data, "00"); + assert_eq!(data, decrypted); + assert_eq!(store, true); + let (_, store) = decrypt_str_or_original("", "00"); + assert_eq!(store, false); + let (_, store) = decrypt_vec_or_original(&vec![], "00"); + assert_eq!(store, false); + } + } +} + +mod base64 { + use super::symmetric_crypt; + use sodiumoxide::base64; + + pub fn encrypt(v: &[u8]) -> Result { + if v.len() > 0 { + symmetric_crypt(v, true).map(|v| base64::encode(v, base64::Variant::Original)) + } else { + Err(()) + } + } + + pub fn decrypt(v: &[u8]) -> Result, ()> { + if v.len() > 0 { + base64::decode(v, base64::Variant::Original).and_then(|v| symmetric_crypt(&v, false)) + } else { + Err(()) + } + } +} + +fn symmetric_crypt(data: &[u8], encrypt: bool) -> Result, ()> { + use sodiumoxide::crypto::secretbox; + use std::convert::TryInto; + + let mut keybuf = crate::get_uuid(); + keybuf.resize(secretbox::KEYBYTES, 0); + let key = secretbox::Key(keybuf.try_into().map_err(|_| ())?); + let nonce = secretbox::Nonce([0; secretbox::NONCEBYTES]); + + if encrypt { + Ok(secretbox::seal(data, &nonce, &key)) + } else { + secretbox::open(data, &nonce, &key) + } +} diff --git a/src/common.rs b/src/common.rs index 19af5986b..d9eab5d99 100644 --- a/src/common.rs +++ b/src/common.rs @@ -537,14 +537,6 @@ pub fn is_setup(name: &str) -> bool { name.to_lowercase().ends_with("setdown.exe") || name.to_lowercase().ends_with("安装.exe") } -pub fn get_uuid() -> Vec { - #[cfg(not(any(target_os = "android", target_os = "ios")))] - if let Ok(id) = machine_uid::get() { - return id.into(); - } - Config::get_key_pair().1 -} - pub fn get_custom_rendezvous_server(custom: String) -> String { if !custom.is_empty() { return custom; diff --git a/src/ipc.rs b/src/ipc.rs index 7df06cd22..5f2f83b89 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -7,7 +7,9 @@ use hbb_common::{ config::{self, Config, Config2}, futures::StreamExt as _, futures_util::sink::SinkExt, - log, timeout, tokio, + log, + password_security::password, + timeout, tokio, tokio::io::{AsyncRead, AsyncWrite}, tokio_util::codec::Framed, ResultType, @@ -20,6 +22,16 @@ use std::{collections::HashMap, sync::atomic::Ordering}; #[cfg(not(windows))] use std::{fs::File, io::prelude::*}; +const STR_RANDOM_PASSWORD: &'static str = "random-password"; +const STR_SECURITY_PASSWORD: &'static str = "security-password"; +const STR_RANDOM_PASSWORD_UPDATE_METHOD: &'static str = "random-password-update-method"; +const STR_RANDOM_PASSWORD_ENABLED: &'static str = "random-password-enabled"; +const STR_SECURITY_PASSWORD_ENABLED: &'static str = "security-password-enabled"; +const STR_ONETIME_PASSWORD_ENABLED: &'static str = "onetime-password-enabled"; +const STR_ONETIME_PASSWORD_ACTIVATED: &'static str = "onetime-password-activated"; +const STR_RANDOM_PASSWORD_VALID: &'static str = "random-password-valid"; +pub const STR_PASSWORD_DESCRIPTION: &'static str = "password-description"; + // State with timestamp, because std::time::Instant cannot be serialized #[derive(Debug, Serialize, Deserialize, Copy, Clone)] #[serde(tag = "t", content = "c")] @@ -128,6 +140,7 @@ pub enum Data { ClipboardFileEnabled(bool), PrivacyModeState((i32, PrivacyModeState)), TestRendezvousServer, + Bool((String, Option)), } #[tokio::main(flavor = "current_thread")] @@ -282,8 +295,20 @@ async fn handle(data: Data, stream: &mut Connection) { let value; if name == "id" { value = Some(Config::get_id()); - } else if name == "password" { - value = Some(Config::get_password()); + } else if name == STR_RANDOM_PASSWORD { + value = Some(password::random_password()); + } else if name == STR_SECURITY_PASSWORD { + value = Some(Config::get_security_password()); + } else if name == STR_RANDOM_PASSWORD_UPDATE_METHOD { + value = Some(password::update_method().to_string()); + } else if name == STR_PASSWORD_DESCRIPTION { + value = Some( + password::random_password() + + &password::security_enabled().to_string() + + &password::random_enabled().to_string() + + &password::onetime_password_enabled().to_string() + + &password::onetime_password_activated().to_string(), + ); } else if name == "salt" { value = Some(Config::get_salt()); } else if name == "rendezvous_server" { @@ -303,8 +328,12 @@ async fn handle(data: Data, stream: &mut Connection) { if name == "id" { Config::set_key_confirmed(false); Config::set_id(&value); - } else if name == "password" { - Config::set_password(&value); + } else if name == STR_RANDOM_PASSWORD { + password::set_random_password(&value); + } else if name == STR_SECURITY_PASSWORD { + Config::set_security_password(&value); + } else if name == STR_RANDOM_PASSWORD_UPDATE_METHOD { + password::set_update_method(&value); } else if name == "salt" { Config::set_salt(&value); } else { @@ -344,6 +373,36 @@ async fn handle(data: Data, stream: &mut Connection) { Data::TestRendezvousServer => { crate::test_rendezvous_server(); } + Data::Bool((name, value)) => match value { + None => { + let value; + if name == STR_SECURITY_PASSWORD_ENABLED { + value = Some(password::security_enabled()); + } else if name == STR_RANDOM_PASSWORD_ENABLED { + value = Some(password::random_enabled()); + } else if name == STR_ONETIME_PASSWORD_ENABLED { + value = Some(password::onetime_password_enabled()); + } else if name == STR_ONETIME_PASSWORD_ACTIVATED { + value = Some(password::onetime_password_activated()); + } else if name == STR_RANDOM_PASSWORD_VALID { + value = Some(password::random_password_valid()); + } else { + return; + } + allow_err!(stream.send(&Data::Bool((name, value))).await); + } + Some(value) => { + if name == STR_SECURITY_PASSWORD_ENABLED { + password::set_security_enabled(value); + } else if name == STR_RANDOM_PASSWORD_ENABLED { + password::set_random_enabled(value); + } else if name == STR_ONETIME_PASSWORD_ENABLED { + password::set_onetime_password_enabled(value); + } else if name == STR_ONETIME_PASSWORD_ACTIVATED { + password::set_onetime_password_activated(value); + } + } + }, _ => {} } } @@ -426,6 +485,10 @@ where .await } + async fn send_bool(&mut self, name: &str, value: bool) -> ResultType<()> { + self.send(&Data::Bool((name.to_owned(), Some(value)))).await + } + pub async fn next_timeout(&mut self, ms_timeout: u64) -> ResultType> { Ok(timeout(ms_timeout, self.next()).await??) } @@ -497,9 +560,128 @@ pub async fn set_config(name: &str, value: String) -> ResultType<()> { set_config_async(name, value).await } -pub fn set_password(v: String) -> ResultType<()> { - Config::set_password(&v); - set_config("password", v) +#[tokio::main(flavor = "current_thread")] +async fn get_bool(name: &str) -> ResultType> { + get_bool_async(name, 1_000).await +} + +async fn get_bool_async(name: &str, ms_timeout: u64) -> ResultType> { + let mut c = connect(ms_timeout, "").await?; + c.send(&Data::Bool((name.to_owned(), None))).await?; + if let Some(Data::Bool((name2, value))) = c.next_timeout(ms_timeout).await? { + if name == name2 { + return Ok(value); + } + } + return Ok(None); +} + +pub async fn set_bool_async(name: &str, value: bool) -> ResultType<()> { + let mut c = connect(1000, "").await?; + c.send_bool(name, value).await?; + Ok(()) +} + +#[tokio::main(flavor = "current_thread")] +pub async fn set_bool(name: &str, value: bool) -> ResultType<()> { + set_bool_async(name, value).await +} + +pub fn get_random_password() -> String { + if let Ok(Some(password)) = get_config(STR_RANDOM_PASSWORD) { + password::set_random_password(&password); + password + } else { + password::random_password() + } +} + +pub fn set_random_password(v: String) -> ResultType<()> { + password::set_random_password(&v); + set_config(STR_RANDOM_PASSWORD, v) +} + +pub fn set_security_password(v: String) -> ResultType<()> { + Config::set_security_password(&v); + set_config(STR_SECURITY_PASSWORD, v) +} + +pub fn random_password_update_method() -> String { + if let Ok(Some(method)) = get_config(STR_RANDOM_PASSWORD_UPDATE_METHOD) { + password::set_update_method(&method); + method + } else { + password::update_method() + } +} + +pub fn set_random_password_update_method(method: String) -> ResultType<()> { + password::set_update_method(&method); + set_config(STR_RANDOM_PASSWORD_UPDATE_METHOD, method) +} + +pub fn is_random_password_enabled() -> bool { + if let Ok(Some(enabled)) = get_bool(STR_RANDOM_PASSWORD_ENABLED) { + password::set_random_enabled(enabled); + enabled + } else { + password::random_enabled() + } +} + +pub fn set_random_password_enabled(enabled: bool) -> ResultType<()> { + password::set_random_enabled(enabled); + set_bool(STR_RANDOM_PASSWORD_ENABLED, enabled) +} + +pub fn is_security_password_enabled() -> bool { + if let Ok(Some(enabled)) = get_bool(STR_SECURITY_PASSWORD_ENABLED) { + password::set_security_enabled(enabled); + enabled + } else { + password::security_enabled() + } +} + +pub fn set_security_password_enabled(enabled: bool) -> ResultType<()> { + password::set_security_enabled(enabled); + set_bool(STR_SECURITY_PASSWORD_ENABLED, enabled) +} + +pub fn is_onetime_password_enabled() -> bool { + if let Ok(Some(enabled)) = get_bool(STR_ONETIME_PASSWORD_ENABLED) { + password::set_onetime_password_enabled(enabled); + enabled + } else { + password::onetime_password_enabled() + } +} + +pub fn set_onetime_password_enabled(enabled: bool) -> ResultType<()> { + password::set_onetime_password_enabled(enabled); + set_bool(STR_ONETIME_PASSWORD_ENABLED, enabled) +} + +pub fn is_onetime_password_activated() -> bool { + if let Ok(Some(activated)) = get_bool(STR_ONETIME_PASSWORD_ACTIVATED) { + password::set_onetime_password_activated(activated); + activated + } else { + password::onetime_password_activated() + } +} + +pub fn set_onetime_password_activated(activated: bool) -> ResultType<()> { + password::set_onetime_password_activated(activated); + set_bool(STR_ONETIME_PASSWORD_ACTIVATED, activated) +} + +pub fn is_random_password_valid() -> bool { + if let Ok(Some(valid)) = get_bool(STR_RANDOM_PASSWORD_VALID) { + valid + } else { + password::random_password_valid() + } } pub fn get_id() -> String { @@ -518,15 +700,6 @@ pub fn get_id() -> String { } } -pub fn get_password() -> String { - if let Ok(Some(v)) = get_config("password") { - Config::set_password(&v); - v - } else { - Config::get_password() - } -} - pub async fn get_rendezvous_server(ms_timeout: u64) -> (String, Vec) { if let Ok(Some(v)) = get_config_async("rendezvous_server", ms_timeout).await { let mut urls = v.split(","); diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 58a3a6cd7..6e3d4d067 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "保持RustDesk后台服务"), ("Ignore Battery Optimizations", "忽略电池优化"), ("android_open_battery_optimizations_tip", "如需关闭此功能,请在接下来的RustDesk应用设置页面中,找到并进入 [电源] 页面,取消勾选 [不受限制]"), + ("Random Password After Session", "会话结束更新随机密码"), + ("Keep", "保持"), + ("Update", "更新"), + ("Disable", "禁用"), + ("Onetime Password", "一次性口令"), + ("Verification Method", "密码验证方式"), + ("Enable security password", "启用安全密码"), + ("Enable random password", "启用随机密码"), + ("Enable onetime password", "启用一次性访问功能"), + ("Disable onetime password", "禁用一次性访问功能"), + ("Activate onetime password", "激活一次性访问功能"), + ("Set security password", "设置安全密码"), + ("Connection not allowed", "对方不允许连接"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index c5c432d46..d9ff10416 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index d13cd8550..f9776e687 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index b7f471992..c9cd243fb 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 4719a3be3..a21833559 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index adf214b97..14ba0ab57 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index e92069993..f387b550b 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index b0d1bfda0..e35ab8195 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 4cdaf8b95..6bf69e476 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 3a0c2dc2a..778313d5b 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index e3cadd204..176833501 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 7735e3db3..d8a7702f3 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index ea8d22aa8..332336007 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 9ea7146e1..7b39a2876 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 1d30425dc..02468201e 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 30c8f5a76..ea94d159c 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "保持RustDesk後台服務"), ("Ignore Battery Optimizations", "忽略電池優化"), ("android_open_battery_optimizations_tip", "如需關閉此功能,請在接下來的RustDesk應用設置頁面中,找到並進入 [電源] 頁面,取消勾選 [不受限制]"), + ("Random Password After Session", "會話結束更新隨機密碼"), + ("Keep", "保持"), + ("Update", "更新"), + ("Disable", "禁用"), + ("Onetime Password", "一次性口令"), + ("Verification Method", "密碼驗證方式"), + ("Enable security password", "啟用安全密碼"), + ("Enable random password", "啟用隨機密碼"), + ("Enable onetime password", "啟用一次性訪問功能"), + ("Disable onetime password", "禁用一次性訪問功能"), + ("Activate onetime password", "激活一次性訪問功能"), + ("Set security password", "設置安全密碼"), + ("Connection not allowed", "對方不允許連接"), ].iter().cloned().collect(); } diff --git a/src/main.rs b/src/main.rs index 2e2e4c8ac..2f30f4b4d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -148,7 +148,7 @@ fn main() { return; } else if args[0] == "--password" { if args.len() == 2 { - ipc::set_password(args[1].to_owned()).unwrap(); + ipc::set_security_password(args[1].to_owned()).unwrap(); } return; } else if args[0] == "--check-hwcodec-config" { diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index 6dc7a11c9..d4b76c991 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -389,7 +389,7 @@ impl RendezvousMediator { async fn register_pk(&mut self, socket: &mut FramedSocket) -> ResultType<()> { let mut msg_out = Message::new(); let pk = Config::get_key_pair().1; - let uuid = crate::get_uuid(); + let uuid = hbb_common::get_uuid(); let id = Config::get_id(); self.last_id_pk_registry = id.clone(); msg_out.set_register_pk(RegisterPk { diff --git a/src/server/connection.rs b/src/server/connection.rs index dbf1545ae..0481126c4 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -8,6 +8,7 @@ use crate::video_service; use crate::{common::MOBILE_INFO2, mobile::connection_manager::start_channel}; use crate::{ipc, VERSION}; use hbb_common::fs::can_enable_overwrite_detection; +use hbb_common::password_security::password; use hbb_common::{ config::Config, fs, @@ -398,6 +399,7 @@ impl Connection { video_service::notify_video_frame_feched(id, None); scrap::codec::Encoder::update_video_encoder(id, scrap::codec::EncoderUpdate::Remove); video_service::VIDEO_QOS.lock().unwrap().reset(); + password::after_session(conn.authorized); if let Err(err) = conn.try_port_forward_loop(&mut rx_from_cm).await { conn.on_close(&err.to_string(), false); } @@ -571,7 +573,7 @@ impl Connection { let url = self.api_server.clone(); let mut v = v; v["id"] = json!(Config::get_id()); - v["uuid"] = json!(base64::encode(crate::get_uuid())); + v["uuid"] = json!(base64::encode(hbb_common::get_uuid())); v["Id"] = json!(self.inner.id); tokio::spawn(async move { allow_err!(Self::post_audit_async(url, v).await); @@ -778,6 +780,36 @@ impl Connection { self.tx_input.send(MessageInput::Key((msg, press))).ok(); } + fn validate_password(&mut self, lr_password: Vec) -> bool { + let validate = |password: String| { + if password.len() == 0 { + return false; + } + let mut hasher = Sha256::new(); + hasher.update(password); + hasher.update(&self.hash.salt); + let mut hasher2 = Sha256::new(); + hasher2.update(&hasher.finalize()[..]); + hasher2.update(&self.hash.challenge); + hasher2.finalize()[..] == lr_password[..] + }; + if password::security_enabled() { + if validate(Config::get_security_password()) { + return true; + } + } + if password::random_password_valid() { + if validate(password::random_password()) { + if password::onetime_password_activated() { + password::set_onetime_password_activated(false); + } + return true; + } + } + + false + } + async fn on_message(&mut self, msg: Message) -> bool { if let Some(message::Union::LoginRequest(lr)) = msg.union { if let Some(o) = lr.option.as_ref() { @@ -853,12 +885,10 @@ impl Connection { } else if lr.password.is_empty() { self.try_start_cm(lr.my_id, lr.my_name, false); } else { - let mut hasher = Sha256::new(); - hasher.update(&Config::get_password()); - hasher.update(&self.hash.salt); - let mut hasher2 = Sha256::new(); - hasher2.update(&hasher.finalize()[..]); - hasher2.update(&self.hash.challenge); + if password::passwords().len() == 0 { + self.send_login_error("Connection not allowed").await; + return false; + } let mut failure = LOGIN_FAILURES .lock() .unwrap() @@ -871,7 +901,7 @@ impl Connection { .await; } else if time == failure.0 && failure.1 > 6 { self.send_login_error("Please try 1 minute later").await; - } else if hasher2.finalize()[..] != lr.password[..] { + } else if !self.validate_password(lr.password.clone()) { if failure.0 == time { failure.1 += 1; failure.2 += 1; diff --git a/src/ui.rs b/src/ui.rs index 2b7ffcc29..3fecc33cd 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -43,6 +43,7 @@ struct UI( Arc>>, Arc>, mpsc::UnboundedSender, + Arc>, ); struct UIHostHandler; @@ -169,7 +170,7 @@ pub fn start(args: &mut [String]) { impl UI { fn new(childs: Childs) -> Self { let res = check_connect_status(true); - Self(childs, res.0, res.1, Default::default(), res.2) + Self(childs, res.0, res.1, Default::default(), res.2, res.3) } fn recent_sessions_updated(&mut self) -> bool { @@ -186,16 +187,16 @@ impl UI { ipc::get_id() } - fn get_password(&mut self) -> String { - ipc::get_password() + fn get_random_password(&self) -> String { + ipc::get_random_password() } - fn update_password(&mut self, password: String) { - if password.is_empty() { - allow_err!(ipc::set_password(Config::get_auto_password())); - } else { - allow_err!(ipc::set_password(password)); - } + fn update_random_password(&self) { + allow_err!(ipc::set_random_password(Config::get_auto_password())); + } + + fn set_security_password(&self, password: String) { + allow_err!(ipc::set_security_password(password)); } fn get_remote_id(&mut self) -> String { @@ -704,7 +705,7 @@ impl UI { } fn get_uuid(&self) -> String { - base64::encode(crate::get_uuid()) + base64::encode(hbb_common::get_uuid()) } fn open_url(&self, url: String) { @@ -774,6 +775,54 @@ impl UI { fn get_langs(&self) -> String { crate::lang::LANGS.to_string() } + + fn random_password_update_method(&self) -> String { + ipc::random_password_update_method() + } + + fn set_random_password_update_method(&self, method: String) { + allow_err!(ipc::set_random_password_update_method(method)); + } + + fn is_random_password_enabled(&self) -> bool { + ipc::is_random_password_enabled() + } + + fn set_random_password_enabled(&self, enabled: bool) { + allow_err!(ipc::set_random_password_enabled(enabled)); + } + + fn is_security_password_enabled(&self) -> bool { + ipc::is_security_password_enabled() + } + + fn set_security_password_enabled(&self, enabled: bool) { + allow_err!(ipc::set_security_password_enabled(enabled)); + } + + fn is_onetime_password_enabled(&self) -> bool { + ipc::is_onetime_password_enabled() + } + + fn set_onetime_password_enabled(&self, enabled: bool) { + allow_err!(ipc::set_onetime_password_enabled(enabled)); + } + + fn is_onetime_password_activated(&self) -> bool { + ipc::is_onetime_password_activated() + } + + fn set_onetime_password_activated(&self, activated: bool) { + allow_err!(ipc::set_onetime_password_activated(activated)); + } + + fn is_random_password_valid(&self) -> bool { + ipc::is_random_password_valid() + } + + fn password_description(&mut self) -> String { + self.5.lock().unwrap().clone() + } } impl sciter::EventHandler for UI { @@ -783,8 +832,9 @@ impl sciter::EventHandler for UI { fn is_xfce(); fn using_public_server(); fn get_id(); - fn get_password(); - fn update_password(String); + fn get_random_password(); + fn update_random_password(); + fn set_security_password(String); fn get_remote_id(); fn set_remote_id(String); fn closing(i32, i32, i32, i32); @@ -854,6 +904,18 @@ impl sciter::EventHandler for UI { fn get_uuid(); fn has_hwcodec(); fn get_langs(); + fn random_password_update_method(); + fn set_random_password_update_method(String); + fn is_random_password_enabled(); + fn set_random_password_enabled(bool); + fn is_security_password_enabled(); + fn set_security_password_enabled(bool); + fn is_onetime_password_enabled(); + fn set_onetime_password_enabled(bool); + fn is_onetime_password_activated(); + fn set_onetime_password_activated(bool); + fn is_random_password_valid(); + fn password_description(); } } @@ -893,6 +955,7 @@ async fn check_connect_status_( status: Arc>, options: Arc>>, rx: mpsc::UnboundedReceiver, + password: Arc>, ) { let mut key_confirmed = false; let mut rx = rx; @@ -919,6 +982,8 @@ async fn check_connect_status_( Ok(Some(ipc::Data::Config((name, Some(value))))) => { if name == "id" { id = value; + } else if name == ipc::STR_PASSWORD_DESCRIPTION { + *password.lock().unwrap() = value; } } Ok(Some(ipc::Data::OnlineStatus(Some((mut x, c))))) => { @@ -938,6 +1003,7 @@ async fn check_connect_status_( c.send(&ipc::Data::OnlineStatus(None)).await.ok(); c.send(&ipc::Data::Options(None)).await.ok(); c.send(&ipc::Data::Config(("id".to_owned(), None))).await.ok(); + c.send(&ipc::Data::Config((ipc::STR_PASSWORD_DESCRIPTION.to_owned(), None))).await.ok(); } } } @@ -986,14 +1052,19 @@ fn check_connect_status( Arc>, Arc>>, mpsc::UnboundedSender, + Arc>, ) { let status = Arc::new(Mutex::new((0, false, 0, "".to_owned()))); let options = Arc::new(Mutex::new(Config::get_options())); let cloned = status.clone(); let cloned_options = options.clone(); let (tx, rx) = mpsc::unbounded_channel::(); - std::thread::spawn(move || check_connect_status_(reconnect, cloned, cloned_options, rx)); - (status, options, tx) + let password = Arc::new(Mutex::new(String::default())); + let cloned_password = password.clone(); + std::thread::spawn(move || { + check_connect_status_(reconnect, cloned, cloned_options, rx, cloned_password) + }); + (status, options, tx, password) } const INVALID_FORMAT: &'static str = "Invalid format"; diff --git a/src/ui/common.css b/src/ui/common.css index 39427fc8a..c3f3706ef 100644 --- a/src/ui/common.css +++ b/src/ui/common.css @@ -120,7 +120,7 @@ textarea:empty { @ELLIPSIS; } -div.password svg { +div.password svg:not(.checkmark) { padding-left: 1em; size: 16px; color: #ddd; diff --git a/src/ui/common.tis b/src/ui/common.tis index 46adc3288..aae950c2d 100644 --- a/src/ui/common.tis +++ b/src/ui/common.tis @@ -141,7 +141,7 @@ function adjustBorder() { if (el) el.attributes.toggleClass("active", view.windowState == View.WINDOW_FULL_SCREEN); } -var svg_checkmark = ; +var svg_checkmark = ; var svg_edit = ; diff --git a/src/ui/index.css b/src/ui/index.css index 3c4dc6a47..f4ec4c2f5 100644 --- a/src/ui/index.css +++ b/src/ui/index.css @@ -403,3 +403,18 @@ div.remote-session svg#menu { background: none; color: white; } + +svg#refresh-password { + display: inline-block; + stroke:#ddd; +} + +svg#refresh-password:hover { + stroke:color(text); +} + +li:disabled, li:disabled:hover { + color: color(lighter-text); + background: color(menu); +} + diff --git a/src/ui/index.tis b/src/ui/index.tis index 099ca2af8..4a6004135 100644 --- a/src/ui/index.tis +++ b/src/ui/index.tis @@ -20,6 +20,7 @@ var svg_menu = ; +var svg_refresh_password = ; var my_id = ""; function get_id() { @@ -520,10 +521,6 @@ class App: Reactor.Component var is_can_screen_recording = handler.is_can_screen_recording(false); return
    - -
  • {translate('Refresh random password')}
  • -
  • {translate('Set your own password')}
  • -
    {translate('Your Desktop')}
    @@ -533,8 +530,7 @@ class App: Reactor.Component {key_confirmed ? : translate("Generating ...")}
    -
    {translate('Password')}
    - +
    {!is_win || handler.is_installed() ? "": } @@ -806,44 +802,151 @@ function watch_screen_recording() { class PasswordEyeArea : Reactor.Component { render() { + var show = handler.is_random_password_valid(); + var value = show ? handler.get_random_password() : "-"; return
    - - {svg_eye} + + {svg_refresh_password}
    ; } - - event mouseenter { - var me = this; - me.leaved = false; - me.timer(300ms, function() { - if (me.leaved) return; - me.input.value = handler.get_password(); - }); - } - event mouseleave { - this.leaved = true; - this.input.value = "******"; + event click $(svg#refresh-password) (_, me) { + if (handler.is_random_password_valid()) handler.update_random_password(); + this.update(); } } -class Password: Reactor.Component { +var verificationMethodMenu; +class VerificationMethodMenu: Reactor.Component { + function this() { + verificationMethodMenu = this; + } + function render() { - return
    - - {svg_edit} + if (!this.show) return
  • ; + var me = this; + self.timer(1ms, function() { me.toggleMenuState() }); + return
  • {translate('Verification Method')} + +
  • {svg_checkmark}{translate('Enable security password')}
  • +
  • {svg_checkmark}{translate('Enable random password')}
  • +
    +
  • ; + } + + function toggleMenuState() { + var security_enabled = handler.is_security_password_enabled(); + var random_enabled = handler.is_random_password_enabled(); + var onetime_enabled = handler.is_onetime_password_enabled(); + for (var (index, el) in this.$$(menu#verification-method>li)) { + if (index == 0) el.attributes.toggleClass("selected", security_enabled); + if (index == 1) el.attributes.toggleClass("selected", random_enabled); + } + } + + event click $(menu#verification-method>li) (_, me) { + switch (me.id.substring('verification-method-'.length)) { + case 'security': + { + var security_enabled = handler.is_security_password_enabled(); + handler.set_security_password_enabled(!security_enabled); + } + break; + case 'random': + { + var random_enabled = handler.is_random_password_enabled(); + handler.set_random_password_enabled(!random_enabled); + } + break; + } + + this.toggleMenuState(); + passwordArea.update(); + } +} + +var randomPasswordUpdateMethodMenu; +class RandomPasswordUpdateMethodMenu: Reactor.Component { + function this() { + randomPasswordUpdateMethodMenu = this; + } + + function render() { + if (!this.show) return
  • ; + var me = this; + var random_enabled = handler.is_random_password_enabled(); + self.timer(1ms, function() { me.toggleMenuState() }); + return
  • {translate('Random Password After Session')} + +
  • {svg_checkmark}{translate('Keep')}
  • +
  • {svg_checkmark}{translate('Update')}
  • +
  • {svg_checkmark}{translate('Disable')}
  • +
    +
  • ; + } + + function toggleMenuState() { + var method = handler.random_password_update_method(); + for (var (index, el) in this.$$(menu#random-password-update-method>li)) { + if (index == 0) el.attributes.toggleClass("selected", method == "KEEP"); + if (index == 1) el.attributes.toggleClass("selected", method == "UPDATE"); + if (index == 2) el.attributes.toggleClass("selected", method == "DISABLE"); + } + } + + event click $(menu#random-password-update-method>li) (_, me) { + if (me.id === 'random-password-update-method-keep') handler.set_random_password_update_method("KEEP"); + if (me.id === 'random-password-update-method-update') handler.set_random_password_update_method("UPDATE"); + if (me.id === 'random-password-update-method-disable') handler.set_random_password_update_method("DISABLE"); + this.toggleMenuState(); + passwordArea.update(); + } +} + +var passwordArea; +class PasswordArea: Reactor.Component { + function this() { + passwordArea = this; + } + + function render() { + var onetime_enabled = handler.is_onetime_password_enabled(); + + return +
    +
    {translate(onetime_enabled ? 'Onetime Password' : 'Password')}
    +
    + {this.renderPop()} + + {svg_edit} +
    ; } - event click $(svg#edit) (_, me) { - var menu = $(menu#edit-password-context); - me.popup(menu); + function renderPop() { + var security_enabled = handler.is_security_password_enabled(); + var random_enabled = handler.is_random_password_enabled(); + var onetime_enabled = handler.is_onetime_password_enabled(); + var onetime_activated = handler.is_onetime_password_activated(); + + return +
  • {translate(onetime_enabled ? "Disable onetime password" : "Enable onetime password")}
  • +
  • {translate('Activate onetime password')}
  • +
    + +
    +
  • {translate('Set security password')}
  • +
    + + ; } - event click $(li#refresh-password) { - handler.update_password(""); - this.update(); + event click $(svg#edit) (_, me) { + randomPasswordUpdateMethodMenu.update({show: true }); + verificationMethodMenu.update({show: true }); + var menu = $(menu#edit-password-context); + me.popup(menu); } event click $(li#set-password) { @@ -862,12 +965,36 @@ class Password: Reactor.Component { if (p0 != p1) { return translate("The confirmation is not identical."); } - handler.update_password(p0); + handler.set_security_password(p0); me.update(); }); } + + event click $(li#enable-onetime-password) { + var onetime_enabled = handler.is_onetime_password_enabled(); + handler.set_onetime_password_enabled(!onetime_enabled); + passwordArea.update(); + } + + event click $(li#activate-onetime-password) { + handler.set_onetime_password_activated(true); + passwordArea.update(); + } } +var last_password_description = ""; +function updatePasswordArea() { + self.timer(1s, function() { + var description = handler.password_description(); + if (last_password_description != description) { + last_password_description = description + passwordArea.update(); + } + updatePasswordArea(); + }); +} +updatePasswordArea(); + class ID: Reactor.Component { function render() { return Date: Wed, 22 Jun 2022 09:41:35 +0800 Subject: [PATCH 37/50] crypt peer/peer_rdp/peer_os/socks5 password Signed-off-by: 21pages --- libs/hbb_common/src/config.rs | 62 +++++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index d8502151f..787ffe5ee 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -1,6 +1,9 @@ use crate::{ log, - password_security::config::{decrypt_str_or_original, encrypt_str_or_original}, + password_security::config::{ + decrypt_str_or_original, decrypt_vec_or_original, encrypt_str_or_original, + encrypt_vec_or_original, + }, }; use anyhow::Result; use directories_next::ProjectDirs; @@ -119,7 +122,7 @@ pub struct Config2 { pub options: HashMap, } -#[derive(Debug, Default, Serialize, Deserialize, Clone)] +#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)] pub struct PeerConfig { #[serde(default)] pub password: Vec, @@ -173,7 +176,7 @@ pub struct PeerInfoSerde { pub platform: String, } -#[derive(Debug, Default, Serialize, Deserialize, Clone)] +#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)] pub struct TransferSerde { #[serde(default)] pub write_jobs: Vec, @@ -212,7 +215,16 @@ fn patch(path: PathBuf) -> PathBuf { impl Config2 { fn load() -> Config2 { - Config::load_::("2") + let mut config = Config::load_::("2"); + if let Some(mut socks) = config.socks { + let (password, store) = decrypt_str_or_original(&socks.password, PASSWORD_ENC_VERSION); + socks.password = password; + config.socks = Some(socks); + if store { + config.store(); + } + } + config } pub fn file() -> PathBuf { @@ -220,7 +232,12 @@ impl Config2 { } fn store(&self) { - Config::store_(self, "2"); + let mut config = self.clone(); + if let Some(mut socks) = config.socks { + socks.password = encrypt_str_or_original(&socks.password, PASSWORD_ENC_VERSION); + config.socks = Some(socks); + } + Config::store_(&config, "2"); } pub fn get() -> Config2 { @@ -722,7 +739,28 @@ impl PeerConfig { pub fn load(id: &str) -> PeerConfig { let _ = CONFIG.read().unwrap(); // for lock match confy::load_path(&Self::path(id)) { - Ok(config) => config, + Ok(config) => { + let mut config: PeerConfig = config; + let mut store = false; + let (password, store2) = + decrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION); + config.password = password; + store = store || store2; + config.options.get_mut("rdp_password").map(|v| { + let (password, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION); + *v = password; + store = store || store2; + }); + config.options.get_mut("os-password").map(|v| { + let (password, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION); + *v = password; + store = store || store2; + }); + if store { + config.store(id); + } + config + } Err(err) => { log::error!("Failed to load config: {}", err); Default::default() @@ -732,7 +770,17 @@ impl PeerConfig { pub fn store(&self, id: &str) { let _ = CONFIG.read().unwrap(); // for lock - if let Err(err) = confy::store_path(Self::path(id), self) { + let mut config = self.clone(); + config.password = encrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION); + config + .options + .get_mut("rdp_password") + .map(|v| *v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION)); + config + .options + .get_mut("os-password") + .map(|v| *v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION)); + if let Err(err) = confy::store_path(Self::path(id), config) { log::error!("Failed to store config: {}", err); } } From 0812dc79b81a1c572864c28d655e493e508fdfcb Mon Sep 17 00:00:00 2001 From: 21pages Date: Mon, 18 Jul 2022 11:05:19 +0800 Subject: [PATCH 38/50] control end close message Signed-off-by: 21pages --- src/server/connection.rs | 4 ++++ src/ui/remote.rs | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/src/server/connection.rs b/src/server/connection.rs index 0481126c4..9c7025efc 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1133,6 +1133,10 @@ impl Connection { Some(Instant::now().into()), ); } + Some(misc::Union::CloseReason(_)) => { + self.on_close("Peer close", true); + return false; + } _ => {} }, _ => {} diff --git a/src/ui/remote.rs b/src/ui/remote.rs index fc83a09a5..4d941e1f8 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -1633,6 +1633,11 @@ impl Remote { // log::info!("new msg from ui, {}",data); match data { Data::Close => { + let mut misc = Misc::new(); + misc.set_close_reason("".to_owned()); + let mut msg = Message::new(); + msg.set_misc(misc); + allow_err!(peer.send(&msg).await); return false; } Data::Login((password, remember)) => { From 66e04c9fe75de81964e304951335561838ea2691 Mon Sep 17 00:00:00 2001 From: 21pages Date: Mon, 18 Jul 2022 14:03:52 +0800 Subject: [PATCH 39/50] keep session when network interrupt Signed-off-by: 21pages --- libs/hbb_common/protos/message.proto | 1 + src/client.rs | 4 ++ src/server/connection.rs | 94 +++++++++++++++++++++++----- 3 files changed, 83 insertions(+), 16 deletions(-) diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index 0538f1aef..2044388f8 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -63,6 +63,7 @@ message LoginRequest { PortForward port_forward = 8; } bool video_ack_required = 9; + uint64 session_id = 10; } message ChatMessage { string text = 1; } diff --git a/src/client.rs b/src/client.rs index 2350d243a..28101896e 100644 --- a/src/client.rs +++ b/src/client.rs @@ -28,6 +28,7 @@ use hbb_common::{ log, message_proto::{option_message::BoolOption, *}, protobuf::Message as _, + rand, rendezvous_proto::*, socket_client, sodiumoxide::crypto::{box_, secretbox, sign}, @@ -782,6 +783,7 @@ pub struct LoginConfigHandler { pub version: i64, pub conn_id: i32, features: Option, + session_id: u64, } impl Deref for LoginConfigHandler { @@ -805,6 +807,7 @@ impl LoginConfigHandler { let config = self.load_config(); self.remember = !config.password.is_empty(); self.config = config; + self.session_id = rand::random(); } pub fn should_auto_login(&self) -> String { @@ -1140,6 +1143,7 @@ impl LoginConfigHandler { my_id, my_name: crate::username(), option: self.get_option_message(true).into(), + session_id: self.session_id, ..Default::default() }; if self.is_file_transfer { diff --git a/src/server/connection.rs b/src/server/connection.rs index 9c7025efc..04b070cfe 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -36,6 +36,7 @@ pub type Sender = mpsc::UnboundedSender<(Instant, Arc)>; lazy_static::lazy_static! { static ref LOGIN_FAILURES: Arc::>> = Default::default(); + static ref SESSIONS: Arc::>> = Default::default(); } pub static CLICK_TIME: AtomicI64 = AtomicI64::new(0); pub static MOUSE_MOVE_TIME: AtomicI64 = AtomicI64::new(0); @@ -54,6 +55,14 @@ enum MessageInput { BlockOff, } +#[derive(Clone, Debug)] +struct Session { + name: String, + session_id: u64, + last_recv_time: Arc>, + random_password: String, +} + pub struct Connection { inner: ConnInner, stream: super::Stream, @@ -81,6 +90,8 @@ pub struct Connection { video_ack_required: bool, peer_info: (String, String), api_server: String, + lr: LoginRequest, + last_recv_time: Arc>, } impl Subscriber for ConnInner { @@ -112,6 +123,7 @@ const H1: Duration = Duration::from_secs(3600); const MILLI1: Duration = Duration::from_millis(1); const SEND_TIMEOUT_VIDEO: u64 = 12_000; const SEND_TIMEOUT_OTHER: u64 = SEND_TIMEOUT_VIDEO * 10; +const SESSION_TIMEOUT: Duration = Duration::from_secs(30); impl Connection { pub async fn start( @@ -165,6 +177,8 @@ impl Connection { video_ack_required: false, peer_info: Default::default(), api_server: "".to_owned(), + lr: Default::default(), + last_recv_time: Arc::new(Mutex::new(Instant::now())), }; #[cfg(not(any(target_os = "android", target_os = "ios")))] tokio::spawn(async move { @@ -224,6 +238,7 @@ impl Connection { msg_out.set_misc(misc); conn.send(msg_out).await; conn.on_close("Close requested from connection manager", false); + SESSIONS.lock().unwrap().remove(&conn.lr.my_id); break; } ipc::Data::ChatMessage{text} => { @@ -317,6 +332,7 @@ impl Connection { }, Ok(bytes) => { last_recv_time = Instant::now(); + *conn.last_recv_time.lock().unwrap() = Instant::now(); if let Ok(msg_in) = Message::parse_from_bytes(&bytes) { if !conn.on_message(msg_in).await { break; @@ -780,38 +796,77 @@ impl Connection { self.tx_input.send(MessageInput::Key((msg, press))).ok(); } - fn validate_password(&mut self, lr_password: Vec) -> bool { - let validate = |password: String| { - if password.len() == 0 { - return false; - } - let mut hasher = Sha256::new(); - hasher.update(password); - hasher.update(&self.hash.salt); - let mut hasher2 = Sha256::new(); - hasher2.update(&hasher.finalize()[..]); - hasher2.update(&self.hash.challenge); - hasher2.finalize()[..] == lr_password[..] - }; + fn validate_one_password(&self, password: String) -> bool { + if password.len() == 0 { + return false; + } + let mut hasher = Sha256::new(); + hasher.update(password); + hasher.update(&self.hash.salt); + let mut hasher2 = Sha256::new(); + hasher2.update(&hasher.finalize()[..]); + hasher2.update(&self.hash.challenge); + hasher2.finalize()[..] == self.lr.password[..] + } + + fn validate_password(&mut self) -> bool { if password::security_enabled() { - if validate(Config::get_security_password()) { + if self.validate_one_password(Config::get_security_password()) { return true; } } if password::random_password_valid() { - if validate(password::random_password()) { + let password = password::random_password(); + if self.validate_one_password(password.clone()) { if password::onetime_password_activated() { password::set_onetime_password_activated(false); } + SESSIONS.lock().unwrap().insert( + self.lr.my_id.clone(), + Session { + name: self.lr.my_name.clone(), + session_id: self.lr.session_id, + last_recv_time: self.last_recv_time.clone(), + random_password: password, + }, + ); return true; } } + false + } + fn is_of_recent_session(&mut self) -> bool { + let session = SESSIONS + .lock() + .unwrap() + .get(&self.lr.my_id) + .map(|s| s.to_owned()); + if let Some(session) = session { + if session.name == self.lr.my_name + && session.session_id == self.lr.session_id + && !self.lr.password.is_empty() + && self.validate_one_password(session.random_password.clone()) + && session.last_recv_time.lock().unwrap().elapsed() < SESSION_TIMEOUT + { + SESSIONS.lock().unwrap().insert( + self.lr.my_id.clone(), + Session { + name: self.lr.my_name.clone(), + session_id: self.lr.session_id, + last_recv_time: self.last_recv_time.clone(), + random_password: session.random_password, + }, + ); + return true; + } + } false } async fn on_message(&mut self, msg: Message) -> bool { if let Some(message::Union::LoginRequest(lr)) = msg.union { + self.lr = lr.clone(); if let Some(o) = lr.option.as_ref() { self.update_option(o).await; if let Some(q) = o.video_codec_state.clone().take() { @@ -882,6 +937,12 @@ impl Connection { } if !crate::is_ip(&lr.username) && lr.username != Config::get_id() { self.send_login_error("Offline").await; + } else if self.is_of_recent_session() { + self.try_start_cm(lr.my_id, lr.my_name, true); + self.send_logon_response().await; + if self.port_forward_socket.is_some() { + return false; + } } else if lr.password.is_empty() { self.try_start_cm(lr.my_id, lr.my_name, false); } else { @@ -901,7 +962,7 @@ impl Connection { .await; } else if time == failure.0 && failure.1 > 6 { self.send_login_error("Please try 1 minute later").await; - } else if !self.validate_password(lr.password.clone()) { + } else if !self.validate_password() { if failure.0 == time { failure.1 += 1; failure.2 += 1; @@ -1135,6 +1196,7 @@ impl Connection { } Some(misc::Union::CloseReason(_)) => { self.on_close("Peer close", true); + SESSIONS.lock().unwrap().remove(&self.lr.my_id); return false; } _ => {} From 6ecf1dfc47ce3f9ac381f6f9bb1bd14642a23f8e Mon Sep 17 00:00:00 2001 From: Vik <63919734+ViktorOn@users.noreply.github.com> Date: Tue, 19 Jul 2022 21:25:31 +0300 Subject: [PATCH 40/50] Update en.rs --- src/lang/en.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/en.rs b/src/lang/en.rs index 1818a619a..a4923eb19 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -27,6 +27,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"), ("doc_fix_wayland", "https://rustdesk.com/docs/en/manual/linux/#x11-required"), ("server_not_support", "Not yet supported by the server"), - ("android_open_battery_optimizations_tip", "If you want to disable this feature, please go to the next RustDesk application settings page, find and enter [Battery] ,Uncheck [Unrestricted]"), + ("android_open_battery_optimizations_tip", "If you want to disable this feature, please go to the next RustDesk application settings page, find and enter [Battery], Uncheck [Unrestricted]"), ].iter().cloned().collect(); } From 013576abeb483469ce0c024f1f510492b1354cb7 Mon Sep 17 00:00:00 2001 From: Vik <63919734+ViktorOn@users.noreply.github.com> Date: Tue, 19 Jul 2022 21:31:21 +0300 Subject: [PATCH 41/50] Update ru.rs Onetime means once, i.e. one time and that's it (not forever). Is that exactly what you mean? --- src/lang/ru.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/lang/ru.rs b/src/lang/ru.rs index d8a7702f3..55d33c7b3 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -222,7 +222,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Remote ID", "Удаленный идентификатор"), ("Paste", "Вставить"), ("Paste here?", "Вставить сюда?"), - ("Are you sure to close the connection?", "Вы уверены, что хотите закрыть соединение?"), + ("Are you sure to close the connection?", "Вы уверены, что хотите завершить подключение?"), ("Download new version", "Скачать новую версию"), ("Touch mode", "Сенсорный режим"), ("Mouse mode", "Режим мыши"), @@ -284,21 +284,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "В режиме конфиденциальности"), ("Out privacy mode", "Выход из режима конфиденциальности"), ("Language", "Язык"), - ("Keep RustDesk background service", ""), - ("Ignore Battery Optimizations", ""), - ("android_open_battery_optimizations_tip", ""), - ("Random Password After Session", ""), - ("Keep", ""), - ("Update", ""), - ("Disable", ""), - ("Onetime Password", ""), - ("Verification Method", ""), - ("Enable security password", ""), - ("Enable random password", ""), - ("Enable onetime password", ""), - ("Disable onetime password", ""), - ("Activate onetime password", ""), - ("Set security password", ""), - ("Connection not allowed", ""), + ("Keep RustDesk background service", "Сохранить фоновый службу RustDesk"), + ("Ignore Battery Optimizations", "Игнорировать оптимизацию батареи"), + ("android_open_battery_optimizations_tip", "Перейдите на следующую страницу настроек "), + ("Random Password After Session", "Случайный пароль после сеанса"), + ("Keep", "Оставить"), + ("Update", "Обновить"), + ("Disable", "Отключить"), + ("Onetime Password", "Одноразовый пароль"), + ("Verification Method", "Метод верификации"), + ("Enable security password", "Включить пароль безопасности"), + ("Enable random password", "Включить случайный пароль"), + ("Enable onetime password", "Включить одноразовый пароль"), + ("Disable onetime password", "Отключить одноразовый пароль"), + ("Activate onetime password", "Активировать одноразовый пароль"), + ("Set security password", "Задать пароль безопасности"), + ("Connection not allowed", "Подключение не разрешено"), ].iter().cloned().collect(); } From aae6e2b16bb47a2b32bcbbd7ee7e18501d71a728 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 7 Jul 2022 01:27:21 +0800 Subject: [PATCH 42/50] linux_wayland_support: init merge, windows build Signed-off-by: fufesou --- .gitignore | 1 + Cargo.lock | 59 ++ Cargo.toml | 6 +- DEBIAN/postinst | 8 +- DEBIAN/preinst | 7 + DEBIAN/prerm | 9 +- build.py | 5 +- libs/enigo/src/lib.rs | 4 +- libs/enigo/src/linux/mod.rs | 37 ++ libs/enigo/src/linux/nix_impl.rs | 178 ++++++ libs/enigo/src/linux/pynput.rs | 280 +++++++++ libs/enigo/src/{linux.rs => linux/xdo.rs} | 250 +------- libs/scrap/src/common/dxgi.rs | 13 +- libs/scrap/src/common/linux.rs | 15 +- libs/scrap/src/common/mod.rs | 1 + libs/scrap/src/common/wayland.rs | 10 +- libs/scrap/src/common/x11.rs | 4 + libs/scrap/src/dxgi/mag.rs | 4 + libs/scrap/src/dxgi/mod.rs | 4 + libs/scrap/src/x11/capturer.rs | 4 + pynput_service.py | 236 -------- rustdesk.service | 2 +- rustdesk.service.user | 13 + src/ipc.rs | 44 ++ src/main.rs | 4 + src/platform/linux.rs | 227 ++++--- src/server.rs | 2 + src/server/connection.rs | 32 +- src/server/input_service.rs | 687 +++++++++++++++++++++- src/server/video_service.rs | 412 ++++++++++--- 30 files changed, 1902 insertions(+), 656 deletions(-) create mode 100644 libs/enigo/src/linux/mod.rs create mode 100644 libs/enigo/src/linux/nix_impl.rs create mode 100644 libs/enigo/src/linux/pynput.rs rename libs/enigo/src/{linux.rs => linux/xdo.rs} (54%) delete mode 100644 pynput_service.py create mode 100644 rustdesk.service.user diff --git a/.gitignore b/.gitignore index 5b26711c5..53bd9cf94 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .vscode .idea .DS_Store +libsciter-gtk.so src/ui/inline.rs extractor __pycache__ diff --git a/Cargo.lock b/Cargo.lock index bbd8edfba..743f0258c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -317,6 +317,18 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitvec" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1489fcb93a5bb47da0462ca93ad252ad6af2145cce58d10d46a83931ba9f016b" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "block" version = "0.1.6" @@ -1323,6 +1335,16 @@ dependencies = [ "str-buf", ] +[[package]] +name = "evdev" +version = "0.11.5" +source = "git+https://github.com/fufesou/evdev#cec616e37790293d2cd2aa54a96601ed6b1b35a9" +dependencies = [ + "bitvec", + "libc", + "nix 0.23.1", +] + [[package]] name = "event-listener" version = "2.5.2" @@ -1498,6 +1520,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.3.21" @@ -2741,6 +2769,14 @@ dependencies = [ "windows-sys 0.28.0", ] +[[package]] +name = "mouce" +version = "0.2.1" +source = "git+https://github.com/fufesou/mouce.git#7da9d9b6597f4c4461881deb4ed49da2385e3cac" +dependencies = [ + "glob", +] + [[package]] name = "muldiv" version = "0.2.1" @@ -3527,6 +3563,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.6.5" @@ -3933,6 +3975,7 @@ dependencies = [ "default-net", "dispatch", "enigo", + "evdev", "flexi_logger", "flutter_rust_bridge", "flutter_rust_bridge_codegen", @@ -3947,6 +3990,7 @@ dependencies = [ "mac_address", "machine-uid", "magnum-opus", + "mouce", "num_cpus", "objc", "parity-tokio-ipc", @@ -4587,6 +4631,12 @@ dependencies = [ "version-compare 0.1.0", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "target_build_utils" version = "0.3.1" @@ -5542,6 +5592,15 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "wyz" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b31594f29d27036c383b53b59ed3476874d518f0efb151b27a4c275141390e" +dependencies = [ + "tap", +] + [[package]] name = "x11" version = "2.19.1" diff --git a/Cargo.toml b/Cargo.toml index 87fc0f169..2b7f4391c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ default = ["use_dasp"] [dependencies] whoami = "1.2" -scrap = { path = "libs/scrap" } +scrap = { path = "libs/scrap", features = ["wayland"] } hbb_common = { path = "libs/hbb_common" } serde_derive = "1.0" serde = "1.0" @@ -69,7 +69,7 @@ machine-uid = "0.2" mac_address = "1.1" sciter-rs = { git = "https://github.com/open-trade/rust-sciter", branch = "dyn" } sys-locale = "0.2" -enigo = { path = "libs/enigo" } +enigo = { path = "libs/enigo", features = [ "with_serde" ] } clipboard = { path = "libs/clipboard" } rdev = { git = "https://github.com/open-trade/rdev" } ctrlc = "3.2" @@ -99,6 +99,8 @@ psimple = { package = "libpulse-simple-binding", version = "2.25" } pulse = { package = "libpulse-binding", version = "2.26" } rust-pulsectl = { git = "https://github.com/open-trade/pulsectl" } async-process = "1.3" +mouce = { git="https://github.com/fufesou/mouce.git" } +evdev = { git="https://github.com/fufesou/evdev" } [target.'cfg(target_os = "android")'.dependencies] android_logger = "0.11" diff --git a/DEBIAN/postinst b/DEBIAN/postinst index 5899bd4df..1c7697acc 100644 --- a/DEBIAN/postinst +++ b/DEBIAN/postinst @@ -8,16 +8,20 @@ if [ "$1" = configure ]; then if [ "systemd" == "$INITSYS" ]; then if [ -e /etc/systemd/system/rustdesk.service ]; then - rm /etc/systemd/system/rustdesk.service + rm /etc/systemd/system/rustdesk.service /usr/lib/systemd/system/rustdesk.service /usr/lib/systemd/user/rustdesk.service >/dev/null 2>&1 fi version=$(python3 -V 2>&1 | grep -Po '(?<=Python )(.+)') parsedVersion=$(echo "${version//./}") if [[ "$parsedVersion" -gt "360" ]]; then sudo -H pip3 install pynput fi - cp /usr/share/rustdesk/files/systemd/rustdesk.service /etc/systemd/system/rustdesk.service + cp /usr/share/rustdesk/files/systemd/rustdesk.service /usr/lib/systemd/system/rustdesk.service systemctl daemon-reload systemctl enable rustdesk systemctl start rustdesk + + cp /usr/share/rustdesk/files/systemd/rustdesk.service.user /usr/lib/systemd/user/rustdesk.service + curUser=$(who | awk '{print $1}' | head -1) + systemctl --machine=${curUser}@.host --user daemon-reload fi fi diff --git a/DEBIAN/preinst b/DEBIAN/preinst index 8b73e9962..7fbedca4a 100644 --- a/DEBIAN/preinst +++ b/DEBIAN/preinst @@ -7,6 +7,13 @@ case $1 in INITSYS=$(ls -al /proc/1/exe | awk -F' ' '{print $NF}' | awk -F'/' '{print $NF}') if [ "systemd" == "${INITSYS}" ]; then service rustdesk stop || true + + serverUser=$(ps -ef | grep -E 'rustdesk +--server' | awk '{print $1}' | head -1) + if [ "$serverUser" != "" ] && [ "$serverUser" != "root" ] + then + systemctl --machine=${serverUser}@.host --user stop rustdesk || true + fi + sleep 1 rm -rf /usr/bin/libsciter-gtk.so fi diff --git a/DEBIAN/prerm b/DEBIAN/prerm index 865b689ab..3bb453198 100644 --- a/DEBIAN/prerm +++ b/DEBIAN/prerm @@ -8,7 +8,14 @@ case $1 in if [ "systemd" == "${INITSYS}" ]; then systemctl stop rustdesk || true systemctl disable rustdesk || true - rm /etc/systemd/system/rustdesk.service || true + + serverUser=$(ps -ef | grep -E 'rustdesk +--server' | awk '{print $1}' | head -1) + if [ "$serverUser" != "" ] && [ "$serverUser" != "root" ] + then + systemctl --machine=${serverUser}@.host --user stop rustdesk || true + fi + + rm /etc/systemd/system/rustdesk.service /usr/lib/systemd/system/rustdesk.service /usr/lib/systemd/user/rustdesk.service || true fi ;; esac diff --git a/build.py b/build.py index 2b7cd3f27..efa6f7831 100644 --- a/build.py +++ b/build.py @@ -209,12 +209,15 @@ rcodesign notarize --api-issuer 69a6de7d-2907-47e3-e053-5b8c7c11a4d1 --api-key 9 os.system('mkdir -p tmpdeb/usr/share/rustdesk/files/systemd/') os.system( 'cp rustdesk.service tmpdeb/usr/share/rustdesk/files/systemd/') + os.system( + 'cp rustdesk.service.user tmpdeb/usr/share/rustdesk/files/systemd/') os.system('cp pynput_service.py tmpdeb/usr/share/rustdesk/files/') - os.system('cp DEBIAN/* tmpdeb/DEBIAN/') + os.system('cp -a DEBIAN/* tmpdeb/DEBIAN/') os.system('strip tmpdeb/usr/bin/rustdesk') os.system('mkdir -p tmpdeb/usr/lib/rustdesk') os.system('cp libsciter-gtk.so tmpdeb/usr/lib/rustdesk/') md5_file('usr/share/rustdesk/files/systemd/rustdesk.service') + md5_file('usr/share/rustdesk/files/systemd/rustdesk.service.user') md5_file('usr/share/rustdesk/files/pynput_service.py') md5_file('usr/lib/rustdesk/libsciter-gtk.so') os.system('dpkg-deb -b tmpdeb rustdesk.deb; /bin/rm -rf tmpdeb/') diff --git a/libs/enigo/src/lib.rs b/libs/enigo/src/lib.rs index 10cde9cbe..40ba1c1db 100644 --- a/libs/enigo/src/lib.rs +++ b/libs/enigo/src/lib.rs @@ -74,7 +74,7 @@ pub use macos::Enigo; #[cfg(target_os = "linux")] mod linux; #[cfg(target_os = "linux")] -pub use crate::linux::Enigo; +pub use crate::linux::{is_x11, Enigo}; /// DSL parser module pub mod dsl; @@ -249,7 +249,7 @@ pub trait MouseControllable { /// For alphabetical keys, use Key::Layout for a system independent key. /// If a key is missing, you can use the raw keycode with Key::Raw. #[cfg_attr(feature = "with_serde", derive(Serialize, Deserialize))] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum Key { /// alt key on Linux and Windows (option key on macOS) Alt, diff --git a/libs/enigo/src/linux/mod.rs b/libs/enigo/src/linux/mod.rs new file mode 100644 index 000000000..3c0784660 --- /dev/null +++ b/libs/enigo/src/linux/mod.rs @@ -0,0 +1,37 @@ +mod nix_impl; +mod pynput; +mod xdo; + +pub use self::nix_impl::Enigo; + +/// Check if display manager is x11. +pub fn is_x11() -> bool { + let stdout = + match std::process::Command::new("sh") + .arg("-c") + .arg("loginctl show-session $(loginctl | awk '/tty/ {print $1}') -p Type | awk -F= '{print $2}'") + .output() { + Ok(output) => { + output.stdout + }, + Err(_) => { + match std::process::Command::new("sh") + .arg("-c") + .arg("echo $XDG_SESSION_TYPE") + .output() { + Ok(output) => { + output.stdout + }, + Err(_) => { + return false; + } + } + } + }; + + if let Ok(display_manager) = std::str::from_utf8(&stdout) { + display_manager.trim() == "x11" + } else { + false + } +} diff --git a/libs/enigo/src/linux/nix_impl.rs b/libs/enigo/src/linux/nix_impl.rs new file mode 100644 index 000000000..332b6d16e --- /dev/null +++ b/libs/enigo/src/linux/nix_impl.rs @@ -0,0 +1,178 @@ +use super::{pynput::EnigoPynput, xdo::EnigoXdo}; +use crate::{Key, KeyboardControllable, MouseButton, MouseControllable}; + +/// The main struct for handling the event emitting +// #[derive(Default)] +pub struct Enigo { + xdo: EnigoXdo, + pynput: EnigoPynput, + is_x11: bool, + uinput_keyboard: Option>, + uinput_mouse: Option>, +} + +impl Enigo { + /// Get delay of xdo implementation. + pub fn delay(&self) -> u64 { + self.xdo.delay() + } + /// Set delay of xdo implemetation. + pub fn set_delay(&mut self, delay: u64) { + self.xdo.set_delay(delay) + } + /// Reset pynput. + pub fn reset(&mut self) { + self.pynput.reset(); + } + /// Set uinput keyboard. + pub fn set_uinput_keyboard( + &mut self, + uinput_keyboard: Option>, + ) { + self.uinput_keyboard = uinput_keyboard + } + /// Set uinput mouse. + pub fn set_uinput_mouse(&mut self, uinput_mouse: Option>) { + self.uinput_mouse = uinput_mouse + } +} + +impl Default for Enigo { + fn default() -> Self { + Self { + is_x11: crate::linux::is_x11(), + uinput_keyboard: None, + uinput_mouse: None, + xdo: EnigoXdo::default(), + pynput: EnigoPynput::default(), + } + } +} + +impl MouseControllable for Enigo { + fn mouse_move_to(&mut self, x: i32, y: i32) { + if self.is_x11 { + self.xdo.mouse_move_to(x, y); + } else { + if let Some(mouse) = &mut self.uinput_mouse { + mouse.mouse_move_to(x, y) + } + } + } + fn mouse_move_relative(&mut self, x: i32, y: i32) { + if self.is_x11 { + self.xdo.mouse_move_relative(x, y); + } else { + if let Some(mouse) = &mut self.uinput_mouse { + mouse.mouse_move_relative(x, y) + } + } + } + fn mouse_down(&mut self, button: MouseButton) -> crate::ResultType { + if self.is_x11 { + self.xdo.mouse_down(button) + } else { + if let Some(mouse) = &mut self.uinput_mouse { + mouse.mouse_down(button) + } else { + Ok(()) + } + } + } + fn mouse_up(&mut self, button: MouseButton) { + if self.is_x11 { + self.xdo.mouse_up(button) + } else { + if let Some(mouse) = &mut self.uinput_mouse { + mouse.mouse_up(button) + } + } + } + fn mouse_click(&mut self, button: MouseButton) { + if self.is_x11 { + self.xdo.mouse_click(button) + } else { + if let Some(mouse) = &mut self.uinput_mouse { + mouse.mouse_click(button) + } + } + } + fn mouse_scroll_x(&mut self, length: i32) { + if self.is_x11 { + self.xdo.mouse_scroll_x(length) + } else { + if let Some(mouse) = &mut self.uinput_mouse { + mouse.mouse_scroll_x(length) + } + } + } + fn mouse_scroll_y(&mut self, length: i32) { + if self.is_x11 { + self.xdo.mouse_scroll_y(length) + } else { + if let Some(mouse) = &mut self.uinput_mouse { + mouse.mouse_scroll_y(length) + } + } + } +} + +impl KeyboardControllable for Enigo { + fn get_key_state(&mut self, key: Key) -> bool { + if self.is_x11 { + self.xdo.get_key_state(key) + } else { + if let Some(keyboard) = &mut self.uinput_keyboard { + keyboard.get_key_state(key) + } else { + false + } + } + } + + fn key_sequence(&mut self, sequence: &str) { + if self.is_x11 { + self.xdo.key_sequence(sequence) + } else { + if let Some(keyboard) = &mut self.uinput_keyboard { + keyboard.key_sequence(sequence) + } + } + } + + fn key_down(&mut self, key: Key) -> crate::ResultType { + if self.is_x11 { + if self.pynput.send_pynput(&key, true) { + return Ok(()); + } + self.xdo.key_down(key) + } else { + if let Some(keyboard) = &mut self.uinput_keyboard { + keyboard.key_down(key) + } else { + Ok(()) + } + } + } + fn key_up(&mut self, key: Key) { + if self.is_x11 { + if self.pynput.send_pynput(&key, false) { + return; + } + self.xdo.key_up(key) + } else { + if let Some(keyboard) = &mut self.uinput_keyboard { + keyboard.key_up(key) + } + } + } + fn key_click(&mut self, key: Key) { + if self.is_x11 { + self.xdo.key_click(key) + } else { + if let Some(keyboard) = &mut self.uinput_keyboard { + keyboard.key_click(key) + } + } + } +} diff --git a/libs/enigo/src/linux/pynput.rs b/libs/enigo/src/linux/pynput.rs new file mode 100644 index 000000000..748b30105 --- /dev/null +++ b/libs/enigo/src/linux/pynput.rs @@ -0,0 +1,280 @@ +use crate::Key; +use std::{io::prelude::*, sync::mpsc}; + +enum PyMsg { + Char(char), + Str(&'static str), +} + +/// The main struct for handling the event emitting +pub(super) struct EnigoPynput { + tx: mpsc::Sender<(PyMsg, bool)>, +} + +impl Default for EnigoPynput { + fn default() -> Self { + let (tx, rx) = mpsc::channel(); + start_pynput_service(rx); + Self { tx } + } +} +impl EnigoPynput { + pub(super) fn reset(&mut self) { + self.tx.send((PyMsg::Char('\0'), true)).ok(); + } + + #[inline] + pub(super) fn send_pynput(&mut self, key: &Key, is_press: bool) -> bool { + if unsafe { PYNPUT_EXIT || !PYNPUT_REDAY } { + return false; + } + if let Key::Layout(c) = key { + return self.tx.send((PyMsg::Char(*c), is_press)).is_ok(); + } + if let Key::Raw(_) = key { + return false; + } + #[allow(deprecated)] + let s = match key { + Key::Alt => "Alt_L", + Key::Backspace => "BackSpace", + Key::CapsLock => "Caps_Lock", + Key::Control => "Control_L", + Key::Delete => "Delete", + Key::DownArrow => "Down", + Key::End => "End", + Key::Escape => "Escape", + Key::F1 => "F1", + Key::F10 => "F10", + Key::F11 => "F11", + Key::F12 => "F12", + Key::F2 => "F2", + Key::F3 => "F3", + Key::F4 => "F4", + Key::F5 => "F5", + Key::F6 => "F6", + Key::F7 => "F7", + Key::F8 => "F8", + Key::F9 => "F9", + Key::Home => "Home", + Key::LeftArrow => "Left", + Key::Option => "Option", + Key::PageDown => "Page_Down", + Key::PageUp => "Page_Up", + Key::Return => "Return", + Key::RightArrow => "Right", + Key::Shift => "Shift_L", + Key::Space => "space", + Key::Tab => "Tab", + Key::UpArrow => "Up", + Key::Numpad0 => "0", + Key::Numpad1 => "1", + Key::Numpad2 => "2", + Key::Numpad3 => "3", + Key::Numpad4 => "4", + Key::Numpad5 => "5", + Key::Numpad6 => "6", + Key::Numpad7 => "7", + Key::Numpad8 => "8", + Key::Numpad9 => "9", + Key::Decimal => "KP_Decimal", + Key::Cancel => "Cancel", + Key::Clear => "Clear", + Key::Pause => "Pause", + Key::Kana => "Kana", + Key::Hangul => "Hangul", + Key::Hanja => "Hanja", + Key::Kanji => "Kanji", + Key::Select => "Select", + Key::Print => "Print", + Key::Execute => "Execute", + Key::Snapshot => "3270_PrintScreen", + Key::Insert => "Insert", + Key::Help => "Help", + Key::Separator => "KP_Separator", + Key::Scroll => "Scroll_Lock", + Key::NumLock => "Num_Lock", + Key::RWin => "Super_R", + Key::Apps => "Menu", + Key::Multiply => "KP_Multiply", + Key::Add => "KP_Add", + Key::Subtract => "KP_Subtract", + Key::Divide => "KP_Divide", + Key::Equals => "KP_Equal", + Key::NumpadEnter => "KP_Enter", + Key::RightShift => "Shift_R", + Key::RightControl => "Control_R", + Key::RightAlt => "Mode_switch", + Key::Command | Key::Super | Key::Windows | Key::Meta => "Super_L", + _ => { + return true; + } + }; + log::info!("send pynput: {:?}", &s); + return self.tx.send((PyMsg::Str(s), is_press)).is_ok(); + } +} + +// impl MouseControllable for EnigoPynput { +// fn mouse_move_to(&mut self, _x: i32, _y: i32) { +// unimplemented!() +// } +// fn mouse_move_relative(&mut self, _x: i32, _y: i32) { +// unimplemented!() +// } +// fn mouse_down(&mut self, _button: MouseButton) -> crate::ResultType { +// unimplemented!() +// } +// fn mouse_up(&mut self, _button: MouseButton) { +// unimplemented!() +// } +// fn mouse_click(&mut self, _button: MouseButton) { +// unimplemented!() +// } +// fn mouse_scroll_x(&mut self, _length: i32) { +// unimplemented!() +// } +// fn mouse_scroll_y(&mut self, _length: i32) { +// unimplemented!() +// } +// } + +// impl KeyboardControllable for EnigoPynput { +// fn get_key_state(&mut self, _key: Key) -> bool { +// unimplemented!() +// } + +// fn key_sequence(&mut self, _sequence: &str) { +// unimplemented!() +// } +// fn key_down(&mut self, key: Key) -> crate::ResultType { +// let _ = self.send_pynput(&key, true); +// Ok(()) +// } +// fn key_up(&mut self, key: Key) { +// let _ = self.send_pynput(&key, false); +// } +// fn key_click(&mut self, _key: Key) { +// unimplemented!() +// } +// } + +static mut PYNPUT_EXIT: bool = false; +static mut PYNPUT_REDAY: bool = false; +static IPC_FILE: &'static str = "/tmp/RustDesk/pynput_service"; + +fn start_pynput_service(rx: mpsc::Receiver<(PyMsg, bool)>) { + let mut py = "./pynput_service.py".to_owned(); + if !std::path::Path::new(&py).exists() { + py = "/usr/share/rustdesk/files/pynput_service.py".to_owned(); + if !std::path::Path::new(&py).exists() { + py = "/usr/lib/rustdesk/pynput_service.py".to_owned(); + if !std::path::Path::new(&py).exists() { + log::error!("{} not exits", py); + } + } + } + log::info!("pynput service: {}", py); + std::thread::spawn(move || { + let username = std::env::var("PYNPUT_USERNAME").unwrap_or("".to_owned()); + let userid = std::env::var("PYNPUT_USERID").unwrap_or("".to_owned()); + let status = if username.is_empty() { + std::process::Command::new("python3") + .arg(&py) + .arg(IPC_FILE) + .status() + .map(|x| x.success()) + } else { + let mut status = Ok(true); + for i in 0..100 { + if i % 10 == 0 { + log::info!("#{} try to start pynput server", i); + } + status = std::process::Command::new("sudo") + .args(vec![ + "-E", + &format!("XDG_RUNTIME_DIR=/run/user/{}", userid) as &str, + "-u", + &username, + "python3", + &py, + IPC_FILE, + ]) + .status() + .map(|x| x.success()); + match status { + Ok(true) => break, + _ => {} + } + std::thread::sleep(std::time::Duration::from_millis(100)); + } + status + }; + log::info!( + "pynput server exit with username/id {}/{}: {:?}", + username, + userid, + status + ); + unsafe { + PYNPUT_EXIT = true; + } + }); + std::thread::spawn(move || { + for i in 0..300 { + std::thread::sleep(std::time::Duration::from_millis(100)); + let mut conn = match std::os::unix::net::UnixStream::connect(IPC_FILE) { + Ok(conn) => conn, + Err(err) => { + if i % 15 == 0 { + log::warn!("Failed to connect to {}: {}", IPC_FILE, err); + } + continue; + } + }; + if let Err(err) = conn.set_nonblocking(true) { + log::error!("Failed to set ipc nonblocking: {}", err); + return; + } + log::info!("Conntected to pynput server"); + let d = std::time::Duration::from_millis(30); + unsafe { + PYNPUT_REDAY = true; + } + let mut buf = [0u8; 1024]; + loop { + if unsafe { PYNPUT_EXIT } { + break; + } + match rx.recv_timeout(d) { + Ok((msg, is_press)) => { + let msg = match msg { + PyMsg::Char(chr) => { + format!("{}{}", if is_press { 'p' } else { 'r' }, chr) + } + PyMsg::Str(s) => format!("{}{}", if is_press { 'p' } else { 'r' }, s), + }; + let n = msg.len(); + buf[0] = n as _; + buf[1..(n + 1)].copy_from_slice(msg.as_bytes()); + if let Err(err) = conn.write_all(&buf[..n + 1]) { + log::error!("Failed to write to ipc: {}", err); + break; + } + } + Err(err) => match err { + mpsc::RecvTimeoutError::Disconnected => { + log::error!("pynput sender disconnecte"); + break; + } + _ => {} + }, + } + } + unsafe { + PYNPUT_REDAY = false; + } + break; + } + }); +} diff --git a/libs/enigo/src/linux.rs b/libs/enigo/src/linux/xdo.rs similarity index 54% rename from libs/enigo/src/linux.rs rename to libs/enigo/src/linux/xdo.rs index adfe9507c..0e3b79ab3 100644 --- a/libs/enigo/src/linux.rs +++ b/libs/enigo/src/linux/xdo.rs @@ -3,7 +3,7 @@ use libc; use crate::{Key, KeyboardControllable, MouseButton, MouseControllable}; use self::libc::{c_char, c_int, c_void, useconds_t}; -use std::{borrow::Cow, ffi::CString, io::prelude::*, ptr, sync::mpsc}; +use std::{borrow::Cow, ffi::CString, ptr}; const CURRENT_WINDOW: c_int = 0; const DEFAULT_DELAY: u64 = 12000; @@ -60,34 +60,25 @@ fn mousebutton(button: MouseButton) -> c_int { } } -enum PyMsg { - Char(char), - Str(&'static str), -} - /// The main struct for handling the event emitting -pub struct Enigo { +pub(super) struct EnigoXdo { xdo: Xdo, delay: u64, - tx: mpsc::Sender<(PyMsg, bool)>, } // This is safe, we have a unique pointer. // TODO: use Unique once stable. -unsafe impl Send for Enigo {} +unsafe impl Send for EnigoXdo {} -impl Default for Enigo { - /// Create a new Enigo instance +impl Default for EnigoXdo { + /// Create a new EnigoXdo instance fn default() -> Self { - let (tx, rx) = mpsc::channel(); - start_pynput_service(rx); Self { xdo: unsafe { xdo_new(ptr::null()) }, delay: DEFAULT_DELAY, - tx, } } } -impl Enigo { +impl EnigoXdo { /// Get the delay per keypress. /// Default value is 12000. /// This is Linux-specific. @@ -99,101 +90,8 @@ impl Enigo { pub fn set_delay(&mut self, delay: u64) { self.delay = delay; } - /// - pub fn reset(&mut self) { - self.tx.send((PyMsg::Char('\0'), true)).ok(); - } - #[inline] - fn send_pynput(&mut self, key: &Key, is_press: bool) -> bool { - if unsafe { PYNPUT_EXIT || !PYNPUT_REDAY } { - return false; - } - if let Key::Layout(c) = key { - return self.tx.send((PyMsg::Char(*c), is_press)).is_ok(); - } - if let Key::Raw(_) = key { - return false; - } - #[allow(deprecated)] - let s = match key { - Key::Alt => "Alt_L", - Key::Backspace => "BackSpace", - Key::CapsLock => "Caps_Lock", - Key::Control => "Control_L", - Key::Delete => "Delete", - Key::DownArrow => "Down", - Key::End => "End", - Key::Escape => "Escape", - Key::F1 => "F1", - Key::F10 => "F10", - Key::F11 => "F11", - Key::F12 => "F12", - Key::F2 => "F2", - Key::F3 => "F3", - Key::F4 => "F4", - Key::F5 => "F5", - Key::F6 => "F6", - Key::F7 => "F7", - Key::F8 => "F8", - Key::F9 => "F9", - Key::Home => "Home", - Key::LeftArrow => "Left", - Key::Option => "Option", - Key::PageDown => "Page_Down", - Key::PageUp => "Page_Up", - Key::Return => "Return", - Key::RightArrow => "Right", - Key::Shift => "Shift_L", - Key::Space => "space", - Key::Tab => "Tab", - Key::UpArrow => "Up", - Key::Numpad0 => "0", - Key::Numpad1 => "1", - Key::Numpad2 => "2", - Key::Numpad3 => "3", - Key::Numpad4 => "4", - Key::Numpad5 => "5", - Key::Numpad6 => "6", - Key::Numpad7 => "7", - Key::Numpad8 => "8", - Key::Numpad9 => "9", - Key::Decimal => "KP_Decimal", - Key::Cancel => "Cancel", - Key::Clear => "Clear", - Key::Pause => "Pause", - Key::Kana => "Kana", - Key::Hangul => "Hangul", - Key::Hanja => "Hanja", - Key::Kanji => "Kanji", - Key::Select => "Select", - Key::Print => "Print", - Key::Execute => "Execute", - Key::Snapshot => "3270_PrintScreen", - Key::Insert => "Insert", - Key::Help => "Help", - Key::Separator => "KP_Separator", - Key::Scroll => "Scroll_Lock", - Key::NumLock => "Num_Lock", - Key::RWin => "Super_R", - Key::Apps => "Menu", - Key::Multiply => "KP_Multiply", - Key::Add => "KP_Add", - Key::Subtract => "KP_Subtract", - Key::Divide => "KP_Divide", - Key::Equals => "KP_Equal", - Key::NumpadEnter => "KP_Enter", - Key::RightShift => "Shift_R", - Key::RightControl => "Control_R", - Key::RightAlt => "Mode_switch", - Key::Command | Key::Super | Key::Windows | Key::Meta => "Super_L", - _ => { - return true; - } - }; - return self.tx.send((PyMsg::Str(s), is_press)).is_ok(); - } } -impl Drop for Enigo { +impl Drop for EnigoXdo { fn drop(&mut self) { if self.xdo.is_null() { return; @@ -203,7 +101,7 @@ impl Drop for Enigo { } } } -impl MouseControllable for Enigo { +impl MouseControllable for EnigoXdo { fn mouse_move_to(&mut self, x: i32, y: i32) { if self.xdo.is_null() { return; @@ -378,7 +276,7 @@ fn keysequence<'a>(key: Key) -> Cow<'a, str> { _ => "", }) } -impl KeyboardControllable for Enigo { +impl KeyboardControllable for EnigoXdo { fn get_key_state(&mut self, key: Key) -> bool { if self.xdo.is_null() { return false; @@ -431,9 +329,6 @@ impl KeyboardControllable for Enigo { if self.xdo.is_null() { return Ok(()); } - if self.send_pynput(&key, true) { - return Ok(()); - } let string = CString::new(&*keysequence(key))?; unsafe { xdo_send_keysequence_window_down( @@ -449,9 +344,6 @@ impl KeyboardControllable for Enigo { if self.xdo.is_null() { return; } - if self.send_pynput(&key, false) { - return; - } if let Ok(string) = CString::new(&*keysequence(key)) { unsafe { xdo_send_keysequence_window_up( @@ -479,127 +371,3 @@ impl KeyboardControllable for Enigo { } } } - -static mut PYNPUT_EXIT: bool = false; -static mut PYNPUT_REDAY: bool = false; -static IPC_FILE: &'static str = "/tmp/RustDesk/pynput_service"; - -fn start_pynput_service(rx: mpsc::Receiver<(PyMsg, bool)>) { - let mut py = "./pynput_service.py".to_owned(); - if !std::path::Path::new(&py).exists() { - py = "/usr/share/rustdesk/files/pynput_service.py".to_owned(); - if !std::path::Path::new(&py).exists() { - py = "/usr/lib/rustdesk/pynput_service.py".to_owned(); - if !std::path::Path::new(&py).exists() { - // enigo libs, not rustdesk root project, so skip using appimage features - py = std::env::var("APPDIR").unwrap_or("".to_string()) + "/usr/lib/rustdesk/pynput_service.py"; - if !std::path::Path::new(&py).exists() { - log::error!("{} not exists", py); - } - } - } - } - log::info!("pynput service: {}", py); - std::thread::spawn(move || { - let username = std::env::var("PYNPUT_USERNAME").unwrap_or("".to_owned()); - let userid = std::env::var("PYNPUT_USERID").unwrap_or("".to_owned()); - let status = if username.is_empty() { - std::process::Command::new("python3") - .arg(&py) - .arg(IPC_FILE) - .status() - .map(|x| x.success()) - } else { - let mut status = Ok(true); - for i in 0..100 { - if i % 10 == 0 { - log::info!("#{} try to start pynput server", i); - } - status = std::process::Command::new("sudo") - .args(vec![ - "-E", - &format!("XDG_RUNTIME_DIR=/run/user/{}", userid) as &str, - "-u", - &username, - "python3", - &py, - IPC_FILE, - ]) - .status() - .map(|x| x.success()); - match status { - Ok(true) => break, - _ => {} - } - std::thread::sleep(std::time::Duration::from_millis(100)); - } - status - }; - log::info!( - "pynput server exit with username/id {}/{}: {:?}", - username, - userid, - status - ); - unsafe { - PYNPUT_EXIT = true; - } - }); - std::thread::spawn(move || { - for i in 0..300 { - std::thread::sleep(std::time::Duration::from_millis(100)); - let mut conn = match std::os::unix::net::UnixStream::connect(IPC_FILE) { - Ok(conn) => conn, - Err(err) => { - if i % 15 == 0 { - log::warn!("Failed to connect to {}: {}", IPC_FILE, err); - } - continue; - } - }; - if let Err(err) = conn.set_nonblocking(true) { - log::error!("Failed to set ipc nonblocking: {}", err); - return; - } - log::info!("Conntected to pynput server"); - let d = std::time::Duration::from_millis(30); - unsafe { - PYNPUT_REDAY = true; - } - let mut buf = [0u8; 1024]; - loop { - if unsafe { PYNPUT_EXIT } { - break; - } - match rx.recv_timeout(d) { - Ok((msg, is_press)) => { - let msg = match msg { - PyMsg::Char(chr) => { - format!("{}{}", if is_press { 'p' } else { 'r' }, chr) - } - PyMsg::Str(s) => format!("{}{}", if is_press { 'p' } else { 'r' }, s), - }; - let n = msg.len(); - buf[0] = n as _; - buf[1..(n + 1)].copy_from_slice(msg.as_bytes()); - if let Err(err) = conn.write_all(&buf[..n + 1]) { - log::error!("Failed to write to ipc: {}", err); - break; - } - } - Err(err) => match err { - mpsc::RecvTimeoutError::Disconnected => { - log::error!("pynput sender disconnecte"); - break; - } - _ => {} - }, - } - } - unsafe { - PYNPUT_REDAY = false; - } - break; - } - }); -} diff --git a/libs/scrap/src/common/dxgi.rs b/libs/scrap/src/common/dxgi.rs index 1a8c39885..855ac7ac3 100644 --- a/libs/scrap/src/common/dxgi.rs +++ b/libs/scrap/src/common/dxgi.rs @@ -21,6 +21,10 @@ impl Capturer { }) } + pub fn set_use_yuv(&mut self, use_yuv: bool) { + self.inner.set_use_yuv(use_yuv); + } + pub fn is_gdi(&self) -> bool { self.inner.is_gdi() } @@ -41,8 +45,8 @@ impl Capturer { self.height } - pub fn frame<'a>(&'a mut self, timeout_ms: Duration) -> io::Result> { - match self.inner.frame(timeout_ms.as_millis() as _) { + pub fn frame<'a>(&'a mut self, timeout: Duration) -> io::Result> { + match self.inner.frame(timeout.as_millis() as _) { Ok(frame) => Ok(Frame(frame)), Err(ref error) if error.kind() == TimedOut => Err(WouldBlock.into()), Err(error) => Err(error), @@ -129,6 +133,11 @@ impl CapturerMag { data: Vec::new(), }) } + + pub fn set_use_yuv(&mut self, use_yuv: bool) { + self.inner.set_use_yuv(use_yuv) + } + pub fn exclude(&mut self, cls: &str, name: &str) -> io::Result { self.inner.exclude(cls, name) } diff --git a/libs/scrap/src/common/linux.rs b/libs/scrap/src/common/linux.rs index 50bab092c..06a4ed9e0 100644 --- a/libs/scrap/src/common/linux.rs +++ b/libs/scrap/src/common/linux.rs @@ -17,6 +17,13 @@ impl Capturer { }) } + pub fn set_use_yuv(&mut self, use_yuv: bool) { + match self { + Capturer::X11(d) => d.set_use_yuv(use_yuv), + Capturer::WAYLAND(d) => d.set_use_yuv(use_yuv), + } + } + pub fn width(&self) -> usize { match self { Capturer::X11(d) => d.width(), @@ -31,10 +38,10 @@ impl Capturer { } } - pub fn frame<'a>(&'a mut self, timeout_ms: u32) -> io::Result> { + pub fn frame<'a>(&'a mut self, timeout: Duration) -> io::Result> { match self { - Capturer::X11(d) => d.frame(timeout_ms), - Capturer::WAYLAND(d) => d.frame(timeout_ms), + Capturer::X11(d) => d.frame(timeout), + Capturer::WAYLAND(d) => d.frame(timeout), } } } @@ -45,7 +52,7 @@ pub enum Display { } #[inline] -fn is_wayland() -> bool { +pub fn is_wayland() -> bool { std::env::var("IS_WAYLAND").is_ok() || std::env::var("XDG_SESSION_TYPE") == Ok("wayland".to_owned()) } diff --git a/libs/scrap/src/common/mod.rs b/libs/scrap/src/common/mod.rs index 792ea14e1..9115bfd3a 100644 --- a/libs/scrap/src/common/mod.rs +++ b/libs/scrap/src/common/mod.rs @@ -11,6 +11,7 @@ cfg_if! { mod wayland; mod x11; pub use self::linux::*; + pub use self::x11::Frame; } else { mod x11; pub use self::x11::*; diff --git a/libs/scrap/src/common/wayland.rs b/libs/scrap/src/common/wayland.rs index ff6bf8022..05bb08744 100644 --- a/libs/scrap/src/common/wayland.rs +++ b/libs/scrap/src/common/wayland.rs @@ -1,6 +1,6 @@ use crate::common::x11::Frame; use crate::wayland::{capturable::*, *}; -use std::io; +use std::{io, time::Duration}; pub struct Capturer(Display, Box, bool, Vec); @@ -14,6 +14,10 @@ impl Capturer { Ok(Capturer(display, r, yuv, Default::default())) } + pub fn set_use_yuv(&mut self, use_yuv: bool) { + self.2 = use_yuv; + } + pub fn width(&self) -> usize { self.0.width() } @@ -22,8 +26,8 @@ impl Capturer { self.0.height() } - pub fn frame<'a>(&'a mut self, timeout_ms: u32) -> io::Result> { - match self.1.capture(timeout_ms as _).map_err(map_err)? { + pub fn frame<'a>(&'a mut self, timeout: Duration) -> io::Result> { + match self.1.capture(timeout.as_millis() as _).map_err(map_err)? { PixelProvider::BGR0(w, h, x) => Ok(Frame(if self.2 { crate::common::bgra_to_i420(w as _, h as _, &x, &mut self.3); &self.3[..] diff --git a/libs/scrap/src/common/x11.rs b/libs/scrap/src/common/x11.rs index 255819902..c1a25c8d6 100644 --- a/libs/scrap/src/common/x11.rs +++ b/libs/scrap/src/common/x11.rs @@ -8,6 +8,10 @@ impl Capturer { x11::Capturer::new(display.0, yuv).map(Capturer) } + pub fn set_use_yuv(&mut self, use_yuv: bool) { + self.0.set_use_yuv(use_yuv); + } + pub fn width(&self) -> usize { self.0.display().rect().w as usize } diff --git a/libs/scrap/src/dxgi/mag.rs b/libs/scrap/src/dxgi/mag.rs index 9adf26cdb..78f14194c 100644 --- a/libs/scrap/src/dxgi/mag.rs +++ b/libs/scrap/src/dxgi/mag.rs @@ -446,6 +446,10 @@ impl CapturerMag { Ok(s) } + pub(crate) fn set_use_yuv(&mut self, use_yuv: bool) { + self.use_yuv = use_yuv; + } + pub(crate) fn exclude(&mut self, cls: &str, name: &str) -> Result { let name_c = CString::new(name).unwrap(); unsafe { diff --git a/libs/scrap/src/dxgi/mod.rs b/libs/scrap/src/dxgi/mod.rs index 46692535d..6b60b256d 100644 --- a/libs/scrap/src/dxgi/mod.rs +++ b/libs/scrap/src/dxgi/mod.rs @@ -156,6 +156,10 @@ impl Capturer { }) } + pub fn set_use_yuv(&mut self, use_yuv: bool) { + self.use_yuv = use_yuv; + } + pub fn is_gdi(&self) -> bool { self.gdi_capturer.is_some() } diff --git a/libs/scrap/src/x11/capturer.rs b/libs/scrap/src/x11/capturer.rs index 890b9db63..ed424c35a 100644 --- a/libs/scrap/src/x11/capturer.rs +++ b/libs/scrap/src/x11/capturer.rs @@ -74,6 +74,10 @@ impl Capturer { Ok(c) } + pub fn set_use_yuv(&mut self, use_yuv: bool) { + self.use_yuv = use_yuv; + } + pub fn display(&self) -> &Display { &self.display } diff --git a/pynput_service.py b/pynput_service.py deleted file mode 100644 index c51e9a524..000000000 --- a/pynput_service.py +++ /dev/null @@ -1,236 +0,0 @@ -from pynput.keyboard import Key, Controller -from pynput.keyboard._xorg import KeyCode -from pynput._util.xorg import display_manager -import Xlib -from pynput._util.xorg import * -import Xlib -import os -import sys -import socket - -KeyCode._from_symbol("\0") # test - -DEAD_KEYS = { - '`': 65104, - '´': 65105, - '^': 65106, - '~': 65107, - '¯': 65108, - '˘': 65109, - '˙': 65110, - '¨': 65111, - '˚': 65112, - '˝': 65113, - 'ˇ': 65114, - '¸': 65115, - '˛': 65116, - '℩': 65117, # ? - '゛': 65118, # ? - '゚ ': 65119, - 'ٜ': 65120, - '↪': 65121, - ' ̛': 65122, -} - - - -def my_keyboard_mapping(display): - """Generates a mapping from *keysyms* to *key codes* and required - modifier shift states. - - :param Xlib.display.Display display: The display for which to retrieve the - keyboard mapping. - - :return: the keyboard mapping - """ - mapping = {} - - shift_mask = 1 << 0 - group_mask = alt_gr_mask(display) - - # Iterate over all keysym lists in the keyboard mapping - min_keycode = display.display.info.min_keycode - keycode_count = display.display.info.max_keycode - min_keycode + 1 - for index, keysyms in enumerate(display.get_keyboard_mapping( - min_keycode, keycode_count)): - key_code = index + min_keycode - - # Normalise the keysym list to yield a tuple containing the two groups - normalized = keysym_normalize(keysyms) - if not normalized: - continue - - # Iterate over the groups to extract the shift and modifier state - for groups, group in zip(normalized, (False, True)): - for keysym, shift in zip(groups, (False, True)): - - if not keysym: - continue - shift_state = 0 \ - | (shift_mask if shift else 0) \ - | (group_mask if group else 0) - - # !!!: Save all keycode combinations of keysym - if keysym in mapping: - mapping[keysym].append((key_code, shift_state)) - else: - mapping[keysym] = [(key_code, shift_state)] - return mapping - - -class MyController(Controller): - def _update_keyboard_mapping(self): - """Updates the keyboard mapping. - """ - with display_manager(self._display) as dm: - self._keyboard_mapping = my_keyboard_mapping(dm) - - def send_event(self, event, keycode, shift_state): - with display_manager(self._display) as dm, self.modifiers as modifiers: - # Under certain cimcumstances, such as when running under Xephyr, - # the value returned by dm.get_input_focus is an int - window = dm.get_input_focus().focus - send_event = getattr( - window, - 'send_event', - lambda event: dm.send_event(window, event)) - send_event(event( - detail=keycode, - state=shift_state | self._shift_mask(modifiers), - time=0, - root=dm.screen().root, - window=window, - same_screen=0, - child=Xlib.X.NONE, - root_x=0, root_y=0, event_x=0, event_y=0)) - - def fake_input(self, keycode, is_press): - with display_manager(self._display) as dm: - Xlib.ext.xtest.fake_input( - dm, - Xlib.X.KeyPress if is_press else Xlib.X.KeyRelease, - keycode) - - 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) - - if key.vk is not None: - keycode = self._display.keysym_to_keycode(key.vk) - self.fake_input(keycode, is_press) - # Otherwise use XSendEvent; we need to use this in the general case to - # work around problems with keyboard layouts - self._emit('_on_fake_event', key, is_press) - return - - # Make sure to verify that the key was resolved - if keysym is None: - raise self.InvalidKeyException(key) - - # There may be multiple keycodes for keysym in keyboard_mapping - keycode_flag = len(self.keyboard_mapping[keysym]) == 1 - if keycode_flag: - keycode, shift_state = self.keyboard_mapping[keysym][0] - else: - keycode, shift_state = self._display.keysym_to_keycode(keysym), 0 - - keycode_set = set(map(lambda x: x[0], self.keyboard_mapping[keysym])) - # The keycode of the dead key is inconsistent, The keysym has multiple combinations of a keycode. - if keycode != self._display.keysym_to_keycode(keysym) \ - or (keycode_flag == False and keycode == list(keycode_set)[0] and len(keycode_set) == 1): - deakkey_chr = str(key).replace("'", '') - keysym = DEAD_KEYS[deakkey_chr] - keycode, shift_state = self.keyboard_mapping[keysym][0] - - # 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 - - try: - with self.modifiers as modifiers: - alt_gr = Key.alt_gr in modifiers - # !!!: Send_event can't support lock screen, this condition cann't be modified - if alt_gr: - self.send_event( - event, keycode, shift_state) - else: - self.fake_input(keycode, is_press) - 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() - -server_address = sys.argv[1] -if not os.path.exists(os.path.dirname(server_address)): - os.makedirs(os.path.dirname(server_address)) - -try: - os.unlink(server_address) -except OSError: - if os.path.exists(server_address): - raise - -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) -print("Got pynput connection") - - -def loop(): - global keyboard - buf = [] - while True: - data = clientsocket.recv(1024) - if not data: - print("Connection broken") - break - buf.extend(data) - while buf: - n = buf[0] - n = n + 1 - if len(buf) < n: - break - msg = bytearray(buf[1:n]).decode("utf-8") - buf = buf[n:] - if len(msg) < 2: - continue - if msg[1] == "\0": - keyboard = MyController() - print("Keyboard reset") - continue - if len(msg) == 2: - name = msg[1] - else: - name = KeyCode._from_symbol(msg[1:]) - if str(name) == "<0>": - continue - try: - if msg[0] == "p": - keyboard.press(name) - else: - keyboard.release(name) - except Exception as e: - print('[x] error key',e) - - -loop() -clientsocket.close() -server.close() diff --git a/rustdesk.service b/rustdesk.service index af4a3c411..e703b056f 100644 --- a/rustdesk.service +++ b/rustdesk.service @@ -6,7 +6,7 @@ After=systemd-user-sessions.service [Service] Type=simple ExecStart=/usr/bin/rustdesk --service -PIDFile=/var/run/rustdesk.pid +PIDFile=/run/rustdesk.pid KillMode=mixed TimeoutStopSec=30 User=root diff --git a/rustdesk.service.user b/rustdesk.service.user new file mode 100644 index 000000000..0756ad35e --- /dev/null +++ b/rustdesk.service.user @@ -0,0 +1,13 @@ +[Unit] +Description=RustDesk + +[Service] +Type=simple +ExecStart=/usr/bin/rustdesk --server +PIDFile=/run/rustdesk.user.pid +KillMode=mixed +TimeoutStopSec=30 +LimitNOFILE=100000 + +[Install] +WantedBy=multi-user.target diff --git a/src/ipc.rs b/src/ipc.rs index 5f2f83b89..a5615ce6a 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -96,6 +96,45 @@ pub enum FS { }, } +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(tag = "t", content = "c")] +pub enum DataKeyboard { + Sequence(String), + KeyDown(enigo::Key), + KeyUp(enigo::Key), + KeyClick(enigo::Key), + GetKeyState(enigo::Key), +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(tag = "t", content = "c")] +pub enum DataKeyboardResponse { + GetKeyState(bool), +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(tag = "t", content = "c")] +pub enum DataMouse { + MoveTo(i32, i32), + MoveRelative(i32, i32), + Down(enigo::MouseButton), + Up(enigo::MouseButton), + Click(enigo::MouseButton), + ScrollX(i32), + ScrollY(i32), +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(tag = "t", content = "c")] +pub enum DataControl { + Resolution { + minx: i32, + maxx: i32, + miny: i32, + maxy: i32, + }, +} + #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(tag = "t", content = "c")] pub enum Data { @@ -141,6 +180,11 @@ pub enum Data { PrivacyModeState((i32, PrivacyModeState)), TestRendezvousServer, Bool((String, Option)), + Keyboard(DataKeyboard), + KeyboardResponse(DataKeyboardResponse), + Mouse(DataMouse), + Control(DataControl), + Empty, } #[tokio::main(flavor = "current_thread")] diff --git a/src/main.rs b/src/main.rs index 2f30f4b4d..c8f76cbd7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -108,6 +108,10 @@ fn main() { args.len() > 1, )); return; + } else if args[0] == "--extract" { + #[cfg(feature = "with_rc")] + hbb_common::allow_err!(crate::rc::extract_resources(&args[1])); + return; } } if args[0] == "--remove" { diff --git a/src/platform/linux.rs b/src/platform/linux.rs index efd6476b6..b08793d12 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -143,7 +143,75 @@ pub fn get_cursor_data(hcursor: u64) -> ResultType { } } +fn start_uinput_service() { + use crate::server::input_service::uinput::service; + std::thread::spawn(|| { + service::start_service_control(); + }); + std::thread::spawn(|| { + service::start_service_keyboard(); + }); + std::thread::spawn(|| { + service::start_service_mouse(); + }); +} + +fn try_start_user_service(username: &str) { + if username == "" || username == "root" { + return; + } + + if let Ok(mut cur_username) = + run_cmds("ps -ef | grep -E 'rustdesk +--server' | awk '{print $1}' | head -1".to_owned()) + { + cur_username = cur_username.trim().to_owned(); + if cur_username != "root" && cur_username != username { + let _ = run_cmds(format!( + "systemctl --machine={}@.host --user stop rustdesk", + &cur_username + )); + } else if cur_username == username { + return; + } + } + + let _ = run_cmds(format!( + "systemctl --machine={}@.host --user start rustdesk", + username + )); +} + +fn try_stop_user_service() { + if let Ok(mut username) = + run_cmds("ps -ef | grep -E 'rustdesk +--server' | awk '{print $1}' | head -1".to_owned()) + { + username = username.trim().to_owned(); + if username != "root" { + let _ = run_cmds(format!( + "systemctl --machine={}@.host --user stop rustdesk", + &username + )); + } + } +} + +fn stop_server(server: &mut Option) { + if let Some(mut ps) = server.take() { + allow_err!(ps.kill()); + std::thread::sleep(std::time::Duration::from_millis(30)); + match ps.try_wait() { + Ok(Some(_status)) => {} + Ok(None) => { + let _res = ps.wait(); + } + Err(e) => log::error!("error attempting to wait: {e}"), + } + } +} + pub fn start_os_service() { + start_uinput_service(); + let running = Arc::new(AtomicBool::new(true)); let r = running.clone(); let mut uid = "".to_owned(); @@ -157,85 +225,106 @@ pub fn start_os_service() { let mut cm0 = false; let mut last_restart = std::time::Instant::now(); while running.load(Ordering::SeqCst) { - let cm = get_cm(); - let tmp = get_active_userid(); - let mut start_new = false; - if tmp != uid && !tmp.is_empty() { - uid = tmp; - log::info!("uid of seat0: {}", uid); - let gdm = format!("/run/user/{}/gdm/Xauthority", uid); - let mut auth = get_env_tries("XAUTHORITY", &uid, 10); - if auth.is_empty() { - auth = if std::path::Path::new(&gdm).exists() { - gdm - } else { - let username = get_active_username(); - if username == "root" { - format!("/{}/.Xauthority", username) + let username = get_active_username(); + let is_wayland = current_is_wayland(); + + if username == "root" || !is_wayland { + // try stop user service + try_stop_user_service(); + + // try start subprocess "--server" + let cm = get_cm(); + let tmp = get_active_userid(); + let mut start_new = false; + if tmp != uid && !tmp.is_empty() { + uid = tmp; + log::info!("uid of seat0: {}", uid); + let gdm = format!("/run/user/{}/gdm/Xauthority", uid); + let mut auth = get_env_tries("XAUTHORITY", &uid, 10); + if auth.is_empty() { + auth = if std::path::Path::new(&gdm).exists() { + gdm } else { - let tmp = format!("/home/{}/.Xauthority", username); - if std::path::Path::new(&tmp).exists() { - tmp + let username = get_active_username(); + if username == "root" { + format!("/{}/.Xauthority", username) } else { - format!("/var/lib/{}/.Xauthority", username) + let tmp = format!("/home/{}/.Xauthority", username); + if std::path::Path::new(&tmp).exists() { + tmp + } else { + format!("/var/lib/{}/.Xauthority", username) + } } - } - }; - } - let mut d = get_env("DISPLAY", &uid); - if d.is_empty() { - d = get_display(); - } - if d.is_empty() { - d = ":0".to_owned(); - } - d = d.replace(&whoami::hostname(), "").replace("localhost", ""); - log::info!("DISPLAY: {}", d); - log::info!("XAUTHORITY: {}", auth); - std::env::set_var("XAUTHORITY", auth); - std::env::set_var("DISPLAY", d); - if let Some(ps) = server.as_mut() { - allow_err!(ps.kill()); - std::thread::sleep(std::time::Duration::from_millis(30)); - last_restart = std::time::Instant::now(); - } - } else if !cm - && ((cm0 && last_restart.elapsed().as_secs() > 60) - || last_restart.elapsed().as_secs() > 3600) - { - // restart server if new connections all closed, or every one hour, - // as a workaround to resolve "SpotUdp" (dns resolve) - // and x server get displays failure issue - if let Some(ps) = server.as_mut() { - allow_err!(ps.kill()); - std::thread::sleep(std::time::Duration::from_millis(30)); - last_restart = std::time::Instant::now(); - log::info!("restart server"); - } - } - if let Some(ps) = server.as_mut() { - match ps.try_wait() { - Ok(Some(_)) => { - server = None; - start_new = true; + }; } - _ => {} + let mut d = get_env("DISPLAY", &uid); + if d.is_empty() { + d = get_display(); + } + if d.is_empty() { + d = ":0".to_owned(); + } + d = d.replace(&whoami::hostname(), "").replace("localhost", ""); + log::info!("DISPLAY: {}", d); + log::info!("XAUTHORITY: {}", auth); + std::env::set_var("XAUTHORITY", auth); + std::env::set_var("DISPLAY", d); + if let Some(ps) = server.as_mut() { + allow_err!(ps.kill()); + std::thread::sleep(std::time::Duration::from_millis(30)); + last_restart = std::time::Instant::now(); + } + } else if !cm + && ((cm0 && last_restart.elapsed().as_secs() > 60) + || last_restart.elapsed().as_secs() > 3600) + { + // restart server if new connections all closed, or every one hour, + // as a workaround to resolve "SpotUdp" (dns resolve) + // and x server get displays failure issue + if let Some(ps) = server.as_mut() { + allow_err!(ps.kill()); + std::thread::sleep(std::time::Duration::from_millis(30)); + last_restart = std::time::Instant::now(); + log::info!("restart server"); + } + } + if let Some(ps) = server.as_mut() { + match ps.try_wait() { + Ok(Some(_)) => { + server = None; + start_new = true; + } + _ => {} + } + } else { + start_new = true; + } + if start_new { + match crate::run_me(vec!["--server"]) { + Ok(ps) => server = Some(ps), + Err(err) => { + log::error!("Failed to start server: {}", err); + } + } + } + cm0 = cm; + } else if username != "" { + if username != "gdm" { + // try kill subprocess "--server" + stop_server(&mut server); + + // try start user service + try_start_user_service(&username); } } else { - start_new = true; + try_stop_user_service(); + stop_server(&mut server); } - if start_new { - match crate::run_me(vec!["--server"]) { - Ok(ps) => server = Some(ps), - Err(err) => { - log::error!("Failed to start server: {}", err); - } - } - } - cm0 = cm; std::thread::sleep(std::time::Duration::from_millis(super::SERVICE_INTERVAL)); } + try_stop_user_service(); if let Some(ps) = server.take().as_mut() { allow_err!(ps.kill()); } diff --git a/src/server.rs b/src/server.rs index d437ce6d4..f1171e61d 100644 --- a/src/server.rs +++ b/src/server.rs @@ -279,6 +279,8 @@ impl Drop for Server { for s in self.services.values() { s.join(); } + #[cfg(target_os = "linux")] + video_service::wayland_support::clear(); } } diff --git a/src/server/connection.rs b/src/server/connection.rs index 04b070cfe..869df4196 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -237,7 +237,7 @@ impl Connection { let mut msg_out = Message::new(); msg_out.set_misc(misc); conn.send(msg_out).await; - conn.on_close("Close requested from connection manager", false); + conn.on_close("Close requested from connection manager", false).await; SESSIONS.lock().unwrap().remove(&conn.lr.my_id); break; } @@ -327,7 +327,7 @@ impl Connection { if let Some(res) = res { match res { Err(err) => { - conn.on_close(&err.to_string(), true); + conn.on_close(&err.to_string(), true).await; break; }, Ok(bytes) => { @@ -341,14 +341,14 @@ impl Connection { } } } else { - conn.on_close("Reset by the peer", true); + conn.on_close("Reset by the peer", true).await; break; } }, _ = conn.timer.tick() => { if !conn.read_jobs.is_empty() { if let Err(err) = fs::handle_read_jobs(&mut conn.read_jobs, &mut conn.stream).await { - conn.on_close(&err.to_string(), false); + conn.on_close(&err.to_string(), false).await; break; } } else { @@ -361,7 +361,7 @@ impl Connection { video_service::notify_video_frame_feched(id, Some(instant.into())); } if let Err(err) = conn.stream.send(&value as &Message).await { - conn.on_close(&err.to_string(), false); + conn.on_close(&err.to_string(), false).await; break; } }, @@ -379,13 +379,13 @@ impl Connection { } } if let Err(err) = conn.stream.send(msg).await { - conn.on_close(&err.to_string(), false); + conn.on_close(&err.to_string(), false).await; break; } }, _ = test_delay_timer.tick() => { if last_recv_time.elapsed() >= SEC30 { - conn.on_close("Timeout", true); + conn.on_close("Timeout", true).await; break; } let time = crate::get_time(); @@ -417,7 +417,7 @@ impl Connection { video_service::VIDEO_QOS.lock().unwrap().reset(); password::after_session(conn.authorized); if let Err(err) = conn.try_port_forward_loop(&mut rx_from_cm).await { - conn.on_close(&err.to_string(), false); + conn.on_close(&err.to_string(), false).await; } conn.post_audit(json!({ @@ -646,9 +646,9 @@ impl Connection { #[cfg(target_os = "linux")] if !self.file_transfer.is_some() && !self.port_forward_socket.is_some() { let dtype = crate::platform::linux::get_display_server(); - if dtype != "x11" { + if dtype != "x11" && dtype != "wayland" { res.set_error(format!( - "Unsupported display server type {}, x11 expected", + "Unsupported display server type {}, x11 or wayland expected", dtype )); let mut msg_out = Message::new(); @@ -684,7 +684,7 @@ impl Connection { res.set_peer_info(pi); } else { try_activate_screen(); - match video_service::get_displays() { + match super::video_service::get_displays().await { Err(err) => { res.set_error(format!("X11 error: {}", err)); } @@ -1175,7 +1175,7 @@ impl Connection { }, Some(message::Union::Misc(misc)) => match misc.union { Some(misc::Union::SwitchDisplay(s)) => { - video_service::switch_display(s.display); + video_service::switch_display(s.display).await; } Some(misc::Union::ChatMessage(c)) => { self.send_to_cm(ipc::Data::ChatMessage { text: c.text }); @@ -1185,7 +1185,7 @@ impl Connection { } Some(misc::Union::RefreshVideo(r)) => { if r { - video_service::refresh(); + super::video_service::refresh(); } } Some(misc::Union::VideoReceived(_)) => { @@ -1195,7 +1195,7 @@ impl Connection { ); } Some(misc::Union::CloseReason(_)) => { - self.on_close("Peer close", true); + self.on_close("Peer close", true).await; SESSIONS.lock().unwrap().remove(&self.lr.my_id); return false; } @@ -1353,14 +1353,14 @@ impl Connection { } } - fn on_close(&mut self, reason: &str, lock: bool) { + async fn on_close(&mut self, reason: &str, lock: bool) { if let Some(s) = self.server.upgrade() { s.write().unwrap().remove_connection(&self.inner); } log::info!("#{} Connection closed: {}", self.inner.id(), reason); if lock && self.lock_after_session_end && self.keyboard { #[cfg(not(any(target_os = "android", target_os = "ios")))] - lock_screen(); + lock_screen().await; } self.tx_to_cm.send(ipc::Data::Close).ok(); self.port_forward_socket.take(); diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 3ec9c0f70..5d0ebe452 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -187,6 +187,26 @@ lazy_static::lazy_static! { static ref IS_SERVER: bool = std::env::args().nth(1) == Some("--server".to_owned()); } +#[cfg(target_os = "linux")] +pub async fn set_uinput() -> ResultType<()> { + // Keyboard and mouse both open /dev/uinput + // TODO: Make sure there's no race + let keyboard = self::uinput::client::UInputKeyboard::new().await?; + log::info!("UInput keyboard created"); + let mouse = self::uinput::client::UInputMouse::new().await?; + log::info!("UInput mouse created"); + + let mut en = ENIGO.lock().unwrap(); + en.set_uinput_keyboard(Some(Box::new(keyboard))); + en.set_uinput_mouse(Some(Box::new(mouse))); + Ok(()) +} + +#[cfg(target_os = "linux")] +pub async fn set_uinput_resolution(minx: i32, maxx: i32, miny: i32, maxy: i32) -> ResultType<()> { + self::uinput::client::set_resolution(minx, maxx, miny, maxy).await +} + pub fn is_left_up(evt: &MouseEvent) -> bool { let buttons = evt.mask >> 3; let evt_type = evt.mask & 0x7; @@ -439,7 +459,7 @@ pub fn is_enter(evt: &KeyEvent) -> bool { return false; } -pub fn lock_screen() { +pub async fn lock_screen() { cfg_if::cfg_if! { if #[cfg(target_os = "linux")] { // xdg_screensaver lock not work on Linux from our service somehow @@ -469,7 +489,7 @@ pub fn lock_screen() { crate::platform::lock_screen(); } } - super::video_service::switch_to_primary(); + super::video_service::switch_to_primary().await; } lazy_static::lazy_static! { @@ -548,7 +568,6 @@ lazy_static::lazy_static! { (ControlKey::Equals, Key::Equals), (ControlKey::NumpadEnter, Key::NumpadEnter), (ControlKey::RAlt, Key::RightAlt), - (ControlKey::RWin, Key::RWin), (ControlKey::RControl, Key::RightControl), (ControlKey::RShift, Key::RightShift), ].iter().map(|(a, b)| (a.value(), b.clone())).collect(); @@ -679,7 +698,7 @@ fn handle_key_(evt: &KeyEvent) { allow_err!(send_sas()); }); } else if ck.value() == ControlKey::LockScreen.value() { - lock_screen(); + lock_screen_2(); } } Some(key_event::Union::Chr(chr)) => { @@ -729,9 +748,669 @@ fn handle_key_(evt: &KeyEvent) { } } +#[tokio::main(flavor = "current_thread")] +async fn lock_screen_2() { + lock_screen().await; +} + #[tokio::main(flavor = "current_thread")] async fn send_sas() -> ResultType<()> { let mut stream = crate::ipc::connect(1000, crate::POSTFIX_SERVICE).await?; timeout(1000, stream.send(&crate::ipc::Data::SAS)).await??; Ok(()) } + +#[cfg(target_os = "linux")] +pub mod uinput { + use crate::ipc::{self, new_listener, Connection, Data, DataKeyboard, DataMouse}; + use enigo::{Key, KeyboardControllable, MouseButton, MouseControllable}; + use evdev::{ + uinput::{VirtualDevice, VirtualDeviceBuilder}, + AttributeSet, EventType, InputEvent, + }; + use hbb_common::{allow_err, bail, log, tokio, ResultType}; + + static IPC_CONN_TIMEOUT: u64 = 1000; + static IPC_REQUEST_TIMEOUT: u64 = 1000; + static IPC_POSTFIX_KEYBOARD: &str = "_uinput_keyboard"; + static IPC_POSTFIX_MOUSE: &str = "_uinput_mouse"; + static IPC_POSTFIX_CONTROL: &str = "_uinput_control"; + + pub mod client { + use super::*; + + pub struct UInputKeyboard { + conn: Connection, + } + + impl UInputKeyboard { + pub async fn new() -> ResultType { + let conn = ipc::connect(IPC_CONN_TIMEOUT, IPC_POSTFIX_KEYBOARD).await?; + Ok(Self { conn }) + } + + #[tokio::main(flavor = "current_thread")] + async fn send(&mut self, data: Data) -> ResultType<()> { + self.conn.send(&data).await + } + + #[tokio::main(flavor = "current_thread")] + async fn send_get_key_state(&mut self, data: Data) -> ResultType { + self.conn.send(&data).await?; + + match self.conn.next_timeout(IPC_REQUEST_TIMEOUT).await { + Ok(Some(Data::KeyboardResponse(ipc::DataKeyboardResponse::GetKeyState( + state, + )))) => Ok(state), + Ok(Some(resp)) => { + // FATAL error!!! + bail!( + "FATAL error, wait keyboard result other response: {:?}", + &resp + ); + } + Ok(None) => { + // FATAL error!!! + // Maybe wait later + bail!("FATAL error, wait keyboard result, receive None",); + } + Err(e) => { + // FATAL error!!! + bail!( + "FATAL error, wait keyboard result timeout {}, {}", + &e, + IPC_REQUEST_TIMEOUT + ); + } + } + } + } + + impl KeyboardControllable for UInputKeyboard { + fn get_key_state(&mut self, key: Key) -> bool { + match self.send_get_key_state(Data::Keyboard(DataKeyboard::GetKeyState(key))) { + Ok(state) => state, + Err(e) => { + // unreachable!() + log::error!("Failed to get key state {}", &e); + false + } + } + } + + fn key_sequence(&mut self, sequence: &str) { + allow_err!(self.send(Data::Keyboard(DataKeyboard::Sequence(sequence.to_string())))); + } + + // TODO: handle error??? + fn key_down(&mut self, key: Key) -> enigo::ResultType { + allow_err!(self.send(Data::Keyboard(DataKeyboard::KeyDown(key)))); + Ok(()) + } + fn key_up(&mut self, key: Key) { + allow_err!(self.send(Data::Keyboard(DataKeyboard::KeyUp(key)))); + } + fn key_click(&mut self, key: Key) { + allow_err!(self.send(Data::Keyboard(DataKeyboard::KeyClick(key)))); + } + } + + pub struct UInputMouse { + conn: Connection, + } + + impl UInputMouse { + pub async fn new() -> ResultType { + let conn = ipc::connect(IPC_CONN_TIMEOUT, IPC_POSTFIX_MOUSE).await?; + Ok(Self { conn }) + } + + #[tokio::main(flavor = "current_thread")] + async fn send(&mut self, data: Data) -> ResultType<()> { + self.conn.send(&data).await + } + } + + impl MouseControllable for UInputMouse { + fn mouse_move_to(&mut self, x: i32, y: i32) { + allow_err!(self.send(Data::Mouse(DataMouse::MoveTo(x, y)))); + } + fn mouse_move_relative(&mut self, x: i32, y: i32) { + allow_err!(self.send(Data::Mouse(DataMouse::MoveRelative(x, y)))); + } + // TODO: handle error??? + fn mouse_down(&mut self, button: MouseButton) -> enigo::ResultType { + allow_err!(self.send(Data::Mouse(DataMouse::Down(button)))); + Ok(()) + } + fn mouse_up(&mut self, button: MouseButton) { + allow_err!(self.send(Data::Mouse(DataMouse::Up(button)))); + } + fn mouse_click(&mut self, button: MouseButton) { + allow_err!(self.send(Data::Mouse(DataMouse::Click(button)))); + } + fn mouse_scroll_x(&mut self, length: i32) { + allow_err!(self.send(Data::Mouse(DataMouse::ScrollX(length)))); + } + fn mouse_scroll_y(&mut self, length: i32) { + allow_err!(self.send(Data::Mouse(DataMouse::ScrollY(length)))); + } + } + + pub async fn set_resolution(minx: i32, maxx: i32, miny: i32, maxy: i32) -> ResultType<()> { + let mut conn = ipc::connect(IPC_CONN_TIMEOUT, IPC_POSTFIX_CONTROL).await?; + conn.send(&Data::Control(ipc::DataControl::Resolution { + minx, + maxx, + miny, + maxy, + })) + .await?; + let _ = conn.next().await?; + Ok(()) + } + } + + pub mod service { + use super::*; + use hbb_common::lazy_static; + use mouce::MouseActions; + use std::{collections::HashMap, sync::Mutex}; + + lazy_static::lazy_static! { + static ref KEY_MAP: HashMap = HashMap::from( + [ + (enigo::Key::Alt, evdev::Key::KEY_LEFTALT), + (enigo::Key::Backspace, evdev::Key::KEY_BACKSPACE), + (enigo::Key::CapsLock, evdev::Key::KEY_CAPSLOCK), + (enigo::Key::Control, evdev::Key::KEY_LEFTCTRL), + (enigo::Key::Delete, evdev::Key::KEY_DELETE), + (enigo::Key::DownArrow, evdev::Key::KEY_DOWN), + (enigo::Key::End, evdev::Key::KEY_END), + (enigo::Key::Escape, evdev::Key::KEY_ESC), + (enigo::Key::F1, evdev::Key::KEY_F1), + (enigo::Key::F10, evdev::Key::KEY_F10), + (enigo::Key::F11, evdev::Key::KEY_F11), + (enigo::Key::F12, evdev::Key::KEY_F12), + (enigo::Key::F2, evdev::Key::KEY_F2), + (enigo::Key::F3, evdev::Key::KEY_F3), + (enigo::Key::F4, evdev::Key::KEY_F4), + (enigo::Key::F5, evdev::Key::KEY_F5), + (enigo::Key::F6, evdev::Key::KEY_F6), + (enigo::Key::F7, evdev::Key::KEY_F7), + (enigo::Key::F8, evdev::Key::KEY_F8), + (enigo::Key::F9, evdev::Key::KEY_F9), + (enigo::Key::Home, evdev::Key::KEY_HOME), + (enigo::Key::LeftArrow, evdev::Key::KEY_LEFT), + (enigo::Key::Meta, evdev::Key::KEY_LEFTMETA), + (enigo::Key::Option, evdev::Key::KEY_OPTION), + (enigo::Key::PageDown, evdev::Key::KEY_PAGEDOWN), + (enigo::Key::PageUp, evdev::Key::KEY_PAGEUP), + (enigo::Key::Return, evdev::Key::KEY_ENTER), + (enigo::Key::RightArrow, evdev::Key::KEY_RIGHT), + (enigo::Key::Shift, evdev::Key::KEY_LEFTSHIFT), + (enigo::Key::Space, evdev::Key::KEY_SPACE), + (enigo::Key::Tab, evdev::Key::KEY_TAB), + (enigo::Key::UpArrow, evdev::Key::KEY_UP), + (enigo::Key::Numpad0, evdev::Key::KEY_KP0), // check if correct? + (enigo::Key::Numpad1, evdev::Key::KEY_KP1), + (enigo::Key::Numpad2, evdev::Key::KEY_KP2), + (enigo::Key::Numpad3, evdev::Key::KEY_KP3), + (enigo::Key::Numpad4, evdev::Key::KEY_KP4), + (enigo::Key::Numpad5, evdev::Key::KEY_KP5), + (enigo::Key::Numpad6, evdev::Key::KEY_KP6), + (enigo::Key::Numpad7, evdev::Key::KEY_KP7), + (enigo::Key::Numpad8, evdev::Key::KEY_KP8), + (enigo::Key::Numpad9, evdev::Key::KEY_KP9), + (enigo::Key::Cancel, evdev::Key::KEY_CANCEL), + (enigo::Key::Clear, evdev::Key::KEY_CLEAR), + (enigo::Key::Alt, evdev::Key::KEY_LEFTALT), + (enigo::Key::Pause, evdev::Key::KEY_PAUSE), + (enigo::Key::Kana, evdev::Key::KEY_KATAKANA), // check if correct? + (enigo::Key::Hangul, evdev::Key::KEY_HANGEUL), // check if correct? + // (enigo::Key::Junja, evdev::Key::KEY_JUNJA), // map? + // (enigo::Key::Final, evdev::Key::KEY_FINAL), // map? + (enigo::Key::Hanja, evdev::Key::KEY_HANJA), + // (enigo::Key::Kanji, evdev::Key::KEY_KANJI), // map? + // (enigo::Key::Convert, evdev::Key::KEY_CONVERT), + (enigo::Key::Select, evdev::Key::KEY_SELECT), + (enigo::Key::Print, evdev::Key::KEY_PRINT), + // (enigo::Key::Execute, evdev::Key::KEY_EXECUTE), + // (enigo::Key::Snapshot, evdev::Key::KEY_SNAPSHOT), + (enigo::Key::Insert, evdev::Key::KEY_INSERT), + (enigo::Key::Help, evdev::Key::KEY_HELP), + (enigo::Key::Sleep, evdev::Key::KEY_SLEEP), + // (enigo::Key::Separator, evdev::Key::KEY_SEPARATOR), + (enigo::Key::Scroll, evdev::Key::KEY_SCROLLLOCK), + (enigo::Key::NumLock, evdev::Key::KEY_NUMLOCK), + (enigo::Key::RWin, evdev::Key::KEY_RIGHTMETA), + (enigo::Key::Apps, evdev::Key::KEY_CONTEXT_MENU), + (enigo::Key::Multiply, evdev::Key::KEY_KPASTERISK), + (enigo::Key::Add, evdev::Key::KEY_KPPLUS), + (enigo::Key::Subtract, evdev::Key::KEY_KPMINUS), + (enigo::Key::Decimal, evdev::Key::KEY_KPCOMMA), // KEY_KPDOT and KEY_KPCOMMA are exchanged? + (enigo::Key::Divide, evdev::Key::KEY_KPSLASH), + (enigo::Key::Equals, evdev::Key::KEY_KPEQUAL), + (enigo::Key::NumpadEnter, evdev::Key::KEY_KPENTER), + (enigo::Key::RightAlt, evdev::Key::KEY_RIGHTALT), + (enigo::Key::RightControl, evdev::Key::KEY_RIGHTCTRL), + (enigo::Key::RightShift, evdev::Key::KEY_RIGHTSHIFT), + ]); + + static ref KEY_MAP_LAYOUT: HashMap = HashMap::from( + [ + ('a', evdev::Key::KEY_A), + ('b', evdev::Key::KEY_B), + ('c', evdev::Key::KEY_C), + ('d', evdev::Key::KEY_D), + ('e', evdev::Key::KEY_E), + ('f', evdev::Key::KEY_F), + ('g', evdev::Key::KEY_G), + ('h', evdev::Key::KEY_H), + ('i', evdev::Key::KEY_I), + ('j', evdev::Key::KEY_J), + ('k', evdev::Key::KEY_K), + ('l', evdev::Key::KEY_L), + ('m', evdev::Key::KEY_M), + ('n', evdev::Key::KEY_N), + ('o', evdev::Key::KEY_O), + ('p', evdev::Key::KEY_P), + ('q', evdev::Key::KEY_Q), + ('r', evdev::Key::KEY_R), + ('s', evdev::Key::KEY_S), + ('t', evdev::Key::KEY_T), + ('u', evdev::Key::KEY_U), + ('v', evdev::Key::KEY_V), + ('w', evdev::Key::KEY_W), + ('x', evdev::Key::KEY_X), + ('y', evdev::Key::KEY_Y), + ('z', evdev::Key::KEY_Z), + ('0', evdev::Key::KEY_0), + ('1', evdev::Key::KEY_1), + ('2', evdev::Key::KEY_2), + ('3', evdev::Key::KEY_3), + ('4', evdev::Key::KEY_4), + ('5', evdev::Key::KEY_5), + ('6', evdev::Key::KEY_6), + ('7', evdev::Key::KEY_7), + ('8', evdev::Key::KEY_8), + ('9', evdev::Key::KEY_9), + ('`', evdev::Key::KEY_GRAVE), + ('-', evdev::Key::KEY_MINUS), + ('=', evdev::Key::KEY_EQUAL), + ('[', evdev::Key::KEY_LEFTBRACE), + (']', evdev::Key::KEY_RIGHTBRACE), + ('\\', evdev::Key::KEY_BACKSLASH), + (',', evdev::Key::KEY_COMMA), + ('.', evdev::Key::KEY_DOT), + ('/', evdev::Key::KEY_SLASH), + (';', evdev::Key::KEY_SEMICOLON), + ('\'', evdev::Key::KEY_APOSTROPHE), + ]); + + // ((minx, maxx), (miny, maxy)) + static ref RESOLUTION: Mutex<((i32, i32), (i32, i32))> = Mutex::new(((0, 0), (0, 0))); + } + + fn create_uinput_keyboard() -> ResultType { + // TODO: ensure keys here + let mut keys = AttributeSet::::new(); + for i in evdev::Key::KEY_ESC.code()..(evdev::Key::BTN_TRIGGER_HAPPY40.code() + 1) { + let key = evdev::Key::new(i); + if !format!("{:?}", &key).contains("unknown key") { + keys.insert(key); + } + } + let mut leds = AttributeSet::::new(); + leds.insert(evdev::LedType::LED_NUML); + leds.insert(evdev::LedType::LED_CAPSL); + leds.insert(evdev::LedType::LED_SCROLLL); + let mut miscs = AttributeSet::::new(); + miscs.insert(evdev::MiscType::MSC_SCAN); + let keyboard = VirtualDeviceBuilder::new()? + .name("RustDesk UInput Keyboard") + .with_keys(&keys)? + .with_leds(&leds)? + .with_miscs(&miscs)? + .build()?; + Ok(keyboard) + } + + fn map_key(key: &enigo::Key) -> ResultType { + if let Some(k) = KEY_MAP.get(&key) { + log::trace!("mapkey {:?}, get {:?}", &key, &k); + return Ok(k.clone()); + } else { + match key { + enigo::Key::Layout(c) => { + if let Some(k) = KEY_MAP_LAYOUT.get(&c) { + log::trace!("mapkey {:?}, get {:?}", &key, k); + return Ok(k.clone()); + } + } + // enigo::Key::Raw(c) => { + // let k = evdev::Key::new(c); + // if !format!("{:?}", &k).contains("unknown key") { + // return Ok(k.clone()); + // } + // } + _ => {} + } + } + bail!("Failed to map key {:?}", &key); + } + + async fn ipc_send_data(stream: &mut Connection, data: &Data) { + allow_err!(stream.send(data).await); + } + + async fn handle_keyboard( + stream: &mut Connection, + keyboard: &mut VirtualDevice, + data: &DataKeyboard, + ) { + log::trace!("handle_keyboard {:?}", &data); + match data { + DataKeyboard::Sequence(_seq) => { + // ignore + } + DataKeyboard::KeyDown(key) => { + if let Ok(k) = map_key(key) { + let down_event = InputEvent::new(EventType::KEY, k.code(), 1); + allow_err!(keyboard.emit(&[down_event])); + } + } + DataKeyboard::KeyUp(key) => { + if let Ok(k) = map_key(key) { + let up_event = InputEvent::new(EventType::KEY, k.code(), 0); + allow_err!(keyboard.emit(&[up_event])); + } + } + DataKeyboard::KeyClick(key) => { + if let Ok(k) = map_key(key) { + let down_event = InputEvent::new(EventType::KEY, k.code(), 1); + let up_event = InputEvent::new(EventType::KEY, k.code(), 0); + allow_err!(keyboard.emit(&[down_event, up_event])); + } + } + DataKeyboard::GetKeyState(key) => { + let key_state = if enigo::Key::CapsLock == *key { + match keyboard.get_led_state() { + Ok(leds) => leds.contains(evdev::LedType::LED_CAPSL), + Err(_e) => { + // log::debug!("Failed to get led state {}", &_e); + false + } + } + } else { + match keyboard.get_key_state() { + Ok(keys) => match key { + enigo::Key::Shift => { + keys.contains(evdev::Key::KEY_LEFTSHIFT) + || keys.contains(evdev::Key::KEY_RIGHTSHIFT) + } + enigo::Key::Control => { + keys.contains(evdev::Key::KEY_LEFTCTRL) + || keys.contains(evdev::Key::KEY_RIGHTCTRL) + } + enigo::Key::Alt => { + keys.contains(evdev::Key::KEY_LEFTALT) + || keys.contains(evdev::Key::KEY_RIGHTALT) + } + enigo::Key::NumLock => keys.contains(evdev::Key::KEY_NUMLOCK), + enigo::Key::Meta => { + keys.contains(evdev::Key::KEY_LEFTMETA) + || keys.contains(evdev::Key::KEY_RIGHTMETA) + } + _ => false, + }, + Err(_e) => { + // log::debug!("Failed to get key state: {}", &_e); + false + } + } + }; + ipc_send_data( + stream, + &Data::KeyboardResponse(ipc::DataKeyboardResponse::GetKeyState(key_state)), + ) + .await; + } + } + } + + fn handle_mouse(mouse: &mut mouce::nix::UInputMouseManager, data: &DataMouse) { + log::trace!("handle_mouse {:?}", &data); + match data { + DataMouse::MoveTo(x, y) => { + allow_err!(mouse.move_to(*x as _, *y as _)) + } + DataMouse::MoveRelative(x, y) => { + allow_err!(mouse.move_relative(*x, *y)) + } + DataMouse::Down(button) => { + let btn = match button { + enigo::MouseButton::Left => mouce::common::MouseButton::Left, + enigo::MouseButton::Middle => mouce::common::MouseButton::Middle, + enigo::MouseButton::Right => mouce::common::MouseButton::Right, + _ => { + return; + } + }; + allow_err!(mouse.press_button(&btn)) + } + DataMouse::Up(button) => { + let btn = match button { + enigo::MouseButton::Left => mouce::common::MouseButton::Left, + enigo::MouseButton::Middle => mouce::common::MouseButton::Middle, + enigo::MouseButton::Right => mouce::common::MouseButton::Right, + _ => { + return; + } + }; + allow_err!(mouse.release_button(&btn)) + } + DataMouse::Click(button) => { + let btn = match button { + enigo::MouseButton::Left => mouce::common::MouseButton::Left, + enigo::MouseButton::Middle => mouce::common::MouseButton::Middle, + enigo::MouseButton::Right => mouce::common::MouseButton::Right, + _ => { + return; + } + }; + allow_err!(mouse.click_button(&btn)) + } + DataMouse::ScrollX(_length) => { + // TODO: not supported for now + } + DataMouse::ScrollY(length) => { + let mut length = *length; + + let scroll = if length < 0 { + mouce::common::ScrollDirection::Up + } else { + mouce::common::ScrollDirection::Down + }; + + if length < 0 { + length = -length; + } + + for _ in 0..length { + allow_err!(mouse.scroll_wheel(&scroll)) + } + } + } + } + + fn spawn_keyboard_handler(mut stream: Connection) { + tokio::spawn(async move { + let mut keyboard = match create_uinput_keyboard() { + Ok(keyboard) => keyboard, + Err(e) => { + log::error!("Failed to create keyboard {}", e); + return; + } + }; + loop { + tokio::select! { + res = stream.next() => { + match res { + Err(err) => { + log::info!("UInput keyboard ipc connection closed: {}", err); + break; + } + Ok(Some(data)) => { + match data { + Data::Keyboard(data) => { + handle_keyboard(&mut stream, &mut keyboard, &data).await; + } + _ => { + } + } + } + _ => {} + } + } + } + } + }); + } + + fn spawn_mouse_handler(mut stream: ipc::Connection) { + let resolution = RESOLUTION.lock().unwrap(); + if resolution.0 .0 == resolution.0 .1 || resolution.1 .0 == resolution.1 .1 { + return; + } + let rng_x = resolution.0.clone(); + let rng_y = resolution.1.clone(); + tokio::spawn(async move { + log::info!( + "Create uinput mouce with rng_x: ({}, {}), rng_y: ({}, {})", + rng_x.0, + rng_x.1, + rng_y.0, + rng_y.1 + ); + let mut mouse = match mouce::Mouse::new_uinput(rng_x, rng_y) { + Ok(mouse) => mouse, + Err(e) => { + log::error!("Failed to create mouse, {}", e); + return; + } + }; + loop { + tokio::select! { + res = stream.next() => { + match res { + Err(err) => { + log::info!("UInput mouse ipc connection closed: {}", err); + break; + } + Ok(Some(data)) => { + match data { + Data::Mouse(data) => { + handle_mouse(&mut mouse, &data); + } + _ => { + } + } + } + _ => {} + } + } + } + } + }); + } + + fn spawn_controller_handler(mut stream: ipc::Connection) { + tokio::spawn(async move { + loop { + tokio::select! { + res = stream.next() => { + match res { + Err(_err) => { + // log::info!("UInput controller ipc connection closed: {}", err); + break; + } + Ok(Some(data)) => { + match data { + Data::Control(data) => match data { + ipc::DataControl::Resolution{ + minx, + maxx, + miny, + maxy, + } => { + *RESOLUTION.lock().unwrap() = ((minx, maxx), (miny, maxy)); + allow_err!(stream.send(&Data::Empty).await); + } + } + _ => { + } + } + } + _ => {} + } + } + } + } + }); + } + + /// Start uinput service. + async fn start_service(postfix: &str, handler: F) { + match new_listener(postfix).await { + Ok(mut incoming) => { + while let Some(result) = incoming.next().await { + match result { + Ok(stream) => { + log::debug!("Got new connection of uinput ipc {}", postfix); + handler(Connection::new(stream)); + } + Err(err) => { + log::error!("Couldn't get uinput mouse client: {:?}", err); + } + } + } + } + Err(err) => { + log::error!("Failed to start uinput mouse ipc service: {}", err); + } + } + } + + /// Start uinput keyboard service. + #[tokio::main(flavor = "current_thread")] + pub async fn start_service_keyboard() { + log::info!("start uinput keyboard service"); + start_service(IPC_POSTFIX_KEYBOARD, spawn_keyboard_handler).await; + } + + /// Start uinput mouse service. + #[tokio::main(flavor = "current_thread")] + pub async fn start_service_mouse() { + log::info!("start uinput mouse service"); + start_service(IPC_POSTFIX_MOUSE, spawn_mouse_handler).await; + } + + /// Start uinput mouse service. + #[tokio::main(flavor = "current_thread")] + pub async fn start_service_control() { + log::info!("start uinput control service"); + start_service(IPC_POSTFIX_CONTROL, spawn_controller_handler).await; + } + + pub fn stop_service_keyboard() { + log::info!("stop uinput keyboard service"); + } + pub fn stop_service_mouse() { + log::info!("stop uinput mouse service"); + } + pub fn stop_service_control() { + log::info!("stop uinput control service"); + } + } +} diff --git a/src/server/video_service.rs b/src/server/video_service.rs index e64ddd807..c8b628015 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -130,6 +130,8 @@ impl VideoFrameController { trait TraitCapturer { fn frame<'a>(&'a mut self, timeout: Duration) -> Result>; + fn set_use_yuv(&mut self, use_yuv: bool); + #[cfg(windows)] fn is_gdi(&self) -> bool; #[cfg(windows)] @@ -141,6 +143,10 @@ impl TraitCapturer for Capturer { self.frame(timeout) } + fn set_use_yuv(&mut self, use_yuv: bool) { + self.set_use_yuv(use_yuv); + } + #[cfg(windows)] fn is_gdi(&self) -> bool { self.is_gdi() @@ -158,6 +164,10 @@ impl TraitCapturer for scrap::CapturerMag { self.frame(_timeout_ms) } + fn set_use_yuv(&mut self, use_yuv: bool) { + self.set_use_yuv(use_yuv); + } + fn is_gdi(&self) -> bool { false } @@ -179,6 +189,14 @@ fn check_display_changed( last_width: usize, last_hegiht: usize, ) -> bool { + #[cfg(target_os = "linux")] + { + // wayland do not support changing display for now + if scrap::is_wayland() { + return false; + } + } + let displays = match try_get_displays() { Ok(d) => d, _ => return false, @@ -293,6 +311,7 @@ fn ensure_close_virtual_device() -> ResultType<()> { Ok(()) } +// This function works on privacy mode. Windows only for now. pub fn test_create_capturer(privacy_mode_id: i32, timeout_millis: u64) -> bool { let test_begin = Instant::now(); while test_begin.elapsed().as_millis() < timeout_millis as _ { @@ -321,9 +340,24 @@ fn check_uac_switch(privacy_mode_id: i32, captuerer_privacy_mode_id: i32) -> Res Ok(()) } -fn run(sp: GenericService) -> ResultType<()> { - #[cfg(windows)] - ensure_close_virtual_device()?; +struct CapturerInfo { + origin: (i32, i32), + width: usize, + height: usize, + ndisplay: usize, + current: usize, + privacy_mode_id: i32, + _captuerer_privacy_mode_id: i32, + capturer: Box, +} + +fn get_capturer(use_yuv: bool) -> ResultType { + #[cfg(target_os = "linux")] + { + if scrap::is_wayland() { + return wayland_support::get_capturer(); + } + } let (ndisplay, current, display) = get_current_display()?; let (origin, width, height) = (display.origin(), display.width(), display.height()); @@ -338,38 +372,6 @@ fn run(sp: GenericService) -> ResultType<()> { num_cpus::get(), ); - let mut video_qos = VIDEO_QOS.lock().unwrap(); - - video_qos.set_size(width as _, height as _); - let mut spf = video_qos.spf(); - let bitrate = video_qos.generate_bitrate()?; - let abr = video_qos.check_abr_config(); - drop(video_qos); - log::info!("init bitrate={}, abr enabled:{}", bitrate, abr); - - let encoder_cfg = match Encoder::current_hw_encoder_name() { - Some(codec_name) => EncoderCfg::HW(HwEncoderConfig { - codec_name, - width, - height, - bitrate: bitrate as _, - }), - None => EncoderCfg::VPX(VpxEncoderConfig { - width: width as _, - height: height as _, - timebase: [1, 1000], // Output timestamp precision - bitrate, - codec: VpxVideoCodecId::VP9, - num_threads: (num_cpus::get() / 2) as _, - }), - }; - - let mut encoder; - match Encoder::new(encoder_cfg) { - Ok(x) => encoder = x, - Err(err) => bail!("Failed to create encoder: {}", err), - } - let privacy_mode_id = *PRIVACY_MODE_CONN_ID.lock().unwrap(); #[cfg(not(windows))] let captuerer_privacy_mode_id = privacy_mode_id; @@ -389,17 +391,67 @@ fn run(sp: GenericService) -> ResultType<()> { } else { log::info!("In privacy mode, the peer side cannot watch the screen"); } - let mut c = create_capturer(captuerer_privacy_mode_id, display, encoder.use_yuv())?; + let capturer = create_capturer(captuerer_privacy_mode_id, display, use_yuv)?; + Ok(CapturerInfo { + origin, + width, + height, + ndisplay, + current, + privacy_mode_id, + _captuerer_privacy_mode_id: captuerer_privacy_mode_id, + capturer, + }) +} + +fn run(sp: GenericService) -> ResultType<()> { + #[cfg(windows)] + ensure_close_virtual_device()?; + + let mut c = get_capturer(true)?; + + let mut video_qos = VIDEO_QOS.lock().unwrap(); + + video_qos.set_size(c.width as _, c.height as _); + let mut spf = video_qos.spf(); + let bitrate = video_qos.generate_bitrate()?; + let abr = video_qos.check_abr_config(); + drop(video_qos); + log::info!("init bitrate={}, abr enabled:{}", bitrate, abr); + + let encoder_cfg = match Encoder::current_hw_encoder_name() { + Some(codec_name) => EncoderCfg::HW(HwEncoderConfig { + codec_name, + width: c.width, + height: c.height, + bitrate: bitrate as _, + }), + None => EncoderCfg::VPX(VpxEncoderConfig { + width: c.width as _, + height: c.height as _, + timebase: [1, 1000], // Output timestamp precision + bitrate, + codec: VpxVideoCodecId::VP9, + num_threads: (num_cpus::get() / 2) as _, + }), + }; + + let mut encoder; + match Encoder::new(encoder_cfg) { + Ok(x) => encoder = x, + Err(err) => bail!("Failed to create encoder: {}", err), + } + c.capturer.set_use_yuv(encoder.use_yuv()); if *SWITCH.lock().unwrap() { log::debug!("Broadcasting display switch"); let mut misc = Misc::new(); misc.set_switch_display(SwitchDisplay { - display: current as _, - x: origin.0 as _, - y: origin.1 as _, - width: width as _, - height: height as _, + display: c.current as _, + x: c.origin.0 as _, + y: c.origin.1 as _, + width: c.width as _, + height: c.height as _, ..Default::default() }); let mut msg_out = Message::new(); @@ -415,11 +467,11 @@ fn run(sp: GenericService) -> ResultType<()> { #[cfg(windows)] let mut try_gdi = 1; #[cfg(windows)] - log::info!("gdi: {}", c.is_gdi()); + log::info!("gdi: {}", c.capturer.is_gdi()); while sp.ok() { #[cfg(windows)] - check_uac_switch(privacy_mode_id, captuerer_privacy_mode_id)?; + check_uac_switch(c.privacy_mode_id, c._captuerer_privacy_mode_id)?; { let mut video_qos = VIDEO_QOS.lock().unwrap(); @@ -437,11 +489,11 @@ fn run(sp: GenericService) -> ResultType<()> { if *SWITCH.lock().unwrap() { bail!("SWITCH"); } - if current != *CURRENT_DISPLAY.lock().unwrap() { + if c.current != *CURRENT_DISPLAY.lock().unwrap() { *SWITCH.lock().unwrap() = true; bail!("SWITCH"); } - check_privacy_mode_changed(&sp, privacy_mode_id)?; + check_privacy_mode_changed(&sp, c.privacy_mode_id)?; #[cfg(windows)] { if crate::platform::windows::desktop_changed() { @@ -451,7 +503,7 @@ fn run(sp: GenericService) -> ResultType<()> { let now = time::Instant::now(); if last_check_displays.elapsed().as_millis() > 1000 { last_check_displays = now; - if ndisplay != get_display_num() { + if c.ndisplay != get_display_num() { log::info!("Displays changed"); *SWITCH.lock().unwrap() = true; bail!("SWITCH"); @@ -463,7 +515,7 @@ fn run(sp: GenericService) -> ResultType<()> { frame_controller.reset(); #[cfg(any(target_os = "android", target_os = "ios"))] - let res = match c.frame(spf) { + let res = match (*c.capturer).frame(spf) { Ok(frame) => { let time = now - start; let ms = (time.as_secs() * 1000 + time.subsec_millis() as u64) as i64; @@ -486,7 +538,7 @@ fn run(sp: GenericService) -> ResultType<()> { }; #[cfg(not(any(target_os = "android", target_os = "ios")))] - let res = match c.frame(spf) { + let res = match (*c.capturer).frame(spf) { Ok(frame) => { let time = now - start; let ms = (time.as_secs() * 1000 + time.subsec_millis() as u64) as i64; @@ -505,9 +557,9 @@ fn run(sp: GenericService) -> ResultType<()> { Err(ref e) if e.kind() == WouldBlock => { #[cfg(windows)] - if try_gdi > 0 && !c.is_gdi() { + if try_gdi > 0 && !c.capturer.is_gdi() { if try_gdi > 3 { - c.set_gdi(); + c.capturer.set_gdi(); try_gdi = 0; log::info!("No image, fall back to gdi"); } @@ -515,15 +567,15 @@ fn run(sp: GenericService) -> ResultType<()> { } } Err(err) => { - if check_display_changed(ndisplay, current, width, height) { + if check_display_changed(c.ndisplay, c.current, c.width, c.height) { log::info!("Displays changed"); *SWITCH.lock().unwrap() = true; bail!("SWITCH"); } #[cfg(windows)] - if !c.is_gdi() { - c.set_gdi(); + if !c.capturer.is_gdi() { + c.capturer.set_gdi(); log::info!("dxgi error, fall back to gdi: {:?}", err); continue; } @@ -537,9 +589,9 @@ fn run(sp: GenericService) -> ResultType<()> { let timeout_millis = 3_000u64; let wait_begin = Instant::now(); while wait_begin.elapsed().as_millis() < timeout_millis as _ { - check_privacy_mode_changed(&sp, privacy_mode_id)?; + check_privacy_mode_changed(&sp, c.privacy_mode_id)?; #[cfg(windows)] - check_uac_switch(privacy_mode_id, captuerer_privacy_mode_id)?; + check_uac_switch(c.privacy_mode_id, c._captuerer_privacy_mode_id)?; frame_controller.try_wait_next(&mut fetched_conn_ids, 300); // break if all connections have received current frame if fetched_conn_ids.len() >= frame_controller.send_conn_ids.len() { @@ -633,6 +685,17 @@ pub fn handle_one_frame_encoded( } fn get_display_num() -> usize { + #[cfg(target_os = "linux")] + { + if scrap::is_wayland() { + return if let Ok(n) = wayland_support::get_display_num() { + n + } else { + 0 + }; + } + } + if let Ok(d) = try_get_displays() { d.len() } else { @@ -640,14 +703,10 @@ fn get_display_num() -> usize { } } -pub fn get_displays() -> ResultType<(usize, Vec)> { - // switch to primary display if long time (30 seconds) no users - if LAST_ACTIVE.lock().unwrap().elapsed().as_secs() >= 30 { - *CURRENT_DISPLAY.lock().unwrap() = usize::MAX; - } +fn get_displays_2(all: &Vec) -> (usize, Vec) { let mut displays = Vec::new(); let mut primary = 0; - for (i, d) in try_get_displays()?.iter().enumerate() { + for (i, d) in all.iter().enumerate() { if d.is_primary() { primary = i; } @@ -665,12 +724,26 @@ pub fn get_displays() -> ResultType<(usize, Vec)> { if *lock >= displays.len() { *lock = primary } - Ok((*lock, displays)) + (*lock, displays) } -pub fn switch_display(i: i32) { +pub async fn get_displays() -> ResultType<(usize, Vec)> { + #[cfg(target_os = "linux")] + { + if scrap::is_wayland() { + return wayland_support::get_displays().await; + } + } + // switch to primary display if long time (30 seconds) no users + if LAST_ACTIVE.lock().unwrap().elapsed().as_secs() >= 30 { + *CURRENT_DISPLAY.lock().unwrap() = usize::MAX; + } + Ok(get_displays_2(&try_get_displays()?)) +} + +pub async fn switch_display(i: i32) { let i = i as usize; - if let Ok((_, displays)) = get_displays() { + if let Ok((_, displays)) = get_displays().await { if i < displays.len() { *CURRENT_DISPLAY.lock().unwrap() = i; } @@ -684,6 +757,16 @@ pub fn refresh() { } fn get_primary() -> usize { + #[cfg(target_os = "linux")] + { + if scrap::is_wayland() { + return match wayland_support::get_primary() { + Ok(n) => n, + Err(_) => 0, + }; + } + } + if let Ok(all) = try_get_displays() { for (i, d) in all.iter().enumerate() { if d.is_primary() { @@ -694,8 +777,8 @@ fn get_primary() -> usize { 0 } -pub fn switch_to_primary() { - switch_display(get_primary() as _); +pub async fn switch_to_primary() { + switch_display(get_primary() as _).await; } #[cfg(not(windows))] @@ -733,16 +816,15 @@ fn try_get_displays() -> ResultType> { Ok(displays) } -fn get_current_display() -> ResultType<(usize, usize, Display)> { +fn get_current_display_2(mut all: Vec) -> ResultType<(usize, usize, Display)> { let mut current = *CURRENT_DISPLAY.lock().unwrap() as usize; - let mut displays = try_get_displays()?; - if displays.len() == 0 { + if all.len() == 0 { bail!("No displays"); } - let n = displays.len(); + let n = all.len(); if current >= n { current = 0; - for (i, d) in displays.iter().enumerate() { + for (i, d) in all.iter().enumerate() { if d.is_primary() { current = i; break; @@ -750,5 +832,191 @@ fn get_current_display() -> ResultType<(usize, usize, Display)> { } *CURRENT_DISPLAY.lock().unwrap() = current; } - return Ok((n, current, displays.remove(current))); + return Ok((n, current, all.remove(current))); +} + +fn get_current_display() -> ResultType<(usize, usize, Display)> { + get_current_display_2(try_get_displays()?) +} + +#[cfg(target_os = "linux")] +pub mod wayland_support { + use super::*; + use hbb_common::allow_err; + + lazy_static::lazy_static! { + static ref CAP_DISPLAY_INFO: RwLock = RwLock::new(0); + } + struct CapDisplayInfo { + rects: Vec<((i32, i32), usize, usize)>, + displays: Vec, + num: usize, + primary: usize, + current: usize, + capturer: *mut Capturer, + } + + impl TraitCapturer for *mut Capturer { + fn frame<'a>(&'a mut self, timeout: Duration) -> Result> { + unsafe { (**self).frame(timeout) } + } + + fn set_use_yuv(&mut self, use_yuv: bool) { + unsafe { + (**self).set_use_yuv(use_yuv); + } + } + } + + async fn check_init() -> ResultType<()> { + if scrap::is_wayland() { + let mut minx = 0; + let mut maxx = 0; + let mut miny = 0; + let mut maxy = 0; + + if *CAP_DISPLAY_INFO.read().unwrap() == 0 { + let mut lock = CAP_DISPLAY_INFO.write().unwrap(); + if *lock == 0 { + let all = Display::all()?; + let num = all.len(); + let (primary, displays) = get_displays_2(&all); + + let mut rects: Vec<((i32, i32), usize, usize)> = Vec::new(); + for d in &all { + rects.push((d.origin(), d.width(), d.height())); + } + + let (ndisplay, current, display) = get_current_display_2(all)?; + let (origin, width, height) = + (display.origin(), display.width(), display.height()); + log::debug!( + "#displays={}, current={}, origin: {:?}, width={}, height={}, cpus={}/{}", + ndisplay, + current, + &origin, + width, + height, + num_cpus::get_physical(), + num_cpus::get(), + ); + + minx = origin.0; + maxx = origin.0 + width as i32; + miny = origin.1; + maxy = origin.1 + height as i32; + + let capturer = Box::into_raw(Box::new( + Capturer::new(display, true) + .with_context(|| "Failed to create capturer")?, + )); + let cap_display_info = Box::into_raw(Box::new(CapDisplayInfo { + rects, + displays, + num, + primary, + current, + capturer, + })); + *lock = cap_display_info as _; + } + } + + if minx != maxx && miny != maxy { + log::info!( + "send uinput resolution: ({}, {}), ({}, {})", + minx, + maxx, + miny, + maxy + ); + allow_err!(input_service::set_uinput_resolution(minx, maxx, miny, maxy).await); + allow_err!(input_service::set_uinput().await); + } + } + Ok(()) + } + + pub fn clear() { + if !scrap::is_wayland() { + return; + } + + let mut lock = CAP_DISPLAY_INFO.write().unwrap(); + if *lock != 0 { + unsafe { + let cap_display_info = Box::from_raw(*lock as *mut CapDisplayInfo); + let _ = Box::from_raw(cap_display_info.capturer); + } + *lock = 0; + } + } + + pub(super) async fn get_displays() -> ResultType<(usize, Vec)> { + check_init().await?; + let addr = *CAP_DISPLAY_INFO.read().unwrap(); + if addr != 0 { + let cap_display_info: *const CapDisplayInfo = addr as _; + unsafe { + let cap_display_info = &*cap_display_info; + let primary = cap_display_info.primary; + let displays = cap_display_info.displays.clone(); + Ok((primary, displays)) + } + } else { + bail!("Failed to get capturer display info"); + } + } + + pub(super) fn get_primary() -> ResultType { + let addr = *CAP_DISPLAY_INFO.read().unwrap(); + if addr != 0 { + let cap_display_info: *const CapDisplayInfo = addr as _; + unsafe { + let cap_display_info = &*cap_display_info; + Ok(cap_display_info.primary) + } + } else { + bail!("Failed to get capturer display info"); + } + } + + pub(super) fn get_display_num() -> ResultType { + let addr = *CAP_DISPLAY_INFO.read().unwrap(); + if addr != 0 { + let cap_display_info: *const CapDisplayInfo = addr as _; + unsafe { + let cap_display_info = &*cap_display_info; + Ok(cap_display_info.num) + } + } else { + bail!("Failed to get capturer display info"); + } + } + + pub(super) fn get_capturer() -> ResultType { + if !scrap::is_wayland() { + bail!("Do not call this function if not wayland"); + } + let addr = *CAP_DISPLAY_INFO.read().unwrap(); + if addr != 0 { + let cap_display_info: *const CapDisplayInfo = addr as _; + unsafe { + let cap_display_info = &*cap_display_info; + let rect = cap_display_info.rects[cap_display_info.current]; + Ok(CapturerInfo { + origin: rect.0, + width: rect.1, + height: rect.2, + ndisplay: cap_display_info.num, + current: cap_display_info.current, + privacy_mode_id: 0, + _captuerer_privacy_mode_id: 0, + capturer: Box::new(cap_display_info.capturer), + }) + } + } else { + bail!("Failed to get capturer display info"); + } + } } From 05b264c96842107ccb797e32a758fedbfaceae41 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 6 Jul 2022 10:49:50 -0700 Subject: [PATCH 43/50] linux_wayland_support: build linux Signed-off-by: fufesou --- DEBIAN/postinst | 0 DEBIAN/postrm | 0 DEBIAN/preinst | 0 DEBIAN/prerm | 0 libs/scrap/src/common/linux.rs | 2 +- 5 files changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 DEBIAN/postinst mode change 100644 => 100755 DEBIAN/postrm mode change 100644 => 100755 DEBIAN/preinst mode change 100644 => 100755 DEBIAN/prerm diff --git a/DEBIAN/postinst b/DEBIAN/postinst old mode 100644 new mode 100755 diff --git a/DEBIAN/postrm b/DEBIAN/postrm old mode 100644 new mode 100755 diff --git a/DEBIAN/preinst b/DEBIAN/preinst old mode 100644 new mode 100755 diff --git a/DEBIAN/prerm b/DEBIAN/prerm old mode 100644 new mode 100755 diff --git a/libs/scrap/src/common/linux.rs b/libs/scrap/src/common/linux.rs index 06a4ed9e0..2a921477e 100644 --- a/libs/scrap/src/common/linux.rs +++ b/libs/scrap/src/common/linux.rs @@ -2,7 +2,7 @@ use crate::common::{ wayland, x11::{self, Frame}, }; -use std::io; +use std::{io, time::Duration}; pub enum Capturer { X11(x11::Capturer), From 8e121eacd9919e1299624168b34c9b044a7386b6 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 7 Jul 2022 02:05:32 +0800 Subject: [PATCH 44/50] linux_wayland_support: pynput_service.py Signed-off-by: fufesou --- pynput_service.py | 236 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 pynput_service.py diff --git a/pynput_service.py b/pynput_service.py new file mode 100644 index 000000000..c51e9a524 --- /dev/null +++ b/pynput_service.py @@ -0,0 +1,236 @@ +from pynput.keyboard import Key, Controller +from pynput.keyboard._xorg import KeyCode +from pynput._util.xorg import display_manager +import Xlib +from pynput._util.xorg import * +import Xlib +import os +import sys +import socket + +KeyCode._from_symbol("\0") # test + +DEAD_KEYS = { + '`': 65104, + '´': 65105, + '^': 65106, + '~': 65107, + '¯': 65108, + '˘': 65109, + '˙': 65110, + '¨': 65111, + '˚': 65112, + '˝': 65113, + 'ˇ': 65114, + '¸': 65115, + '˛': 65116, + '℩': 65117, # ? + '゛': 65118, # ? + '゚ ': 65119, + 'ٜ': 65120, + '↪': 65121, + ' ̛': 65122, +} + + + +def my_keyboard_mapping(display): + """Generates a mapping from *keysyms* to *key codes* and required + modifier shift states. + + :param Xlib.display.Display display: The display for which to retrieve the + keyboard mapping. + + :return: the keyboard mapping + """ + mapping = {} + + shift_mask = 1 << 0 + group_mask = alt_gr_mask(display) + + # Iterate over all keysym lists in the keyboard mapping + min_keycode = display.display.info.min_keycode + keycode_count = display.display.info.max_keycode - min_keycode + 1 + for index, keysyms in enumerate(display.get_keyboard_mapping( + min_keycode, keycode_count)): + key_code = index + min_keycode + + # Normalise the keysym list to yield a tuple containing the two groups + normalized = keysym_normalize(keysyms) + if not normalized: + continue + + # Iterate over the groups to extract the shift and modifier state + for groups, group in zip(normalized, (False, True)): + for keysym, shift in zip(groups, (False, True)): + + if not keysym: + continue + shift_state = 0 \ + | (shift_mask if shift else 0) \ + | (group_mask if group else 0) + + # !!!: Save all keycode combinations of keysym + if keysym in mapping: + mapping[keysym].append((key_code, shift_state)) + else: + mapping[keysym] = [(key_code, shift_state)] + return mapping + + +class MyController(Controller): + def _update_keyboard_mapping(self): + """Updates the keyboard mapping. + """ + with display_manager(self._display) as dm: + self._keyboard_mapping = my_keyboard_mapping(dm) + + def send_event(self, event, keycode, shift_state): + with display_manager(self._display) as dm, self.modifiers as modifiers: + # Under certain cimcumstances, such as when running under Xephyr, + # the value returned by dm.get_input_focus is an int + window = dm.get_input_focus().focus + send_event = getattr( + window, + 'send_event', + lambda event: dm.send_event(window, event)) + send_event(event( + detail=keycode, + state=shift_state | self._shift_mask(modifiers), + time=0, + root=dm.screen().root, + window=window, + same_screen=0, + child=Xlib.X.NONE, + root_x=0, root_y=0, event_x=0, event_y=0)) + + def fake_input(self, keycode, is_press): + with display_manager(self._display) as dm: + Xlib.ext.xtest.fake_input( + dm, + Xlib.X.KeyPress if is_press else Xlib.X.KeyRelease, + keycode) + + 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) + + if key.vk is not None: + keycode = self._display.keysym_to_keycode(key.vk) + self.fake_input(keycode, is_press) + # Otherwise use XSendEvent; we need to use this in the general case to + # work around problems with keyboard layouts + self._emit('_on_fake_event', key, is_press) + return + + # Make sure to verify that the key was resolved + if keysym is None: + raise self.InvalidKeyException(key) + + # There may be multiple keycodes for keysym in keyboard_mapping + keycode_flag = len(self.keyboard_mapping[keysym]) == 1 + if keycode_flag: + keycode, shift_state = self.keyboard_mapping[keysym][0] + else: + keycode, shift_state = self._display.keysym_to_keycode(keysym), 0 + + keycode_set = set(map(lambda x: x[0], self.keyboard_mapping[keysym])) + # The keycode of the dead key is inconsistent, The keysym has multiple combinations of a keycode. + if keycode != self._display.keysym_to_keycode(keysym) \ + or (keycode_flag == False and keycode == list(keycode_set)[0] and len(keycode_set) == 1): + deakkey_chr = str(key).replace("'", '') + keysym = DEAD_KEYS[deakkey_chr] + keycode, shift_state = self.keyboard_mapping[keysym][0] + + # 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 + + try: + with self.modifiers as modifiers: + alt_gr = Key.alt_gr in modifiers + # !!!: Send_event can't support lock screen, this condition cann't be modified + if alt_gr: + self.send_event( + event, keycode, shift_state) + else: + self.fake_input(keycode, is_press) + 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() + +server_address = sys.argv[1] +if not os.path.exists(os.path.dirname(server_address)): + os.makedirs(os.path.dirname(server_address)) + +try: + os.unlink(server_address) +except OSError: + if os.path.exists(server_address): + raise + +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) +print("Got pynput connection") + + +def loop(): + global keyboard + buf = [] + while True: + data = clientsocket.recv(1024) + if not data: + print("Connection broken") + break + buf.extend(data) + while buf: + n = buf[0] + n = n + 1 + if len(buf) < n: + break + msg = bytearray(buf[1:n]).decode("utf-8") + buf = buf[n:] + if len(msg) < 2: + continue + if msg[1] == "\0": + keyboard = MyController() + print("Keyboard reset") + continue + if len(msg) == 2: + name = msg[1] + else: + name = KeyCode._from_symbol(msg[1:]) + if str(name) == "<0>": + continue + try: + if msg[0] == "p": + keyboard.press(name) + else: + keyboard.release(name) + except Exception as e: + print('[x] error key',e) + + +loop() +clientsocket.close() +server.close() From 8d72dec0881b185ff747ca3bb6829431862fd013 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 6 Jul 2022 19:40:20 -0700 Subject: [PATCH 45/50] linux_wayland_support: debug wayland Signed-off-by: fufesou --- rustdesk.service.user | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rustdesk.service.user b/rustdesk.service.user index 0756ad35e..f6c7454c9 100644 --- a/rustdesk.service.user +++ b/rustdesk.service.user @@ -1,5 +1,5 @@ [Unit] -Description=RustDesk +Description=RustDesk user service (--server) [Service] Type=simple @@ -8,6 +8,8 @@ PIDFile=/run/rustdesk.user.pid KillMode=mixed TimeoutStopSec=30 LimitNOFILE=100000 +Restart=on-failure +RestartSec=3 [Install] WantedBy=multi-user.target From 01bbfcad8e678f7e3d937a534047468a0cae9fca Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 6 Jul 2022 23:16:32 -0700 Subject: [PATCH 46/50] linux_wayland_support: debug multi user login Signed-off-by: fufesou --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 743f0258c..160765a04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2772,7 +2772,7 @@ dependencies = [ [[package]] name = "mouce" version = "0.2.1" -source = "git+https://github.com/fufesou/mouce.git#7da9d9b6597f4c4461881deb4ed49da2385e3cac" +source = "git+https://github.com/fufesou/mouce.git#f503ca2b01c50f46c2a0f61d37cd23a1732f7b55" dependencies = [ "glob", ] From 6721d729e9c7069483613f2ecc08f6737cd3291d Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 7 Jul 2022 20:23:57 +0800 Subject: [PATCH 47/50] linux_wayland_support: gstreamer-1.0 deps Signed-off-by: fufesou --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cd8282187..aa1f5595b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -78,7 +78,7 @@ jobs: shell: bash run: | case ${{ matrix.job.target }} in - x86_64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt install -y g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake ;; + x86_64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt install -y g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev ;; # arm-unknown-linux-*) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;; # aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;; esac From 613b1a858248e4a26ded629825a0171c9a0a1428 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 19 Jul 2022 07:36:13 -0700 Subject: [PATCH 48/50] linux_wayland_support: update crate mouce Signed-off-by: fufesou --- Cargo.lock | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 160765a04..70c76a9cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2772,9 +2772,10 @@ dependencies = [ [[package]] name = "mouce" version = "0.2.1" -source = "git+https://github.com/fufesou/mouce.git#f503ca2b01c50f46c2a0f61d37cd23a1732f7b55" +source = "git+https://github.com/fufesou/mouce.git#26da8d4b0009b7f96996799c2a5c0990a8dbf08b" dependencies = [ "glob", + "libc", ] [[package]] From 634cb5ef1adeec2fc219b46a458a36eb00adb2d9 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 20 Jul 2022 09:50:08 -0700 Subject: [PATCH 49/50] linux_wayland_support: refactor Signed-off-by: fufesou --- src/platform/linux.rs | 2 +- src/server.rs | 8 +- src/server/input_service.rs | 661 +----------------------------------- src/server/uinput.rs | 651 +++++++++++++++++++++++++++++++++++ src/server/video_service.rs | 245 +++---------- src/server/wayland.rs | 179 ++++++++++ 6 files changed, 879 insertions(+), 867 deletions(-) create mode 100644 src/server/uinput.rs create mode 100644 src/server/wayland.rs diff --git a/src/platform/linux.rs b/src/platform/linux.rs index b08793d12..14d43e0e1 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -144,7 +144,7 @@ pub fn get_cursor_data(hcursor: u64) -> ResultType { } fn start_uinput_service() { - use crate::server::input_service::uinput::service; + use crate::server::uinput::service; std::thread::spawn(|| { service::start_service_control(); }); diff --git a/src/server.rs b/src/server.rs index f1171e61d..a71efaee2 100644 --- a/src/server.rs +++ b/src/server.rs @@ -7,7 +7,7 @@ use hbb_common::{ config::{Config, Config2, CONNECT_TIMEOUT, RELAY_PORT}, log, message_proto::*, - protobuf::{Message as _, Enum}, + protobuf::{Enum, Message as _}, rendezvous_proto::*, socket_client, sodiumoxide::crypto::{box_, secretbox, sign}, @@ -24,6 +24,10 @@ pub mod audio_service; cfg_if::cfg_if! { if #[cfg(not(any(target_os = "android", target_os = "ios")))] { mod clipboard_service; +#[cfg(target_os = "linux")] +mod wayland; +#[cfg(target_os = "linux")] +pub mod uinput; pub mod input_service; } else { mod clipboard_service { @@ -280,7 +284,7 @@ impl Drop for Server { s.join(); } #[cfg(target_os = "linux")] - video_service::wayland_support::clear(); + wayland::clear(); } } diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 5d0ebe452..29f4207b3 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -191,9 +191,9 @@ lazy_static::lazy_static! { pub async fn set_uinput() -> ResultType<()> { // Keyboard and mouse both open /dev/uinput // TODO: Make sure there's no race - let keyboard = self::uinput::client::UInputKeyboard::new().await?; + let keyboard = super::uinput::client::UInputKeyboard::new().await?; log::info!("UInput keyboard created"); - let mouse = self::uinput::client::UInputMouse::new().await?; + let mouse = super::uinput::client::UInputMouse::new().await?; log::info!("UInput mouse created"); let mut en = ENIGO.lock().unwrap(); @@ -204,7 +204,7 @@ pub async fn set_uinput() -> ResultType<()> { #[cfg(target_os = "linux")] pub async fn set_uinput_resolution(minx: i32, maxx: i32, miny: i32, maxy: i32) -> ResultType<()> { - self::uinput::client::set_resolution(minx, maxx, miny, maxy).await + super::uinput::client::set_resolution(minx, maxx, miny, maxy).await } pub fn is_left_up(evt: &MouseEvent) -> bool { @@ -759,658 +759,3 @@ async fn send_sas() -> ResultType<()> { timeout(1000, stream.send(&crate::ipc::Data::SAS)).await??; Ok(()) } - -#[cfg(target_os = "linux")] -pub mod uinput { - use crate::ipc::{self, new_listener, Connection, Data, DataKeyboard, DataMouse}; - use enigo::{Key, KeyboardControllable, MouseButton, MouseControllable}; - use evdev::{ - uinput::{VirtualDevice, VirtualDeviceBuilder}, - AttributeSet, EventType, InputEvent, - }; - use hbb_common::{allow_err, bail, log, tokio, ResultType}; - - static IPC_CONN_TIMEOUT: u64 = 1000; - static IPC_REQUEST_TIMEOUT: u64 = 1000; - static IPC_POSTFIX_KEYBOARD: &str = "_uinput_keyboard"; - static IPC_POSTFIX_MOUSE: &str = "_uinput_mouse"; - static IPC_POSTFIX_CONTROL: &str = "_uinput_control"; - - pub mod client { - use super::*; - - pub struct UInputKeyboard { - conn: Connection, - } - - impl UInputKeyboard { - pub async fn new() -> ResultType { - let conn = ipc::connect(IPC_CONN_TIMEOUT, IPC_POSTFIX_KEYBOARD).await?; - Ok(Self { conn }) - } - - #[tokio::main(flavor = "current_thread")] - async fn send(&mut self, data: Data) -> ResultType<()> { - self.conn.send(&data).await - } - - #[tokio::main(flavor = "current_thread")] - async fn send_get_key_state(&mut self, data: Data) -> ResultType { - self.conn.send(&data).await?; - - match self.conn.next_timeout(IPC_REQUEST_TIMEOUT).await { - Ok(Some(Data::KeyboardResponse(ipc::DataKeyboardResponse::GetKeyState( - state, - )))) => Ok(state), - Ok(Some(resp)) => { - // FATAL error!!! - bail!( - "FATAL error, wait keyboard result other response: {:?}", - &resp - ); - } - Ok(None) => { - // FATAL error!!! - // Maybe wait later - bail!("FATAL error, wait keyboard result, receive None",); - } - Err(e) => { - // FATAL error!!! - bail!( - "FATAL error, wait keyboard result timeout {}, {}", - &e, - IPC_REQUEST_TIMEOUT - ); - } - } - } - } - - impl KeyboardControllable for UInputKeyboard { - fn get_key_state(&mut self, key: Key) -> bool { - match self.send_get_key_state(Data::Keyboard(DataKeyboard::GetKeyState(key))) { - Ok(state) => state, - Err(e) => { - // unreachable!() - log::error!("Failed to get key state {}", &e); - false - } - } - } - - fn key_sequence(&mut self, sequence: &str) { - allow_err!(self.send(Data::Keyboard(DataKeyboard::Sequence(sequence.to_string())))); - } - - // TODO: handle error??? - fn key_down(&mut self, key: Key) -> enigo::ResultType { - allow_err!(self.send(Data::Keyboard(DataKeyboard::KeyDown(key)))); - Ok(()) - } - fn key_up(&mut self, key: Key) { - allow_err!(self.send(Data::Keyboard(DataKeyboard::KeyUp(key)))); - } - fn key_click(&mut self, key: Key) { - allow_err!(self.send(Data::Keyboard(DataKeyboard::KeyClick(key)))); - } - } - - pub struct UInputMouse { - conn: Connection, - } - - impl UInputMouse { - pub async fn new() -> ResultType { - let conn = ipc::connect(IPC_CONN_TIMEOUT, IPC_POSTFIX_MOUSE).await?; - Ok(Self { conn }) - } - - #[tokio::main(flavor = "current_thread")] - async fn send(&mut self, data: Data) -> ResultType<()> { - self.conn.send(&data).await - } - } - - impl MouseControllable for UInputMouse { - fn mouse_move_to(&mut self, x: i32, y: i32) { - allow_err!(self.send(Data::Mouse(DataMouse::MoveTo(x, y)))); - } - fn mouse_move_relative(&mut self, x: i32, y: i32) { - allow_err!(self.send(Data::Mouse(DataMouse::MoveRelative(x, y)))); - } - // TODO: handle error??? - fn mouse_down(&mut self, button: MouseButton) -> enigo::ResultType { - allow_err!(self.send(Data::Mouse(DataMouse::Down(button)))); - Ok(()) - } - fn mouse_up(&mut self, button: MouseButton) { - allow_err!(self.send(Data::Mouse(DataMouse::Up(button)))); - } - fn mouse_click(&mut self, button: MouseButton) { - allow_err!(self.send(Data::Mouse(DataMouse::Click(button)))); - } - fn mouse_scroll_x(&mut self, length: i32) { - allow_err!(self.send(Data::Mouse(DataMouse::ScrollX(length)))); - } - fn mouse_scroll_y(&mut self, length: i32) { - allow_err!(self.send(Data::Mouse(DataMouse::ScrollY(length)))); - } - } - - pub async fn set_resolution(minx: i32, maxx: i32, miny: i32, maxy: i32) -> ResultType<()> { - let mut conn = ipc::connect(IPC_CONN_TIMEOUT, IPC_POSTFIX_CONTROL).await?; - conn.send(&Data::Control(ipc::DataControl::Resolution { - minx, - maxx, - miny, - maxy, - })) - .await?; - let _ = conn.next().await?; - Ok(()) - } - } - - pub mod service { - use super::*; - use hbb_common::lazy_static; - use mouce::MouseActions; - use std::{collections::HashMap, sync::Mutex}; - - lazy_static::lazy_static! { - static ref KEY_MAP: HashMap = HashMap::from( - [ - (enigo::Key::Alt, evdev::Key::KEY_LEFTALT), - (enigo::Key::Backspace, evdev::Key::KEY_BACKSPACE), - (enigo::Key::CapsLock, evdev::Key::KEY_CAPSLOCK), - (enigo::Key::Control, evdev::Key::KEY_LEFTCTRL), - (enigo::Key::Delete, evdev::Key::KEY_DELETE), - (enigo::Key::DownArrow, evdev::Key::KEY_DOWN), - (enigo::Key::End, evdev::Key::KEY_END), - (enigo::Key::Escape, evdev::Key::KEY_ESC), - (enigo::Key::F1, evdev::Key::KEY_F1), - (enigo::Key::F10, evdev::Key::KEY_F10), - (enigo::Key::F11, evdev::Key::KEY_F11), - (enigo::Key::F12, evdev::Key::KEY_F12), - (enigo::Key::F2, evdev::Key::KEY_F2), - (enigo::Key::F3, evdev::Key::KEY_F3), - (enigo::Key::F4, evdev::Key::KEY_F4), - (enigo::Key::F5, evdev::Key::KEY_F5), - (enigo::Key::F6, evdev::Key::KEY_F6), - (enigo::Key::F7, evdev::Key::KEY_F7), - (enigo::Key::F8, evdev::Key::KEY_F8), - (enigo::Key::F9, evdev::Key::KEY_F9), - (enigo::Key::Home, evdev::Key::KEY_HOME), - (enigo::Key::LeftArrow, evdev::Key::KEY_LEFT), - (enigo::Key::Meta, evdev::Key::KEY_LEFTMETA), - (enigo::Key::Option, evdev::Key::KEY_OPTION), - (enigo::Key::PageDown, evdev::Key::KEY_PAGEDOWN), - (enigo::Key::PageUp, evdev::Key::KEY_PAGEUP), - (enigo::Key::Return, evdev::Key::KEY_ENTER), - (enigo::Key::RightArrow, evdev::Key::KEY_RIGHT), - (enigo::Key::Shift, evdev::Key::KEY_LEFTSHIFT), - (enigo::Key::Space, evdev::Key::KEY_SPACE), - (enigo::Key::Tab, evdev::Key::KEY_TAB), - (enigo::Key::UpArrow, evdev::Key::KEY_UP), - (enigo::Key::Numpad0, evdev::Key::KEY_KP0), // check if correct? - (enigo::Key::Numpad1, evdev::Key::KEY_KP1), - (enigo::Key::Numpad2, evdev::Key::KEY_KP2), - (enigo::Key::Numpad3, evdev::Key::KEY_KP3), - (enigo::Key::Numpad4, evdev::Key::KEY_KP4), - (enigo::Key::Numpad5, evdev::Key::KEY_KP5), - (enigo::Key::Numpad6, evdev::Key::KEY_KP6), - (enigo::Key::Numpad7, evdev::Key::KEY_KP7), - (enigo::Key::Numpad8, evdev::Key::KEY_KP8), - (enigo::Key::Numpad9, evdev::Key::KEY_KP9), - (enigo::Key::Cancel, evdev::Key::KEY_CANCEL), - (enigo::Key::Clear, evdev::Key::KEY_CLEAR), - (enigo::Key::Alt, evdev::Key::KEY_LEFTALT), - (enigo::Key::Pause, evdev::Key::KEY_PAUSE), - (enigo::Key::Kana, evdev::Key::KEY_KATAKANA), // check if correct? - (enigo::Key::Hangul, evdev::Key::KEY_HANGEUL), // check if correct? - // (enigo::Key::Junja, evdev::Key::KEY_JUNJA), // map? - // (enigo::Key::Final, evdev::Key::KEY_FINAL), // map? - (enigo::Key::Hanja, evdev::Key::KEY_HANJA), - // (enigo::Key::Kanji, evdev::Key::KEY_KANJI), // map? - // (enigo::Key::Convert, evdev::Key::KEY_CONVERT), - (enigo::Key::Select, evdev::Key::KEY_SELECT), - (enigo::Key::Print, evdev::Key::KEY_PRINT), - // (enigo::Key::Execute, evdev::Key::KEY_EXECUTE), - // (enigo::Key::Snapshot, evdev::Key::KEY_SNAPSHOT), - (enigo::Key::Insert, evdev::Key::KEY_INSERT), - (enigo::Key::Help, evdev::Key::KEY_HELP), - (enigo::Key::Sleep, evdev::Key::KEY_SLEEP), - // (enigo::Key::Separator, evdev::Key::KEY_SEPARATOR), - (enigo::Key::Scroll, evdev::Key::KEY_SCROLLLOCK), - (enigo::Key::NumLock, evdev::Key::KEY_NUMLOCK), - (enigo::Key::RWin, evdev::Key::KEY_RIGHTMETA), - (enigo::Key::Apps, evdev::Key::KEY_CONTEXT_MENU), - (enigo::Key::Multiply, evdev::Key::KEY_KPASTERISK), - (enigo::Key::Add, evdev::Key::KEY_KPPLUS), - (enigo::Key::Subtract, evdev::Key::KEY_KPMINUS), - (enigo::Key::Decimal, evdev::Key::KEY_KPCOMMA), // KEY_KPDOT and KEY_KPCOMMA are exchanged? - (enigo::Key::Divide, evdev::Key::KEY_KPSLASH), - (enigo::Key::Equals, evdev::Key::KEY_KPEQUAL), - (enigo::Key::NumpadEnter, evdev::Key::KEY_KPENTER), - (enigo::Key::RightAlt, evdev::Key::KEY_RIGHTALT), - (enigo::Key::RightControl, evdev::Key::KEY_RIGHTCTRL), - (enigo::Key::RightShift, evdev::Key::KEY_RIGHTSHIFT), - ]); - - static ref KEY_MAP_LAYOUT: HashMap = HashMap::from( - [ - ('a', evdev::Key::KEY_A), - ('b', evdev::Key::KEY_B), - ('c', evdev::Key::KEY_C), - ('d', evdev::Key::KEY_D), - ('e', evdev::Key::KEY_E), - ('f', evdev::Key::KEY_F), - ('g', evdev::Key::KEY_G), - ('h', evdev::Key::KEY_H), - ('i', evdev::Key::KEY_I), - ('j', evdev::Key::KEY_J), - ('k', evdev::Key::KEY_K), - ('l', evdev::Key::KEY_L), - ('m', evdev::Key::KEY_M), - ('n', evdev::Key::KEY_N), - ('o', evdev::Key::KEY_O), - ('p', evdev::Key::KEY_P), - ('q', evdev::Key::KEY_Q), - ('r', evdev::Key::KEY_R), - ('s', evdev::Key::KEY_S), - ('t', evdev::Key::KEY_T), - ('u', evdev::Key::KEY_U), - ('v', evdev::Key::KEY_V), - ('w', evdev::Key::KEY_W), - ('x', evdev::Key::KEY_X), - ('y', evdev::Key::KEY_Y), - ('z', evdev::Key::KEY_Z), - ('0', evdev::Key::KEY_0), - ('1', evdev::Key::KEY_1), - ('2', evdev::Key::KEY_2), - ('3', evdev::Key::KEY_3), - ('4', evdev::Key::KEY_4), - ('5', evdev::Key::KEY_5), - ('6', evdev::Key::KEY_6), - ('7', evdev::Key::KEY_7), - ('8', evdev::Key::KEY_8), - ('9', evdev::Key::KEY_9), - ('`', evdev::Key::KEY_GRAVE), - ('-', evdev::Key::KEY_MINUS), - ('=', evdev::Key::KEY_EQUAL), - ('[', evdev::Key::KEY_LEFTBRACE), - (']', evdev::Key::KEY_RIGHTBRACE), - ('\\', evdev::Key::KEY_BACKSLASH), - (',', evdev::Key::KEY_COMMA), - ('.', evdev::Key::KEY_DOT), - ('/', evdev::Key::KEY_SLASH), - (';', evdev::Key::KEY_SEMICOLON), - ('\'', evdev::Key::KEY_APOSTROPHE), - ]); - - // ((minx, maxx), (miny, maxy)) - static ref RESOLUTION: Mutex<((i32, i32), (i32, i32))> = Mutex::new(((0, 0), (0, 0))); - } - - fn create_uinput_keyboard() -> ResultType { - // TODO: ensure keys here - let mut keys = AttributeSet::::new(); - for i in evdev::Key::KEY_ESC.code()..(evdev::Key::BTN_TRIGGER_HAPPY40.code() + 1) { - let key = evdev::Key::new(i); - if !format!("{:?}", &key).contains("unknown key") { - keys.insert(key); - } - } - let mut leds = AttributeSet::::new(); - leds.insert(evdev::LedType::LED_NUML); - leds.insert(evdev::LedType::LED_CAPSL); - leds.insert(evdev::LedType::LED_SCROLLL); - let mut miscs = AttributeSet::::new(); - miscs.insert(evdev::MiscType::MSC_SCAN); - let keyboard = VirtualDeviceBuilder::new()? - .name("RustDesk UInput Keyboard") - .with_keys(&keys)? - .with_leds(&leds)? - .with_miscs(&miscs)? - .build()?; - Ok(keyboard) - } - - fn map_key(key: &enigo::Key) -> ResultType { - if let Some(k) = KEY_MAP.get(&key) { - log::trace!("mapkey {:?}, get {:?}", &key, &k); - return Ok(k.clone()); - } else { - match key { - enigo::Key::Layout(c) => { - if let Some(k) = KEY_MAP_LAYOUT.get(&c) { - log::trace!("mapkey {:?}, get {:?}", &key, k); - return Ok(k.clone()); - } - } - // enigo::Key::Raw(c) => { - // let k = evdev::Key::new(c); - // if !format!("{:?}", &k).contains("unknown key") { - // return Ok(k.clone()); - // } - // } - _ => {} - } - } - bail!("Failed to map key {:?}", &key); - } - - async fn ipc_send_data(stream: &mut Connection, data: &Data) { - allow_err!(stream.send(data).await); - } - - async fn handle_keyboard( - stream: &mut Connection, - keyboard: &mut VirtualDevice, - data: &DataKeyboard, - ) { - log::trace!("handle_keyboard {:?}", &data); - match data { - DataKeyboard::Sequence(_seq) => { - // ignore - } - DataKeyboard::KeyDown(key) => { - if let Ok(k) = map_key(key) { - let down_event = InputEvent::new(EventType::KEY, k.code(), 1); - allow_err!(keyboard.emit(&[down_event])); - } - } - DataKeyboard::KeyUp(key) => { - if let Ok(k) = map_key(key) { - let up_event = InputEvent::new(EventType::KEY, k.code(), 0); - allow_err!(keyboard.emit(&[up_event])); - } - } - DataKeyboard::KeyClick(key) => { - if let Ok(k) = map_key(key) { - let down_event = InputEvent::new(EventType::KEY, k.code(), 1); - let up_event = InputEvent::new(EventType::KEY, k.code(), 0); - allow_err!(keyboard.emit(&[down_event, up_event])); - } - } - DataKeyboard::GetKeyState(key) => { - let key_state = if enigo::Key::CapsLock == *key { - match keyboard.get_led_state() { - Ok(leds) => leds.contains(evdev::LedType::LED_CAPSL), - Err(_e) => { - // log::debug!("Failed to get led state {}", &_e); - false - } - } - } else { - match keyboard.get_key_state() { - Ok(keys) => match key { - enigo::Key::Shift => { - keys.contains(evdev::Key::KEY_LEFTSHIFT) - || keys.contains(evdev::Key::KEY_RIGHTSHIFT) - } - enigo::Key::Control => { - keys.contains(evdev::Key::KEY_LEFTCTRL) - || keys.contains(evdev::Key::KEY_RIGHTCTRL) - } - enigo::Key::Alt => { - keys.contains(evdev::Key::KEY_LEFTALT) - || keys.contains(evdev::Key::KEY_RIGHTALT) - } - enigo::Key::NumLock => keys.contains(evdev::Key::KEY_NUMLOCK), - enigo::Key::Meta => { - keys.contains(evdev::Key::KEY_LEFTMETA) - || keys.contains(evdev::Key::KEY_RIGHTMETA) - } - _ => false, - }, - Err(_e) => { - // log::debug!("Failed to get key state: {}", &_e); - false - } - } - }; - ipc_send_data( - stream, - &Data::KeyboardResponse(ipc::DataKeyboardResponse::GetKeyState(key_state)), - ) - .await; - } - } - } - - fn handle_mouse(mouse: &mut mouce::nix::UInputMouseManager, data: &DataMouse) { - log::trace!("handle_mouse {:?}", &data); - match data { - DataMouse::MoveTo(x, y) => { - allow_err!(mouse.move_to(*x as _, *y as _)) - } - DataMouse::MoveRelative(x, y) => { - allow_err!(mouse.move_relative(*x, *y)) - } - DataMouse::Down(button) => { - let btn = match button { - enigo::MouseButton::Left => mouce::common::MouseButton::Left, - enigo::MouseButton::Middle => mouce::common::MouseButton::Middle, - enigo::MouseButton::Right => mouce::common::MouseButton::Right, - _ => { - return; - } - }; - allow_err!(mouse.press_button(&btn)) - } - DataMouse::Up(button) => { - let btn = match button { - enigo::MouseButton::Left => mouce::common::MouseButton::Left, - enigo::MouseButton::Middle => mouce::common::MouseButton::Middle, - enigo::MouseButton::Right => mouce::common::MouseButton::Right, - _ => { - return; - } - }; - allow_err!(mouse.release_button(&btn)) - } - DataMouse::Click(button) => { - let btn = match button { - enigo::MouseButton::Left => mouce::common::MouseButton::Left, - enigo::MouseButton::Middle => mouce::common::MouseButton::Middle, - enigo::MouseButton::Right => mouce::common::MouseButton::Right, - _ => { - return; - } - }; - allow_err!(mouse.click_button(&btn)) - } - DataMouse::ScrollX(_length) => { - // TODO: not supported for now - } - DataMouse::ScrollY(length) => { - let mut length = *length; - - let scroll = if length < 0 { - mouce::common::ScrollDirection::Up - } else { - mouce::common::ScrollDirection::Down - }; - - if length < 0 { - length = -length; - } - - for _ in 0..length { - allow_err!(mouse.scroll_wheel(&scroll)) - } - } - } - } - - fn spawn_keyboard_handler(mut stream: Connection) { - tokio::spawn(async move { - let mut keyboard = match create_uinput_keyboard() { - Ok(keyboard) => keyboard, - Err(e) => { - log::error!("Failed to create keyboard {}", e); - return; - } - }; - loop { - tokio::select! { - res = stream.next() => { - match res { - Err(err) => { - log::info!("UInput keyboard ipc connection closed: {}", err); - break; - } - Ok(Some(data)) => { - match data { - Data::Keyboard(data) => { - handle_keyboard(&mut stream, &mut keyboard, &data).await; - } - _ => { - } - } - } - _ => {} - } - } - } - } - }); - } - - fn spawn_mouse_handler(mut stream: ipc::Connection) { - let resolution = RESOLUTION.lock().unwrap(); - if resolution.0 .0 == resolution.0 .1 || resolution.1 .0 == resolution.1 .1 { - return; - } - let rng_x = resolution.0.clone(); - let rng_y = resolution.1.clone(); - tokio::spawn(async move { - log::info!( - "Create uinput mouce with rng_x: ({}, {}), rng_y: ({}, {})", - rng_x.0, - rng_x.1, - rng_y.0, - rng_y.1 - ); - let mut mouse = match mouce::Mouse::new_uinput(rng_x, rng_y) { - Ok(mouse) => mouse, - Err(e) => { - log::error!("Failed to create mouse, {}", e); - return; - } - }; - loop { - tokio::select! { - res = stream.next() => { - match res { - Err(err) => { - log::info!("UInput mouse ipc connection closed: {}", err); - break; - } - Ok(Some(data)) => { - match data { - Data::Mouse(data) => { - handle_mouse(&mut mouse, &data); - } - _ => { - } - } - } - _ => {} - } - } - } - } - }); - } - - fn spawn_controller_handler(mut stream: ipc::Connection) { - tokio::spawn(async move { - loop { - tokio::select! { - res = stream.next() => { - match res { - Err(_err) => { - // log::info!("UInput controller ipc connection closed: {}", err); - break; - } - Ok(Some(data)) => { - match data { - Data::Control(data) => match data { - ipc::DataControl::Resolution{ - minx, - maxx, - miny, - maxy, - } => { - *RESOLUTION.lock().unwrap() = ((minx, maxx), (miny, maxy)); - allow_err!(stream.send(&Data::Empty).await); - } - } - _ => { - } - } - } - _ => {} - } - } - } - } - }); - } - - /// Start uinput service. - async fn start_service(postfix: &str, handler: F) { - match new_listener(postfix).await { - Ok(mut incoming) => { - while let Some(result) = incoming.next().await { - match result { - Ok(stream) => { - log::debug!("Got new connection of uinput ipc {}", postfix); - handler(Connection::new(stream)); - } - Err(err) => { - log::error!("Couldn't get uinput mouse client: {:?}", err); - } - } - } - } - Err(err) => { - log::error!("Failed to start uinput mouse ipc service: {}", err); - } - } - } - - /// Start uinput keyboard service. - #[tokio::main(flavor = "current_thread")] - pub async fn start_service_keyboard() { - log::info!("start uinput keyboard service"); - start_service(IPC_POSTFIX_KEYBOARD, spawn_keyboard_handler).await; - } - - /// Start uinput mouse service. - #[tokio::main(flavor = "current_thread")] - pub async fn start_service_mouse() { - log::info!("start uinput mouse service"); - start_service(IPC_POSTFIX_MOUSE, spawn_mouse_handler).await; - } - - /// Start uinput mouse service. - #[tokio::main(flavor = "current_thread")] - pub async fn start_service_control() { - log::info!("start uinput control service"); - start_service(IPC_POSTFIX_CONTROL, spawn_controller_handler).await; - } - - pub fn stop_service_keyboard() { - log::info!("stop uinput keyboard service"); - } - pub fn stop_service_mouse() { - log::info!("stop uinput mouse service"); - } - pub fn stop_service_control() { - log::info!("stop uinput control service"); - } - } -} diff --git a/src/server/uinput.rs b/src/server/uinput.rs new file mode 100644 index 000000000..7a6d47cff --- /dev/null +++ b/src/server/uinput.rs @@ -0,0 +1,651 @@ +use crate::ipc::{self, new_listener, Connection, Data, DataKeyboard, DataMouse}; +use enigo::{Key, KeyboardControllable, MouseButton, MouseControllable}; +use evdev::{ + uinput::{VirtualDevice, VirtualDeviceBuilder}, + AttributeSet, EventType, InputEvent, +}; +use hbb_common::{allow_err, bail, log, tokio, ResultType}; + +static IPC_CONN_TIMEOUT: u64 = 1000; +static IPC_REQUEST_TIMEOUT: u64 = 1000; +static IPC_POSTFIX_KEYBOARD: &str = "_uinput_keyboard"; +static IPC_POSTFIX_MOUSE: &str = "_uinput_mouse"; +static IPC_POSTFIX_CONTROL: &str = "_uinput_control"; + +pub mod client { + use super::*; + + pub struct UInputKeyboard { + conn: Connection, + } + + impl UInputKeyboard { + pub async fn new() -> ResultType { + let conn = ipc::connect(IPC_CONN_TIMEOUT, IPC_POSTFIX_KEYBOARD).await?; + Ok(Self { conn }) + } + + #[tokio::main(flavor = "current_thread")] + async fn send(&mut self, data: Data) -> ResultType<()> { + self.conn.send(&data).await + } + + #[tokio::main(flavor = "current_thread")] + async fn send_get_key_state(&mut self, data: Data) -> ResultType { + self.conn.send(&data).await?; + + match self.conn.next_timeout(IPC_REQUEST_TIMEOUT).await { + Ok(Some(Data::KeyboardResponse(ipc::DataKeyboardResponse::GetKeyState(state)))) => { + Ok(state) + } + Ok(Some(resp)) => { + // FATAL error!!! + bail!( + "FATAL error, wait keyboard result other response: {:?}", + &resp + ); + } + Ok(None) => { + // FATAL error!!! + // Maybe wait later + bail!("FATAL error, wait keyboard result, receive None",); + } + Err(e) => { + // FATAL error!!! + bail!( + "FATAL error, wait keyboard result timeout {}, {}", + &e, + IPC_REQUEST_TIMEOUT + ); + } + } + } + } + + impl KeyboardControllable for UInputKeyboard { + fn get_key_state(&mut self, key: Key) -> bool { + match self.send_get_key_state(Data::Keyboard(DataKeyboard::GetKeyState(key))) { + Ok(state) => state, + Err(e) => { + // unreachable!() + log::error!("Failed to get key state {}", &e); + false + } + } + } + + fn key_sequence(&mut self, sequence: &str) { + allow_err!(self.send(Data::Keyboard(DataKeyboard::Sequence(sequence.to_string())))); + } + + // TODO: handle error??? + fn key_down(&mut self, key: Key) -> enigo::ResultType { + allow_err!(self.send(Data::Keyboard(DataKeyboard::KeyDown(key)))); + Ok(()) + } + fn key_up(&mut self, key: Key) { + allow_err!(self.send(Data::Keyboard(DataKeyboard::KeyUp(key)))); + } + fn key_click(&mut self, key: Key) { + allow_err!(self.send(Data::Keyboard(DataKeyboard::KeyClick(key)))); + } + } + + pub struct UInputMouse { + conn: Connection, + } + + impl UInputMouse { + pub async fn new() -> ResultType { + let conn = ipc::connect(IPC_CONN_TIMEOUT, IPC_POSTFIX_MOUSE).await?; + Ok(Self { conn }) + } + + #[tokio::main(flavor = "current_thread")] + async fn send(&mut self, data: Data) -> ResultType<()> { + self.conn.send(&data).await + } + } + + impl MouseControllable for UInputMouse { + fn mouse_move_to(&mut self, x: i32, y: i32) { + allow_err!(self.send(Data::Mouse(DataMouse::MoveTo(x, y)))); + } + fn mouse_move_relative(&mut self, x: i32, y: i32) { + allow_err!(self.send(Data::Mouse(DataMouse::MoveRelative(x, y)))); + } + // TODO: handle error??? + fn mouse_down(&mut self, button: MouseButton) -> enigo::ResultType { + allow_err!(self.send(Data::Mouse(DataMouse::Down(button)))); + Ok(()) + } + fn mouse_up(&mut self, button: MouseButton) { + allow_err!(self.send(Data::Mouse(DataMouse::Up(button)))); + } + fn mouse_click(&mut self, button: MouseButton) { + allow_err!(self.send(Data::Mouse(DataMouse::Click(button)))); + } + fn mouse_scroll_x(&mut self, length: i32) { + allow_err!(self.send(Data::Mouse(DataMouse::ScrollX(length)))); + } + fn mouse_scroll_y(&mut self, length: i32) { + allow_err!(self.send(Data::Mouse(DataMouse::ScrollY(length)))); + } + } + + pub async fn set_resolution(minx: i32, maxx: i32, miny: i32, maxy: i32) -> ResultType<()> { + let mut conn = ipc::connect(IPC_CONN_TIMEOUT, IPC_POSTFIX_CONTROL).await?; + conn.send(&Data::Control(ipc::DataControl::Resolution { + minx, + maxx, + miny, + maxy, + })) + .await?; + let _ = conn.next().await?; + Ok(()) + } +} + +pub mod service { + use super::*; + use hbb_common::lazy_static; + use mouce::MouseActions; + use std::{collections::HashMap, sync::Mutex}; + + lazy_static::lazy_static! { + static ref KEY_MAP: HashMap = HashMap::from( + [ + (enigo::Key::Alt, evdev::Key::KEY_LEFTALT), + (enigo::Key::Backspace, evdev::Key::KEY_BACKSPACE), + (enigo::Key::CapsLock, evdev::Key::KEY_CAPSLOCK), + (enigo::Key::Control, evdev::Key::KEY_LEFTCTRL), + (enigo::Key::Delete, evdev::Key::KEY_DELETE), + (enigo::Key::DownArrow, evdev::Key::KEY_DOWN), + (enigo::Key::End, evdev::Key::KEY_END), + (enigo::Key::Escape, evdev::Key::KEY_ESC), + (enigo::Key::F1, evdev::Key::KEY_F1), + (enigo::Key::F10, evdev::Key::KEY_F10), + (enigo::Key::F11, evdev::Key::KEY_F11), + (enigo::Key::F12, evdev::Key::KEY_F12), + (enigo::Key::F2, evdev::Key::KEY_F2), + (enigo::Key::F3, evdev::Key::KEY_F3), + (enigo::Key::F4, evdev::Key::KEY_F4), + (enigo::Key::F5, evdev::Key::KEY_F5), + (enigo::Key::F6, evdev::Key::KEY_F6), + (enigo::Key::F7, evdev::Key::KEY_F7), + (enigo::Key::F8, evdev::Key::KEY_F8), + (enigo::Key::F9, evdev::Key::KEY_F9), + (enigo::Key::Home, evdev::Key::KEY_HOME), + (enigo::Key::LeftArrow, evdev::Key::KEY_LEFT), + (enigo::Key::Meta, evdev::Key::KEY_LEFTMETA), + (enigo::Key::Option, evdev::Key::KEY_OPTION), + (enigo::Key::PageDown, evdev::Key::KEY_PAGEDOWN), + (enigo::Key::PageUp, evdev::Key::KEY_PAGEUP), + (enigo::Key::Return, evdev::Key::KEY_ENTER), + (enigo::Key::RightArrow, evdev::Key::KEY_RIGHT), + (enigo::Key::Shift, evdev::Key::KEY_LEFTSHIFT), + (enigo::Key::Space, evdev::Key::KEY_SPACE), + (enigo::Key::Tab, evdev::Key::KEY_TAB), + (enigo::Key::UpArrow, evdev::Key::KEY_UP), + (enigo::Key::Numpad0, evdev::Key::KEY_KP0), // check if correct? + (enigo::Key::Numpad1, evdev::Key::KEY_KP1), + (enigo::Key::Numpad2, evdev::Key::KEY_KP2), + (enigo::Key::Numpad3, evdev::Key::KEY_KP3), + (enigo::Key::Numpad4, evdev::Key::KEY_KP4), + (enigo::Key::Numpad5, evdev::Key::KEY_KP5), + (enigo::Key::Numpad6, evdev::Key::KEY_KP6), + (enigo::Key::Numpad7, evdev::Key::KEY_KP7), + (enigo::Key::Numpad8, evdev::Key::KEY_KP8), + (enigo::Key::Numpad9, evdev::Key::KEY_KP9), + (enigo::Key::Cancel, evdev::Key::KEY_CANCEL), + (enigo::Key::Clear, evdev::Key::KEY_CLEAR), + (enigo::Key::Alt, evdev::Key::KEY_LEFTALT), + (enigo::Key::Pause, evdev::Key::KEY_PAUSE), + (enigo::Key::Kana, evdev::Key::KEY_KATAKANA), // check if correct? + (enigo::Key::Hangul, evdev::Key::KEY_HANGEUL), // check if correct? + // (enigo::Key::Junja, evdev::Key::KEY_JUNJA), // map? + // (enigo::Key::Final, evdev::Key::KEY_FINAL), // map? + (enigo::Key::Hanja, evdev::Key::KEY_HANJA), + // (enigo::Key::Kanji, evdev::Key::KEY_KANJI), // map? + // (enigo::Key::Convert, evdev::Key::KEY_CONVERT), + (enigo::Key::Select, evdev::Key::KEY_SELECT), + (enigo::Key::Print, evdev::Key::KEY_PRINT), + // (enigo::Key::Execute, evdev::Key::KEY_EXECUTE), + // (enigo::Key::Snapshot, evdev::Key::KEY_SNAPSHOT), + (enigo::Key::Insert, evdev::Key::KEY_INSERT), + (enigo::Key::Help, evdev::Key::KEY_HELP), + (enigo::Key::Sleep, evdev::Key::KEY_SLEEP), + // (enigo::Key::Separator, evdev::Key::KEY_SEPARATOR), + (enigo::Key::Scroll, evdev::Key::KEY_SCROLLLOCK), + (enigo::Key::NumLock, evdev::Key::KEY_NUMLOCK), + (enigo::Key::RWin, evdev::Key::KEY_RIGHTMETA), + (enigo::Key::Apps, evdev::Key::KEY_CONTEXT_MENU), + (enigo::Key::Multiply, evdev::Key::KEY_KPASTERISK), + (enigo::Key::Add, evdev::Key::KEY_KPPLUS), + (enigo::Key::Subtract, evdev::Key::KEY_KPMINUS), + (enigo::Key::Decimal, evdev::Key::KEY_KPCOMMA), // KEY_KPDOT and KEY_KPCOMMA are exchanged? + (enigo::Key::Divide, evdev::Key::KEY_KPSLASH), + (enigo::Key::Equals, evdev::Key::KEY_KPEQUAL), + (enigo::Key::NumpadEnter, evdev::Key::KEY_KPENTER), + (enigo::Key::RightAlt, evdev::Key::KEY_RIGHTALT), + (enigo::Key::RightControl, evdev::Key::KEY_RIGHTCTRL), + (enigo::Key::RightShift, evdev::Key::KEY_RIGHTSHIFT), + ]); + + static ref KEY_MAP_LAYOUT: HashMap = HashMap::from( + [ + ('a', evdev::Key::KEY_A), + ('b', evdev::Key::KEY_B), + ('c', evdev::Key::KEY_C), + ('d', evdev::Key::KEY_D), + ('e', evdev::Key::KEY_E), + ('f', evdev::Key::KEY_F), + ('g', evdev::Key::KEY_G), + ('h', evdev::Key::KEY_H), + ('i', evdev::Key::KEY_I), + ('j', evdev::Key::KEY_J), + ('k', evdev::Key::KEY_K), + ('l', evdev::Key::KEY_L), + ('m', evdev::Key::KEY_M), + ('n', evdev::Key::KEY_N), + ('o', evdev::Key::KEY_O), + ('p', evdev::Key::KEY_P), + ('q', evdev::Key::KEY_Q), + ('r', evdev::Key::KEY_R), + ('s', evdev::Key::KEY_S), + ('t', evdev::Key::KEY_T), + ('u', evdev::Key::KEY_U), + ('v', evdev::Key::KEY_V), + ('w', evdev::Key::KEY_W), + ('x', evdev::Key::KEY_X), + ('y', evdev::Key::KEY_Y), + ('z', evdev::Key::KEY_Z), + ('0', evdev::Key::KEY_0), + ('1', evdev::Key::KEY_1), + ('2', evdev::Key::KEY_2), + ('3', evdev::Key::KEY_3), + ('4', evdev::Key::KEY_4), + ('5', evdev::Key::KEY_5), + ('6', evdev::Key::KEY_6), + ('7', evdev::Key::KEY_7), + ('8', evdev::Key::KEY_8), + ('9', evdev::Key::KEY_9), + ('`', evdev::Key::KEY_GRAVE), + ('-', evdev::Key::KEY_MINUS), + ('=', evdev::Key::KEY_EQUAL), + ('[', evdev::Key::KEY_LEFTBRACE), + (']', evdev::Key::KEY_RIGHTBRACE), + ('\\', evdev::Key::KEY_BACKSLASH), + (',', evdev::Key::KEY_COMMA), + ('.', evdev::Key::KEY_DOT), + ('/', evdev::Key::KEY_SLASH), + (';', evdev::Key::KEY_SEMICOLON), + ('\'', evdev::Key::KEY_APOSTROPHE), + ]); + + // ((minx, maxx), (miny, maxy)) + static ref RESOLUTION: Mutex<((i32, i32), (i32, i32))> = Mutex::new(((0, 0), (0, 0))); + } + + fn create_uinput_keyboard() -> ResultType { + // TODO: ensure keys here + let mut keys = AttributeSet::::new(); + for i in evdev::Key::KEY_ESC.code()..(evdev::Key::BTN_TRIGGER_HAPPY40.code() + 1) { + let key = evdev::Key::new(i); + if !format!("{:?}", &key).contains("unknown key") { + keys.insert(key); + } + } + let mut leds = AttributeSet::::new(); + leds.insert(evdev::LedType::LED_NUML); + leds.insert(evdev::LedType::LED_CAPSL); + leds.insert(evdev::LedType::LED_SCROLLL); + let mut miscs = AttributeSet::::new(); + miscs.insert(evdev::MiscType::MSC_SCAN); + let keyboard = VirtualDeviceBuilder::new()? + .name("RustDesk UInput Keyboard") + .with_keys(&keys)? + .with_leds(&leds)? + .with_miscs(&miscs)? + .build()?; + Ok(keyboard) + } + + fn map_key(key: &enigo::Key) -> ResultType { + if let Some(k) = KEY_MAP.get(&key) { + log::trace!("mapkey {:?}, get {:?}", &key, &k); + return Ok(k.clone()); + } else { + match key { + enigo::Key::Layout(c) => { + if let Some(k) = KEY_MAP_LAYOUT.get(&c) { + log::trace!("mapkey {:?}, get {:?}", &key, k); + return Ok(k.clone()); + } + } + // enigo::Key::Raw(c) => { + // let k = evdev::Key::new(c); + // if !format!("{:?}", &k).contains("unknown key") { + // return Ok(k.clone()); + // } + // } + _ => {} + } + } + bail!("Failed to map key {:?}", &key); + } + + async fn ipc_send_data(stream: &mut Connection, data: &Data) { + allow_err!(stream.send(data).await); + } + + async fn handle_keyboard( + stream: &mut Connection, + keyboard: &mut VirtualDevice, + data: &DataKeyboard, + ) { + log::trace!("handle_keyboard {:?}", &data); + match data { + DataKeyboard::Sequence(_seq) => { + // ignore + } + DataKeyboard::KeyDown(key) => { + if let Ok(k) = map_key(key) { + let down_event = InputEvent::new(EventType::KEY, k.code(), 1); + allow_err!(keyboard.emit(&[down_event])); + } + } + DataKeyboard::KeyUp(key) => { + if let Ok(k) = map_key(key) { + let up_event = InputEvent::new(EventType::KEY, k.code(), 0); + allow_err!(keyboard.emit(&[up_event])); + } + } + DataKeyboard::KeyClick(key) => { + if let Ok(k) = map_key(key) { + let down_event = InputEvent::new(EventType::KEY, k.code(), 1); + let up_event = InputEvent::new(EventType::KEY, k.code(), 0); + allow_err!(keyboard.emit(&[down_event, up_event])); + } + } + DataKeyboard::GetKeyState(key) => { + let key_state = if enigo::Key::CapsLock == *key { + match keyboard.get_led_state() { + Ok(leds) => leds.contains(evdev::LedType::LED_CAPSL), + Err(_e) => { + // log::debug!("Failed to get led state {}", &_e); + false + } + } + } else { + match keyboard.get_key_state() { + Ok(keys) => match key { + enigo::Key::Shift => { + keys.contains(evdev::Key::KEY_LEFTSHIFT) + || keys.contains(evdev::Key::KEY_RIGHTSHIFT) + } + enigo::Key::Control => { + keys.contains(evdev::Key::KEY_LEFTCTRL) + || keys.contains(evdev::Key::KEY_RIGHTCTRL) + } + enigo::Key::Alt => { + keys.contains(evdev::Key::KEY_LEFTALT) + || keys.contains(evdev::Key::KEY_RIGHTALT) + } + enigo::Key::NumLock => keys.contains(evdev::Key::KEY_NUMLOCK), + enigo::Key::Meta => { + keys.contains(evdev::Key::KEY_LEFTMETA) + || keys.contains(evdev::Key::KEY_RIGHTMETA) + } + _ => false, + }, + Err(_e) => { + // log::debug!("Failed to get key state: {}", &_e); + false + } + } + }; + ipc_send_data( + stream, + &Data::KeyboardResponse(ipc::DataKeyboardResponse::GetKeyState(key_state)), + ) + .await; + } + } + } + + fn handle_mouse(mouse: &mut mouce::nix::UInputMouseManager, data: &DataMouse) { + log::trace!("handle_mouse {:?}", &data); + match data { + DataMouse::MoveTo(x, y) => { + allow_err!(mouse.move_to(*x as _, *y as _)) + } + DataMouse::MoveRelative(x, y) => { + allow_err!(mouse.move_relative(*x, *y)) + } + DataMouse::Down(button) => { + let btn = match button { + enigo::MouseButton::Left => mouce::common::MouseButton::Left, + enigo::MouseButton::Middle => mouce::common::MouseButton::Middle, + enigo::MouseButton::Right => mouce::common::MouseButton::Right, + _ => { + return; + } + }; + allow_err!(mouse.press_button(&btn)) + } + DataMouse::Up(button) => { + let btn = match button { + enigo::MouseButton::Left => mouce::common::MouseButton::Left, + enigo::MouseButton::Middle => mouce::common::MouseButton::Middle, + enigo::MouseButton::Right => mouce::common::MouseButton::Right, + _ => { + return; + } + }; + allow_err!(mouse.release_button(&btn)) + } + DataMouse::Click(button) => { + let btn = match button { + enigo::MouseButton::Left => mouce::common::MouseButton::Left, + enigo::MouseButton::Middle => mouce::common::MouseButton::Middle, + enigo::MouseButton::Right => mouce::common::MouseButton::Right, + _ => { + return; + } + }; + allow_err!(mouse.click_button(&btn)) + } + DataMouse::ScrollX(_length) => { + // TODO: not supported for now + } + DataMouse::ScrollY(length) => { + let mut length = *length; + + let scroll = if length < 0 { + mouce::common::ScrollDirection::Up + } else { + mouce::common::ScrollDirection::Down + }; + + if length < 0 { + length = -length; + } + + for _ in 0..length { + allow_err!(mouse.scroll_wheel(&scroll)) + } + } + } + } + + fn spawn_keyboard_handler(mut stream: Connection) { + tokio::spawn(async move { + let mut keyboard = match create_uinput_keyboard() { + Ok(keyboard) => keyboard, + Err(e) => { + log::error!("Failed to create keyboard {}", e); + return; + } + }; + loop { + tokio::select! { + res = stream.next() => { + match res { + Err(err) => { + log::info!("UInput keyboard ipc connection closed: {}", err); + break; + } + Ok(Some(data)) => { + match data { + Data::Keyboard(data) => { + handle_keyboard(&mut stream, &mut keyboard, &data).await; + } + _ => { + } + } + } + _ => {} + } + } + } + } + }); + } + + fn spawn_mouse_handler(mut stream: ipc::Connection) { + let resolution = RESOLUTION.lock().unwrap(); + if resolution.0 .0 == resolution.0 .1 || resolution.1 .0 == resolution.1 .1 { + return; + } + let rng_x = resolution.0.clone(); + let rng_y = resolution.1.clone(); + tokio::spawn(async move { + log::info!( + "Create uinput mouce with rng_x: ({}, {}), rng_y: ({}, {})", + rng_x.0, + rng_x.1, + rng_y.0, + rng_y.1 + ); + let mut mouse = match mouce::Mouse::new_uinput(rng_x, rng_y) { + Ok(mouse) => mouse, + Err(e) => { + log::error!("Failed to create mouse, {}", e); + return; + } + }; + loop { + tokio::select! { + res = stream.next() => { + match res { + Err(err) => { + log::info!("UInput mouse ipc connection closed: {}", err); + break; + } + Ok(Some(data)) => { + match data { + Data::Mouse(data) => { + handle_mouse(&mut mouse, &data); + } + _ => { + } + } + } + _ => {} + } + } + } + } + }); + } + + fn spawn_controller_handler(mut stream: ipc::Connection) { + tokio::spawn(async move { + loop { + tokio::select! { + res = stream.next() => { + match res { + Err(_err) => { + // log::info!("UInput controller ipc connection closed: {}", err); + break; + } + Ok(Some(data)) => { + match data { + Data::Control(data) => match data { + ipc::DataControl::Resolution{ + minx, + maxx, + miny, + maxy, + } => { + *RESOLUTION.lock().unwrap() = ((minx, maxx), (miny, maxy)); + allow_err!(stream.send(&Data::Empty).await); + } + } + _ => { + } + } + } + _ => {} + } + } + } + } + }); + } + + /// Start uinput service. + async fn start_service(postfix: &str, handler: F) { + match new_listener(postfix).await { + Ok(mut incoming) => { + while let Some(result) = incoming.next().await { + match result { + Ok(stream) => { + log::debug!("Got new connection of uinput ipc {}", postfix); + handler(Connection::new(stream)); + } + Err(err) => { + log::error!("Couldn't get uinput mouse client: {:?}", err); + } + } + } + } + Err(err) => { + log::error!("Failed to start uinput mouse ipc service: {}", err); + } + } + } + + /// Start uinput keyboard service. + #[tokio::main(flavor = "current_thread")] + pub async fn start_service_keyboard() { + log::info!("start uinput keyboard service"); + start_service(IPC_POSTFIX_KEYBOARD, spawn_keyboard_handler).await; + } + + /// Start uinput mouse service. + #[tokio::main(flavor = "current_thread")] + pub async fn start_service_mouse() { + log::info!("start uinput mouse service"); + start_service(IPC_POSTFIX_MOUSE, spawn_mouse_handler).await; + } + + /// Start uinput mouse service. + #[tokio::main(flavor = "current_thread")] + pub async fn start_service_control() { + log::info!("start uinput control service"); + start_service(IPC_POSTFIX_CONTROL, spawn_controller_handler).await; + } + + pub fn stop_service_keyboard() { + log::info!("stop uinput keyboard service"); + } + pub fn stop_service_mouse() { + log::info!("stop uinput mouse service"); + } + pub fn stop_service_control() { + log::info!("stop uinput control service"); + } +} diff --git a/src/server/video_service.rs b/src/server/video_service.rs index c8b628015..ede62efee 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -31,6 +31,7 @@ use scrap::{ use std::{ collections::HashSet, io::{ErrorKind::WouldBlock, Result}, + ops::{Deref, DerefMut}, time::{self, Duration, Instant}, }; #[cfg(windows)] @@ -127,7 +128,7 @@ impl VideoFrameController { } } -trait TraitCapturer { +pub(super) trait TraitCapturer { fn frame<'a>(&'a mut self, timeout: Duration) -> Result>; fn set_use_yuv(&mut self, use_yuv: bool); @@ -340,22 +341,36 @@ fn check_uac_switch(privacy_mode_id: i32, captuerer_privacy_mode_id: i32) -> Res Ok(()) } -struct CapturerInfo { - origin: (i32, i32), - width: usize, - height: usize, - ndisplay: usize, - current: usize, - privacy_mode_id: i32, - _captuerer_privacy_mode_id: i32, - capturer: Box, +pub(super) struct CapturerInfo { + pub origin: (i32, i32), + pub width: usize, + pub height: usize, + pub ndisplay: usize, + pub current: usize, + pub privacy_mode_id: i32, + pub _captuerer_privacy_mode_id: i32, + pub capturer: Box, +} + +impl Deref for CapturerInfo { + type Target = Box; + + fn deref(&self) -> &Self::Target { + &self.capturer + } +} + +impl DerefMut for CapturerInfo { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.capturer + } } fn get_capturer(use_yuv: bool) -> ResultType { #[cfg(target_os = "linux")] { if scrap::is_wayland() { - return wayland_support::get_capturer(); + return super::wayland::get_capturer(); } } @@ -441,7 +456,7 @@ fn run(sp: GenericService) -> ResultType<()> { Ok(x) => encoder = x, Err(err) => bail!("Failed to create encoder: {}", err), } - c.capturer.set_use_yuv(encoder.use_yuv()); + c.set_use_yuv(encoder.use_yuv()); if *SWITCH.lock().unwrap() { log::debug!("Broadcasting display switch"); @@ -467,7 +482,7 @@ fn run(sp: GenericService) -> ResultType<()> { #[cfg(windows)] let mut try_gdi = 1; #[cfg(windows)] - log::info!("gdi: {}", c.capturer.is_gdi()); + log::info!("gdi: {}", c.is_gdi()); while sp.ok() { #[cfg(windows)] @@ -515,7 +530,7 @@ fn run(sp: GenericService) -> ResultType<()> { frame_controller.reset(); #[cfg(any(target_os = "android", target_os = "ios"))] - let res = match (*c.capturer).frame(spf) { + 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; @@ -538,7 +553,7 @@ fn run(sp: GenericService) -> ResultType<()> { }; #[cfg(not(any(target_os = "android", target_os = "ios")))] - let res = match (*c.capturer).frame(spf) { + 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; @@ -557,9 +572,9 @@ fn run(sp: GenericService) -> ResultType<()> { Err(ref e) if e.kind() == WouldBlock => { #[cfg(windows)] - if try_gdi > 0 && !c.capturer.is_gdi() { + if try_gdi > 0 && !c.is_gdi() { if try_gdi > 3 { - c.capturer.set_gdi(); + c.set_gdi(); try_gdi = 0; log::info!("No image, fall back to gdi"); } @@ -574,8 +589,8 @@ fn run(sp: GenericService) -> ResultType<()> { } #[cfg(windows)] - if !c.capturer.is_gdi() { - c.capturer.set_gdi(); + if !c.is_gdi() { + c.set_gdi(); log::info!("dxgi error, fall back to gdi: {:?}", err); continue; } @@ -688,7 +703,7 @@ fn get_display_num() -> usize { #[cfg(target_os = "linux")] { if scrap::is_wayland() { - return if let Ok(n) = wayland_support::get_display_num() { + return if let Ok(n) = super::wayland::get_display_num() { n } else { 0 @@ -703,7 +718,7 @@ fn get_display_num() -> usize { } } -fn get_displays_2(all: &Vec) -> (usize, Vec) { +pub(super) fn get_displays_2(all: &Vec) -> (usize, Vec) { let mut displays = Vec::new(); let mut primary = 0; for (i, d) in all.iter().enumerate() { @@ -731,7 +746,7 @@ pub async fn get_displays() -> ResultType<(usize, Vec)> { #[cfg(target_os = "linux")] { if scrap::is_wayland() { - return wayland_support::get_displays().await; + return super::wayland::get_displays().await; } } // switch to primary display if long time (30 seconds) no users @@ -760,7 +775,7 @@ fn get_primary() -> usize { #[cfg(target_os = "linux")] { if scrap::is_wayland() { - return match wayland_support::get_primary() { + return match super::wayland::get_primary() { Ok(n) => n, Err(_) => 0, }; @@ -816,7 +831,7 @@ fn try_get_displays() -> ResultType> { Ok(displays) } -fn get_current_display_2(mut all: Vec) -> ResultType<(usize, usize, Display)> { +pub(super) fn get_current_display_2(mut all: Vec) -> ResultType<(usize, usize, Display)> { let mut current = *CURRENT_DISPLAY.lock().unwrap() as usize; if all.len() == 0 { bail!("No displays"); @@ -838,185 +853,3 @@ fn get_current_display_2(mut all: Vec) -> ResultType<(usize, usize, Dis fn get_current_display() -> ResultType<(usize, usize, Display)> { get_current_display_2(try_get_displays()?) } - -#[cfg(target_os = "linux")] -pub mod wayland_support { - use super::*; - use hbb_common::allow_err; - - lazy_static::lazy_static! { - static ref CAP_DISPLAY_INFO: RwLock = RwLock::new(0); - } - struct CapDisplayInfo { - rects: Vec<((i32, i32), usize, usize)>, - displays: Vec, - num: usize, - primary: usize, - current: usize, - capturer: *mut Capturer, - } - - impl TraitCapturer for *mut Capturer { - fn frame<'a>(&'a mut self, timeout: Duration) -> Result> { - unsafe { (**self).frame(timeout) } - } - - fn set_use_yuv(&mut self, use_yuv: bool) { - unsafe { - (**self).set_use_yuv(use_yuv); - } - } - } - - async fn check_init() -> ResultType<()> { - if scrap::is_wayland() { - let mut minx = 0; - let mut maxx = 0; - let mut miny = 0; - let mut maxy = 0; - - if *CAP_DISPLAY_INFO.read().unwrap() == 0 { - let mut lock = CAP_DISPLAY_INFO.write().unwrap(); - if *lock == 0 { - let all = Display::all()?; - let num = all.len(); - let (primary, displays) = get_displays_2(&all); - - let mut rects: Vec<((i32, i32), usize, usize)> = Vec::new(); - for d in &all { - rects.push((d.origin(), d.width(), d.height())); - } - - let (ndisplay, current, display) = get_current_display_2(all)?; - let (origin, width, height) = - (display.origin(), display.width(), display.height()); - log::debug!( - "#displays={}, current={}, origin: {:?}, width={}, height={}, cpus={}/{}", - ndisplay, - current, - &origin, - width, - height, - num_cpus::get_physical(), - num_cpus::get(), - ); - - minx = origin.0; - maxx = origin.0 + width as i32; - miny = origin.1; - maxy = origin.1 + height as i32; - - let capturer = Box::into_raw(Box::new( - Capturer::new(display, true) - .with_context(|| "Failed to create capturer")?, - )); - let cap_display_info = Box::into_raw(Box::new(CapDisplayInfo { - rects, - displays, - num, - primary, - current, - capturer, - })); - *lock = cap_display_info as _; - } - } - - if minx != maxx && miny != maxy { - log::info!( - "send uinput resolution: ({}, {}), ({}, {})", - minx, - maxx, - miny, - maxy - ); - allow_err!(input_service::set_uinput_resolution(minx, maxx, miny, maxy).await); - allow_err!(input_service::set_uinput().await); - } - } - Ok(()) - } - - pub fn clear() { - if !scrap::is_wayland() { - return; - } - - let mut lock = CAP_DISPLAY_INFO.write().unwrap(); - if *lock != 0 { - unsafe { - let cap_display_info = Box::from_raw(*lock as *mut CapDisplayInfo); - let _ = Box::from_raw(cap_display_info.capturer); - } - *lock = 0; - } - } - - pub(super) async fn get_displays() -> ResultType<(usize, Vec)> { - check_init().await?; - let addr = *CAP_DISPLAY_INFO.read().unwrap(); - if addr != 0 { - let cap_display_info: *const CapDisplayInfo = addr as _; - unsafe { - let cap_display_info = &*cap_display_info; - let primary = cap_display_info.primary; - let displays = cap_display_info.displays.clone(); - Ok((primary, displays)) - } - } else { - bail!("Failed to get capturer display info"); - } - } - - pub(super) fn get_primary() -> ResultType { - let addr = *CAP_DISPLAY_INFO.read().unwrap(); - if addr != 0 { - let cap_display_info: *const CapDisplayInfo = addr as _; - unsafe { - let cap_display_info = &*cap_display_info; - Ok(cap_display_info.primary) - } - } else { - bail!("Failed to get capturer display info"); - } - } - - pub(super) fn get_display_num() -> ResultType { - let addr = *CAP_DISPLAY_INFO.read().unwrap(); - if addr != 0 { - let cap_display_info: *const CapDisplayInfo = addr as _; - unsafe { - let cap_display_info = &*cap_display_info; - Ok(cap_display_info.num) - } - } else { - bail!("Failed to get capturer display info"); - } - } - - pub(super) fn get_capturer() -> ResultType { - if !scrap::is_wayland() { - bail!("Do not call this function if not wayland"); - } - let addr = *CAP_DISPLAY_INFO.read().unwrap(); - if addr != 0 { - let cap_display_info: *const CapDisplayInfo = addr as _; - unsafe { - let cap_display_info = &*cap_display_info; - let rect = cap_display_info.rects[cap_display_info.current]; - Ok(CapturerInfo { - origin: rect.0, - width: rect.1, - height: rect.2, - ndisplay: cap_display_info.num, - current: cap_display_info.current, - privacy_mode_id: 0, - _captuerer_privacy_mode_id: 0, - capturer: Box::new(cap_display_info.capturer), - }) - } - } else { - bail!("Failed to get capturer display info"); - } - } -} diff --git a/src/server/wayland.rs b/src/server/wayland.rs new file mode 100644 index 000000000..b81d378a6 --- /dev/null +++ b/src/server/wayland.rs @@ -0,0 +1,179 @@ +use super::*; +use hbb_common::allow_err; +use scrap::{Capturer, Display, Frame}; +use std::{io::Result, time::Duration}; + +lazy_static::lazy_static! { + static ref CAP_DISPLAY_INFO: RwLock = RwLock::new(0); +} +struct CapDisplayInfo { + rects: Vec<((i32, i32), usize, usize)>, + displays: Vec, + num: usize, + primary: usize, + current: usize, + capturer: *mut Capturer, +} + +impl super::video_service::TraitCapturer for *mut Capturer { + fn frame<'a>(&'a mut self, timeout: Duration) -> Result> { + unsafe { (**self).frame(timeout) } + } + + fn set_use_yuv(&mut self, use_yuv: bool) { + unsafe { + (**self).set_use_yuv(use_yuv); + } + } +} + +async fn check_init() -> ResultType<()> { + if scrap::is_wayland() { + let mut minx = 0; + let mut maxx = 0; + let mut miny = 0; + let mut maxy = 0; + + if *CAP_DISPLAY_INFO.read().unwrap() == 0 { + let mut lock = CAP_DISPLAY_INFO.write().unwrap(); + if *lock == 0 { + let all = Display::all()?; + let num = all.len(); + let (primary, displays) = super::video_service::get_displays_2(&all); + + let mut rects: Vec<((i32, i32), usize, usize)> = Vec::new(); + for d in &all { + rects.push((d.origin(), d.width(), d.height())); + } + + let (ndisplay, current, display) = + super::video_service::get_current_display_2(all)?; + let (origin, width, height) = (display.origin(), display.width(), display.height()); + log::debug!( + "#displays={}, current={}, origin: {:?}, width={}, height={}, cpus={}/{}", + ndisplay, + current, + &origin, + width, + height, + num_cpus::get_physical(), + num_cpus::get(), + ); + + minx = origin.0; + maxx = origin.0 + width as i32; + miny = origin.1; + maxy = origin.1 + height as i32; + + let capturer = Box::into_raw(Box::new( + Capturer::new(display, true).with_context(|| "Failed to create capturer")?, + )); + let cap_display_info = Box::into_raw(Box::new(CapDisplayInfo { + rects, + displays, + num, + primary, + current, + capturer, + })); + *lock = cap_display_info as _; + } + } + + if minx != maxx && miny != maxy { + log::info!( + "send uinput resolution: ({}, {}), ({}, {})", + minx, + maxx, + miny, + maxy + ); + allow_err!(input_service::set_uinput_resolution(minx, maxx, miny, maxy).await); + allow_err!(input_service::set_uinput().await); + } + } + Ok(()) +} + +pub fn clear() { + if !scrap::is_wayland() { + return; + } + + let mut lock = CAP_DISPLAY_INFO.write().unwrap(); + if *lock != 0 { + unsafe { + let cap_display_info = Box::from_raw(*lock as *mut CapDisplayInfo); + let _ = Box::from_raw(cap_display_info.capturer); + } + *lock = 0; + } +} + +pub(super) async fn get_displays() -> ResultType<(usize, Vec)> { + check_init().await?; + let addr = *CAP_DISPLAY_INFO.read().unwrap(); + if addr != 0 { + let cap_display_info: *const CapDisplayInfo = addr as _; + unsafe { + let cap_display_info = &*cap_display_info; + let primary = cap_display_info.primary; + let displays = cap_display_info.displays.clone(); + Ok((primary, displays)) + } + } else { + bail!("Failed to get capturer display info"); + } +} + +pub(super) fn get_primary() -> ResultType { + let addr = *CAP_DISPLAY_INFO.read().unwrap(); + if addr != 0 { + let cap_display_info: *const CapDisplayInfo = addr as _; + unsafe { + let cap_display_info = &*cap_display_info; + Ok(cap_display_info.primary) + } + } else { + bail!("Failed to get capturer display info"); + } +} + +pub(super) fn get_display_num() -> ResultType { + let addr = *CAP_DISPLAY_INFO.read().unwrap(); + if addr != 0 { + let cap_display_info: *const CapDisplayInfo = addr as _; + unsafe { + let cap_display_info = &*cap_display_info; + Ok(cap_display_info.num) + } + } else { + bail!("Failed to get capturer display info"); + } +} + +pub(super) fn get_capturer() -> ResultType { + if !scrap::is_wayland() { + bail!("Do not call this function if not wayland"); + } + let addr = *CAP_DISPLAY_INFO.read().unwrap(); + if addr != 0 { + let cap_display_info: *const CapDisplayInfo = addr as _; + unsafe { + let cap_display_info = &*cap_display_info; + let rect = cap_display_info.rects[cap_display_info.current]; + Ok(super::video_service::CapturerInfo { + origin: rect.0, + width: rect.1, + height: rect.2, + ndisplay: cap_display_info.num, + current: cap_display_info.current, + privacy_mode_id: 0, + _captuerer_privacy_mode_id: 0, + capturer: Box::new(cap_display_info.capturer), + }) + } + } else { + bail!("Failed to get capturer display info"); + } +} From 00dc473703b38b70d2ecc3dbc0a47fab9519a6d8 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 20 Jul 2022 10:44:27 -0700 Subject: [PATCH 50/50] linux_wayland_support: dup detecting function of x11 or wayland Signed-off-by: fufesou --- Cargo.lock | 1 + libs/enigo/Cargo.toml | 1 + libs/enigo/src/lib.rs | 2 +- libs/enigo/src/linux/mod.rs | 32 -------- libs/enigo/src/linux/nix_impl.rs | 2 +- libs/hbb_common/src/lib.rs | 1 + libs/hbb_common/src/platform/linux.rs | 102 +++++++++++++++++++++++++ libs/hbb_common/src/platform/mod.rs | 2 + libs/scrap/src/common/linux.rs | 23 +++--- src/platform/linux.rs | 103 +------------------------- src/server/video_service.rs | 10 +-- src/server/wayland.rs | 6 +- 12 files changed, 130 insertions(+), 155 deletions(-) create mode 100644 libs/hbb_common/src/platform/linux.rs create mode 100644 libs/hbb_common/src/platform/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 70c76a9cc..c91272112 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1266,6 +1266,7 @@ name = "enigo" version = "0.0.14" dependencies = [ "core-graphics 0.22.3", + "hbb_common", "libc", "log", "objc", diff --git a/libs/enigo/Cargo.toml b/libs/enigo/Cargo.toml index 6842dab56..b0028b564 100644 --- a/libs/enigo/Cargo.toml +++ b/libs/enigo/Cargo.toml @@ -22,6 +22,7 @@ appveyor = { repository = "pythoneer/enigo-85xiy" } serde = { version = "1.0", optional = true } serde_derive = { version = "1.0", optional = true } log = "0.4" +hbb_common = { path = "../hbb_common" } [features] with_serde = ["serde", "serde_derive"] diff --git a/libs/enigo/src/lib.rs b/libs/enigo/src/lib.rs index 40ba1c1db..164fb1c17 100644 --- a/libs/enigo/src/lib.rs +++ b/libs/enigo/src/lib.rs @@ -74,7 +74,7 @@ pub use macos::Enigo; #[cfg(target_os = "linux")] mod linux; #[cfg(target_os = "linux")] -pub use crate::linux::{is_x11, Enigo}; +pub use crate::linux::Enigo; /// DSL parser module pub mod dsl; diff --git a/libs/enigo/src/linux/mod.rs b/libs/enigo/src/linux/mod.rs index 3c0784660..42e1dfebf 100644 --- a/libs/enigo/src/linux/mod.rs +++ b/libs/enigo/src/linux/mod.rs @@ -3,35 +3,3 @@ mod pynput; mod xdo; pub use self::nix_impl::Enigo; - -/// Check if display manager is x11. -pub fn is_x11() -> bool { - let stdout = - match std::process::Command::new("sh") - .arg("-c") - .arg("loginctl show-session $(loginctl | awk '/tty/ {print $1}') -p Type | awk -F= '{print $2}'") - .output() { - Ok(output) => { - output.stdout - }, - Err(_) => { - match std::process::Command::new("sh") - .arg("-c") - .arg("echo $XDG_SESSION_TYPE") - .output() { - Ok(output) => { - output.stdout - }, - Err(_) => { - return false; - } - } - } - }; - - if let Ok(display_manager) = std::str::from_utf8(&stdout) { - display_manager.trim() == "x11" - } else { - false - } -} diff --git a/libs/enigo/src/linux/nix_impl.rs b/libs/enigo/src/linux/nix_impl.rs index 332b6d16e..840290b2b 100644 --- a/libs/enigo/src/linux/nix_impl.rs +++ b/libs/enigo/src/linux/nix_impl.rs @@ -40,7 +40,7 @@ impl Enigo { impl Default for Enigo { fn default() -> Self { Self { - is_x11: crate::linux::is_x11(), + is_x11: "x11" == hbb_common::platform::linux::get_display_server(), uinput_keyboard: None, uinput_mouse: None, xdo: EnigoXdo::default(), diff --git a/libs/hbb_common/src/lib.rs b/libs/hbb_common/src/lib.rs index fdd32c4c7..a5443db0f 100644 --- a/libs/hbb_common/src/lib.rs +++ b/libs/hbb_common/src/lib.rs @@ -1,5 +1,6 @@ pub mod compress; pub mod protos; +pub mod platform; pub use protos::message as message_proto; pub use protos::rendezvous as rendezvous_proto; pub use bytes; diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs new file mode 100644 index 000000000..da79e9e39 --- /dev/null +++ b/libs/hbb_common/src/platform/linux.rs @@ -0,0 +1,102 @@ +use crate::ResultType; + +pub fn get_display_server() -> String { + let session = get_value_of_seat0(0); + get_display_server_of_session(&session) +} + +fn get_display_server_of_session(session: &str) -> String { + if let Ok(output) = std::process::Command::new("loginctl") + .args(vec!["show-session", "-p", "Type", session]) + .output() + // Check session type of the session + { + let display_server = String::from_utf8_lossy(&output.stdout) + .replace("Type=", "") + .trim_end() + .into(); + if display_server == "tty" { + // If the type is tty... + if let Ok(output) = std::process::Command::new("loginctl") + .args(vec!["show-session", "-p", "TTY", session]) + .output() + // Get the tty number + { + let tty: String = String::from_utf8_lossy(&output.stdout) + .replace("TTY=", "") + .trim_end() + .into(); + if let Ok(xorg_results) = run_cmds(format!("ps -e | grep \"{}.\\\\+Xorg\"", tty)) + // And check if Xorg is running on that tty + { + if xorg_results.trim_end().to_string() != "" { + // If it is, manually return "x11", otherwise return tty + "x11".to_owned() + } else { + display_server + } + } else { + // If any of these commands fail just fall back to the display server + display_server + } + } else { + display_server + } + } else { + // If the session is not a tty, then just return the type as usual + display_server + } + } else { + "".to_owned() + } +} + +pub fn get_value_of_seat0(i: usize) -> String { + if let Ok(output) = std::process::Command::new("loginctl").output() { + for line in String::from_utf8_lossy(&output.stdout).lines() { + if line.contains("seat0") { + if let Some(sid) = line.split_whitespace().nth(0) { + if is_active(sid) { + if let Some(uid) = line.split_whitespace().nth(i) { + return uid.to_owned(); + } + } + } + } + } + } + + // some case, there is no seat0 https://github.com/rustdesk/rustdesk/issues/73 + if let Ok(output) = std::process::Command::new("loginctl").output() { + for line in String::from_utf8_lossy(&output.stdout).lines() { + if let Some(sid) = line.split_whitespace().nth(0) { + let d = get_display_server_of_session(sid); + if is_active(sid) && d != "tty" { + if let Some(uid) = line.split_whitespace().nth(i) { + return uid.to_owned(); + } + } + } + } + } + + return "".to_owned(); +} + +fn is_active(sid: &str) -> bool { + if let Ok(output) = std::process::Command::new("loginctl") + .args(vec!["show-session", "-p", "State", sid]) + .output() + { + String::from_utf8_lossy(&output.stdout).contains("active") + } else { + false + } +} + +pub fn run_cmds(cmds: String) -> ResultType { + let output = std::process::Command::new("sh") + .args(vec!["-c", &cmds]) + .output()?; + Ok(String::from_utf8_lossy(&output.stdout).to_string()) +} diff --git a/libs/hbb_common/src/platform/mod.rs b/libs/hbb_common/src/platform/mod.rs new file mode 100644 index 000000000..8daba257f --- /dev/null +++ b/libs/hbb_common/src/platform/mod.rs @@ -0,0 +1,2 @@ +#[cfg(target_os = "linux")] +pub mod linux; diff --git a/libs/scrap/src/common/linux.rs b/libs/scrap/src/common/linux.rs index 2a921477e..8498ab7ff 100644 --- a/libs/scrap/src/common/linux.rs +++ b/libs/scrap/src/common/linux.rs @@ -52,31 +52,30 @@ pub enum Display { } #[inline] -pub fn is_wayland() -> bool { - std::env::var("IS_WAYLAND").is_ok() - || std::env::var("XDG_SESSION_TYPE") == Ok("wayland".to_owned()) +pub fn is_x11() -> bool { + "x11" == hbb_common::platform::linux::get_display_server() } impl Display { pub fn primary() -> io::Result { - Ok(if is_wayland() { - Display::WAYLAND(wayland::Display::primary()?) - } else { + Ok(if is_x11() { Display::X11(x11::Display::primary()?) + } else { + Display::WAYLAND(wayland::Display::primary()?) }) } pub fn all() -> io::Result> { - Ok(if is_wayland() { - wayland::Display::all()? - .drain(..) - .map(|x| Display::WAYLAND(x)) - .collect() - } else { + Ok(if is_x11() { x11::Display::all()? .drain(..) .map(|x| Display::X11(x)) .collect() + } else { + wayland::Display::all()? + .drain(..) + .map(|x| Display::WAYLAND(x)) + .collect() }) } diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 14d43e0e1..5a12f5ac0 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -1,4 +1,5 @@ use super::{CursorData, ResultType}; +pub use hbb_common::platform::linux::*; use hbb_common::{allow_err, bail, log}; use libc::{c_char, c_int, c_void}; use std::{ @@ -8,6 +9,7 @@ use std::{ Arc, }, }; + type Xdo = *const c_void; pub const PA_SAMPLE_RATE: u32 = 48000; @@ -335,17 +337,6 @@ pub fn get_active_userid() -> String { get_value_of_seat0(1) } -fn is_active(sid: &str) -> bool { - if let Ok(output) = std::process::Command::new("loginctl") - .args(vec!["show-session", "-p", "State", sid]) - .output() - { - String::from_utf8_lossy(&output.stdout).contains("active") - } else { - false - } -} - fn get_cm() -> bool { if let Ok(output) = std::process::Command::new("ps").args(vec!["aux"]).output() { for line in String::from_utf8_lossy(&output.stdout).lines() { @@ -401,89 +392,6 @@ fn get_display() -> String { last } -fn get_value_of_seat0(i: usize) -> String { - if let Ok(output) = std::process::Command::new("loginctl").output() { - for line in String::from_utf8_lossy(&output.stdout).lines() { - if line.contains("seat0") { - if let Some(sid) = line.split_whitespace().nth(0) { - if is_active(sid) { - if let Some(uid) = line.split_whitespace().nth(i) { - return uid.to_owned(); - } - } - } - } - } - } - - // some case, there is no seat0 https://github.com/rustdesk/rustdesk/issues/73 - if let Ok(output) = std::process::Command::new("loginctl").output() { - for line in String::from_utf8_lossy(&output.stdout).lines() { - if let Some(sid) = line.split_whitespace().nth(0) { - let d = get_display_server_of_session(sid); - if is_active(sid) && d != "tty" { - if let Some(uid) = line.split_whitespace().nth(i) { - return uid.to_owned(); - } - } - } - } - } - - return "".to_owned(); -} - -pub fn get_display_server() -> String { - let session = get_value_of_seat0(0); - get_display_server_of_session(&session) -} - -fn get_display_server_of_session(session: &str) -> String { - if let Ok(output) = std::process::Command::new("loginctl") - .args(vec!["show-session", "-p", "Type", session]) - .output() - // Check session type of the session - { - let display_server = String::from_utf8_lossy(&output.stdout) - .replace("Type=", "") - .trim_end() - .into(); - if display_server == "tty" { - // If the type is tty... - if let Ok(output) = std::process::Command::new("loginctl") - .args(vec!["show-session", "-p", "TTY", session]) - .output() - // Get the tty number - { - let tty: String = String::from_utf8_lossy(&output.stdout) - .replace("TTY=", "") - .trim_end() - .into(); - if let Ok(xorg_results) = run_cmds(format!("ps -e | grep \"{}.\\\\+Xorg\"", tty)) - // And check if Xorg is running on that tty - { - if xorg_results.trim_end().to_string() != "" { - // If it is, manually return "x11", otherwise return tty - "x11".to_owned() - } else { - display_server - } - } else { - // If any of these commands fail just fall back to the display server - display_server - } - } else { - display_server - } - } else { - // If the session is not a tty, then just return the type as usual - display_server - } - } else { - "".to_owned() - } -} - pub fn is_login_wayland() -> bool { if let Ok(contents) = std::fs::read_to_string("/etc/gdm3/custom.conf") { contents.contains("#WaylandEnable=false") @@ -690,13 +598,6 @@ pub fn is_installed() -> bool { true } -pub fn run_cmds(cmds: String) -> ResultType { - let output = std::process::Command::new("sh") - .args(vec!["-c", &cmds]) - .output()?; - Ok(String::from_utf8_lossy(&output.stdout).to_string()) -} - fn get_env_tries(name: &str, uid: &str, n: usize) -> String { for _ in 0..n { let x = get_env(name, uid); diff --git a/src/server/video_service.rs b/src/server/video_service.rs index ede62efee..e6ea713a1 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -193,7 +193,7 @@ fn check_display_changed( #[cfg(target_os = "linux")] { // wayland do not support changing display for now - if scrap::is_wayland() { + if !scrap::is_x11() { return false; } } @@ -369,7 +369,7 @@ impl DerefMut for CapturerInfo { fn get_capturer(use_yuv: bool) -> ResultType { #[cfg(target_os = "linux")] { - if scrap::is_wayland() { + if !scrap::is_x11() { return super::wayland::get_capturer(); } } @@ -702,7 +702,7 @@ pub fn handle_one_frame_encoded( fn get_display_num() -> usize { #[cfg(target_os = "linux")] { - if scrap::is_wayland() { + if !scrap::is_x11() { return if let Ok(n) = super::wayland::get_display_num() { n } else { @@ -745,7 +745,7 @@ pub(super) fn get_displays_2(all: &Vec) -> (usize, Vec) { pub async fn get_displays() -> ResultType<(usize, Vec)> { #[cfg(target_os = "linux")] { - if scrap::is_wayland() { + if !scrap::is_x11() { return super::wayland::get_displays().await; } } @@ -774,7 +774,7 @@ pub fn refresh() { fn get_primary() -> usize { #[cfg(target_os = "linux")] { - if scrap::is_wayland() { + if !scrap::is_x11() { return match super::wayland::get_primary() { Ok(n) => n, Err(_) => 0, diff --git a/src/server/wayland.rs b/src/server/wayland.rs index b81d378a6..e84be3f7c 100644 --- a/src/server/wayland.rs +++ b/src/server/wayland.rs @@ -28,7 +28,7 @@ impl super::video_service::TraitCapturer for *mut Capturer { } async fn check_init() -> ResultType<()> { - if scrap::is_wayland() { + if !scrap::is_x11() { let mut minx = 0; let mut maxx = 0; let mut miny = 0; @@ -96,7 +96,7 @@ async fn check_init() -> ResultType<()> { } pub fn clear() { - if !scrap::is_wayland() { + if scrap::is_x11() { return; } @@ -153,7 +153,7 @@ pub(super) fn get_display_num() -> ResultType { } pub(super) fn get_capturer() -> ResultType { - if !scrap::is_wayland() { + if scrap::is_x11() { bail!("Do not call this function if not wayland"); } let addr = *CAP_DISPLAY_INFO.read().unwrap();