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 4213392a..3532dd1e 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 7966920c..ce4a805d 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 ce4a805d..7966920c 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 b37c4beb..f0183371 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 00000000..aef200bc --- /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 aef200bc..3e8ff9ee 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 3e8ff9ee..f4453cbe 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 f4453cbe..b14a3f41 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 00000000..c699a61f --- /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 b14a3f41..ccf221f3 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 055a654d..636d2611 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 1dd5463a..ac4567a0 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 3f770d22..ab76ea47 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 21a4f952..f70f54ff 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 ce8601fa..3d8f019a 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 0f7ca1a9..033709a8 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 a2d7534e..27ccb75b 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 b1f8e367..1709c327 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 c699a61f..08859970 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 624336f4..632271cd 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 7eba7860..05491af3 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 60816a5d..a912d3cf 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 750cf91b..11eed8ab 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 c479d049..fba4822b 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 2d87504d..11ecc622 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 162ca764..d525f65c 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 76b36028..bb6a25cb 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 755d91ca..3b01b874 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 cd1a332c..cce3841e 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 2166073a..c0021299 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 08859970..eeeaaa37 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 ccf221f3..d6a65670 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 d6a65670..e7a95870 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 d63ce970..c6ccd2c2 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 667dd3e7..5ef127f4 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 24a156eb..7df06cd2 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 5b9651e5..942510b1 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 a7f90b97..e2397fa1 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 bc31223c..000cbae9 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 99dacb7e..5c1c6af2 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 4512ce94..4880b462 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 0a9dace0..07c6a686 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 e06c0e29..1d9deba6 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 d63ce970..33d966d2 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 ea12cb7e..26dc37ba 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 39b2eb76..2f35065c 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 667dd3e7..9917a47f 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 afc65f23..36d011b3 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 a7f90b97..4381fbc7 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 3b68fc7d..d437ce6d 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 46c73009..e8344c4a 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 8c5f3060..3ec9c0f7 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 f0134ace..a1f9093d 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 90d066d6..c1acb692 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 f85e37d0..fc83a09a 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 1759a1ac..04b2ccc9 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 7ce7d3ec..3cb3ae58 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 d6be5198..1b36c548 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 2c8b7fe9..5bbc8dc9 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 5ef127f4..b40c1a68 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 ba8e6d17..ba85d7f3 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 3c92ee71..e12bc607 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 16b98359..f8c67637 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 c2fd5600..8685e764 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 c9701d63..5952f51a 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 60cc0534..9e12ed43 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 f447c5ac..5e223b26 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 e7a95870..ab890766 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 b1c6aaa1..e926ee4c 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 c756d221..016363d4 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 946e47d4..e36672e5 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 d421bd85..d8f13822 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 8c60abaa..4f2a4a29 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 ad963b32..8feac4d3 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 d12b2881..5bab8d8c 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 4f981394..8bc1392b 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 efad1f9d..87ae1cf3 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 5bbc8dc9..7506d849 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 76068eee..905a2734 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 c383bc36..7a3e489b 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 87ae1cf3..b0e06bc0 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 884df3c5..3a5c3486 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 f270f7b3..55a9d0a2 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 000cbae9..355be368 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 3a5c3486..fb5c4cd1 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 55a9d0a2..ff17ac85 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 355be368..acb93c78 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 36d011b3..6d7a25b6 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 e8344c4a..dbf1545a 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 fb5c4cd1..e581e842 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 ff17ac85..14cae5f9 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 28f2dbc3..42b8c49f 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 716ff1ba..f2c8746d 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 3532dd1e..c2d5057f 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 e581e842..89438238 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 acb93c78..6fec6719 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 b4d8eaff..ee843377 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 9471ad4b..d8735736 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 a1f9093d..4bf201c0 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 f2c8746d..03768c6d 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 03768c6d..2fda2a5e 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 89438238..aa4c87dd 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 14cae5f9..c008f782 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 ee843377..8d2437c3 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 d8735736..f4f4fb15 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 4bf201c0..084422f6 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 2fda2a5e..5b822ed2 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 f4f4fb15..db087aa6 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 5b822ed2..28fa6235 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 aa4c87dd..adc15244 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 8d2437c3..d05831c0 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 00000000..05dfa327 --- /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 db087aa6..8da1937a 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 c2d5057f..3532dd1e 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 93cd6773..715a59b5 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 8da1937a..6dc7a11c 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 084422f6..2b7ffcc2 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 3cc105bf..fd340f7e 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 3cb3ae58..4bf244a0 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 1b36c548..a7c5dfea 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 7506d849..55a7c871 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 ba85d7f3..58a3a6cd 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 a4d11d41..1818a619 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 6a0b71a5..a0f0490e 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 ab76ea47..0df001e3 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 f70f54ff..b532aa4d 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 033709a8..0aac205a 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 27ccb75b..ca846007 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 1709c327..5d421b97 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 632271cd..6dc00f6f 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 05491af3..6d3aaf5e 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 fba4822b..d72d14c0 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 11ecc622..cce863b6 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 d525f65c..d21461fe 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 bb6a25cb..11986df5 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 55a7c871..30eb88b7 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 942510b1..d3adaaa7 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 a0f0490e..6a0b71a5 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 d05831c0..340a725f 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 e12bc607..c5c432d4 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 f8c67637..d13cd855 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 8685e764..b7f47199 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 5952f51a..4719a3be 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 9e12ed43..adf214b9 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 5e223b26..e9206999 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 ab890766..b0d1bfda 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 e926ee4c..4cdaf8b9 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 016363d4..3a0c2dc2 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 e36672e5..e3cadd20 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 d8f13822..7735e3db 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 4f2a4a29..ea8d22aa 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 8feac4d3..9ea7146e 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 5bab8d8c..1d30425d 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 8bc1392b..30c8f5a7 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 adc15244..62d1c9d8 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 c008f782..87fc0f16 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 bb11e275..63387fec 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 62d1c9d8..bbd8edfb 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 6fec6719..b8db8d50 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 340a725f..d8502151 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 07c6a686..fdd32c4c 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 00000000..ed6376ff --- /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 19af5986..d9eab5d9 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 7df06cd2..5f2f83b8 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 58a3a6cd..6e3d4d06 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 c5c432d4..d9ff1041 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 d13cd855..f9776e68 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 b7f47199..c9cd243f 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 4719a3be..a2183355 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 adf214b9..14ba0ab5 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 e9206999..f387b550 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 b0d1bfda..e35ab819 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 4cdaf8b9..6bf69e47 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 3a0c2dc2..778313d5 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 e3cadd20..17683350 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 7735e3db..d8a7702f 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 ea8d22aa..33233600 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 9ea7146e..7b39a287 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 1d30425d..02468201 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 30c8f5a7..ea94d159 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 2e2e4c8a..2f30f4b4 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 6dc7a11c..d4b76c99 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 dbf1545a..0481126c 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 2b7ffcc2..3fecc33c 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 39427fc8..c3f3706e 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 46adc328..aae950c2 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 3c4dc6a4..f4ec4c2f 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 099ca2af..4a600413 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 d8502151..787ffe5e 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 0481126c..9c7025ef 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 fc83a09a..4d941e1f 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 0538f1ae..2044388f 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 2350d243..28101896 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 9c7025ef..04b070cf 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 1818a619..a4923eb1 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 d8a7702f..55d33c7b 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 5b26711c..53bd9cf9 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 bbd8edfb..743f0258 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 87fc0f16..2b7f4391 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 5899bd4d..1c7697ac 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 8b73e996..7fbedca4 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 865b689a..3bb45319 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 2b7cd3f2..efa6f783 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 10cde9cb..40ba1c1d 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 00000000..3c078466 --- /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 00000000..332b6d16 --- /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 00000000..748b3010 --- /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 adfe9507..0e3b79ab 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 1a8c3988..855ac7ac 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 50bab092..06a4ed9e 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 792ea14e..9115bfd3 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 ff6bf802..05bb0874 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 25581990..c1a25c8d 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 9adf26cd..78f14194 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 46692535..6b60b256 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 890b9db6..ed424c35 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 c51e9a52..00000000 --- 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 af4a3c41..e703b056 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 00000000..0756ad35 --- /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 5f2f83b8..a5615ce6 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 2f30f4b4..c8f76cbd 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 efd6476b..b08793d1 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 d437ce6d..f1171e61 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 04b070cf..869df419 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 3ec9c0f7..5d0ebe45 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 e64ddd80..c8b62801 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 06a4ed9e..2a921477 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 00000000..c51e9a52 --- /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 0756ad35..f6c7454c 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 743f0258..160765a0 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 cd828218..aa1f5595 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 160765a0..70c76a9c 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 b08793d1..14d43e0e 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 f1171e61..a71efaee 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 5d0ebe45..29f4207b 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 00000000..7a6d47cf --- /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 c8b62801..ede62efe 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 00000000..b81d378a --- /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 70c76a9c..c9127211 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 6842dab5..b0028b56 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 40ba1c1d..164fb1c1 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 3c078466..42e1dfeb 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 332b6d16..840290b2 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 fdd32c4c..a5443db0 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 00000000..da79e9e3 --- /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 00000000..8daba257 --- /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 2a921477..8498ab7f 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 14d43e0e..5a12f5ac 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 ede62efe..e6ea713a 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 b81d378a..e84be3f7 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();