From 8e1545b43231ecff341aeac86ca553ffdd003f3e Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 10 Nov 2022 10:27:13 +0800 Subject: [PATCH 01/11] portable service Signed-off-by: 21pages --- Cargo.lock | 75 +- Cargo.toml | 2 + flutter/lib/desktop/pages/server_page.dart | 88 ++- flutter/lib/models/model.dart | 3 + flutter/lib/models/server_model.dart | 10 + libs/scrap/src/common/dxgi.rs | 2 +- libs/scrap/src/common/vpxcodec.rs | 6 +- src/core_main.rs | 12 + src/flutter.rs | 6 +- src/flutter_ffi.rs | 8 + src/ipc.rs | 14 + src/lang/cn.rs | 1 + 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/ja.rs | 1 + src/lang/ko.rs | 1 + src/lang/kz.rs | 1 + src/lang/pl.rs | 1 + src/lang/pt_PT.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 + src/lang/ua.rs | 1 + src/lang/vn.rs | 1 + src/platform/windows.rs | 8 +- src/server.rs | 5 + src/server/connection.rs | 57 +- src/server/input_service.rs | 43 +- src/server/portable_service.rs | 779 +++++++++++++++++++++ src/server/video_qos.rs | 6 +- src/server/video_service.rs | 42 +- src/ui/cm.css | 2 +- src/ui/cm.rs | 14 + src/ui/cm.tis | 45 +- src/ui_cm_interface.rs | 34 + src/ui_interface.rs | 5 +- 46 files changed, 1217 insertions(+), 72 deletions(-) create mode 100644 src/server/portable_service.rs diff --git a/Cargo.lock b/Cargo.lock index 00916eab..67a471ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1253,7 +1253,7 @@ dependencies = [ "libc", "memalloc", "system-configuration", - "windows", + "windows 0.30.0", ] [[package]] @@ -4413,6 +4413,8 @@ dependencies = [ "serde_derive", "serde_json 1.0.85", "sha2", + "shared_memory", + "shutdown_hooks", "simple_rc", "sys-locale", "sysinfo", @@ -4761,12 +4763,31 @@ dependencies = [ "digest", ] +[[package]] +name = "shared_memory" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba8593196da75d9dc4f69349682bd4c2099f8cde114257d1ef7ef1b33d1aba54" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "nix 0.23.1", + "rand 0.8.5", + "win-sys", +] + [[package]] name = "shlex" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +[[package]] +name = "shutdown_hooks" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6057adedbec913419c92996f395ba69931acbd50b7d56955394cd3f7bedbfa45" + [[package]] name = "signal-hook" version = "0.3.14" @@ -5844,6 +5865,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" +[[package]] +name = "win-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b7b128a98c1cfa201b09eb49ba285887deb3cbe7466a98850eb1adabb452be5" +dependencies = [ + "windows 0.34.0", +] + [[package]] name = "winapi" version = "0.2.8" @@ -5909,6 +5939,19 @@ dependencies = [ "windows_x86_64_msvc 0.30.0", ] +[[package]] +name = "windows" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45296b64204227616fdbf2614cefa4c236b98ee64dfaaaa435207ed99fe7829f" +dependencies = [ + "windows_aarch64_msvc 0.34.0", + "windows_i686_gnu 0.34.0", + "windows_i686_msvc 0.34.0", + "windows_x86_64_gnu 0.34.0", + "windows_x86_64_msvc 0.34.0", +] + [[package]] name = "windows-service" version = "0.4.0" @@ -5959,6 +6002,12 @@ version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29277a4435d642f775f63c7d1faeb927adba532886ce0287bd985bffb16b6bca" +[[package]] +name = "windows_aarch64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" + [[package]] name = "windows_aarch64_msvc" version = "0.36.1" @@ -5977,6 +6026,12 @@ version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1145e1989da93956c68d1864f32fb97c8f561a8f89a5125f6a2b7ea75524e4b8" +[[package]] +name = "windows_i686_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" + [[package]] name = "windows_i686_gnu" version = "0.36.1" @@ -5995,6 +6050,12 @@ version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4a09e3a0d4753b73019db171c1339cd4362c8c44baf1bcea336235e955954a6" +[[package]] +name = "windows_i686_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" + [[package]] name = "windows_i686_msvc" version = "0.36.1" @@ -6013,6 +6074,12 @@ version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ca64fcb0220d58db4c119e050e7af03c69e6f4f415ef69ec1773d9aab422d5a" +[[package]] +name = "windows_x86_64_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" + [[package]] name = "windows_x86_64_gnu" version = "0.36.1" @@ -6031,6 +6098,12 @@ version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08cabc9f0066848fef4bc6a1c1668e6efce38b661d2aeec75d18d8617eebb5f1" +[[package]] +name = "windows_x86_64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" + [[package]] name = "windows_x86_64_msvc" version = "0.36.1" diff --git a/Cargo.toml b/Cargo.toml index 5dc54d58..689e1a98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,6 +92,8 @@ winreg = "0.10" windows-service = "0.4" virtual_display = { path = "libs/virtual_display" } impersonate_system = { git = "https://github.com/21pages/impersonate-system" } +shared_memory = "0.12.4" +shutdown_hooks = "0.1.0" [target.'cfg(target_os = "macos")'.dependencies] objc = "0.2" diff --git a/flutter/lib/desktop/pages/server_page.dart b/flutter/lib/desktop/pages/server_page.dart index e344575c..aac6ee01 100644 --- a/flutter/lib/desktop/pages/server_page.dart +++ b/flutter/lib/desktop/pages/server_page.dart @@ -501,11 +501,46 @@ class _CmControlPanel extends StatelessWidget { } buildAuthorized(BuildContext context) { + final bool canElevate = bind.cmCanElevate(); + final model = Provider.of(context); + final offstage = !(canElevate && model.showElevation); + final width = offstage ? 200.0 : 100.0; return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ + Offstage( + offstage: offstage, + child: Ink( + width: width, + height: 40, + decoration: BoxDecoration( + color: Colors.green[700], + borderRadius: BorderRadius.circular(10)), + child: InkWell( + onTap: () => + checkClickTime(client.id, () => handleElevate(context)), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.security_sharp, + color: Colors.white, + ), + Text( + translate("Elevate"), + style: TextStyle(color: Colors.white), + ), + ], + )), + ), + ), + Offstage( + offstage: offstage, + child: SizedBox( + width: 30, + )), Ink( - width: 200, + width: width, height: 40, decoration: BoxDecoration( color: Colors.redAccent, borderRadius: BorderRadius.circular(10)), @@ -552,11 +587,50 @@ class _CmControlPanel extends StatelessWidget { } buildUnAuthorized(BuildContext context) { + final bool canElevate = bind.cmCanElevate(); + final model = Provider.of(context); + final offstage = !(canElevate && model.showElevation); + final width = offstage ? 100.0 : 85.0; + final spacerWidth = offstage ? 30.0 : 5.0; return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ + Offstage( + offstage: offstage, + child: Ink( + height: 40, + width: width, + decoration: BoxDecoration( + color: Colors.green[700], + borderRadius: BorderRadius.circular(10)), + child: InkWell( + onTap: () => checkClickTime(client.id, () { + handleAccept(context); + handleElevate(context); + windowManager.minimize(); + }), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.security_sharp, + color: Colors.white, + ), + Text( + translate("Accept"), + style: TextStyle(color: Colors.white), + ), + ], + )), + ), + ), + Offstage( + offstage: offstage, + child: SizedBox( + width: spacerWidth, + )), Ink( - width: 100, + width: width, height: 40, decoration: BoxDecoration( color: MyTheme.accent, borderRadius: BorderRadius.circular(10)), @@ -576,10 +650,10 @@ class _CmControlPanel extends StatelessWidget { )), ), SizedBox( - width: 30, + width: spacerWidth, ), Ink( - width: 100, + width: width, height: 40, decoration: BoxDecoration( color: Colors.transparent, @@ -611,6 +685,12 @@ class _CmControlPanel extends StatelessWidget { model.sendLoginResponse(client, true); } + void handleElevate(BuildContext context) { + final model = Provider.of(context, listen: false); + model.setShowElevation(false); + bind.cmElevatePortable(connId: client.id); + } + void handleClose(BuildContext context) async { await bind.cmRemoveDisconnectedConnection(connId: client.id); if (await bind.cmGetClientsLength() == 0) { diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index d7fc414d..fb4f8b4f 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -192,6 +192,9 @@ class FfiModel with ChangeNotifier { } } else if (name == 'alias') { handleAliasChanged(evt); + } else if (name == 'show_elevation') { + final show = evt['show'].toString() == 'true'; + parent.target?.serverModel.setShowElevation(show); } }; } diff --git a/flutter/lib/models/server_model.dart b/flutter/lib/models/server_model.dart index d407ca51..1c0d1cbd 100644 --- a/flutter/lib/models/server_model.dart +++ b/flutter/lib/models/server_model.dart @@ -27,6 +27,7 @@ class ServerModel with ChangeNotifier { bool _inputOk = false; bool _audioOk = false; bool _fileOk = false; + bool _showElevation = true; int _connectStatus = 0; // Rendezvous Server status String _verificationMethod = ""; String _temporaryPasswordLength = ""; @@ -51,6 +52,8 @@ class ServerModel with ChangeNotifier { bool get fileOk => _fileOk; + bool get showElevation => _showElevation; + int get connectStatus => _connectStatus; String get verificationMethod { @@ -530,6 +533,13 @@ class ServerModel with ChangeNotifier { final index = _clients.indexWhere((client) => client.id == id); tabController.jumpTo(index); } + + void setShowElevation(bool show) { + if (_showElevation != show) { + _showElevation = show; + notifyListeners(); + } + } } class Client { diff --git a/libs/scrap/src/common/dxgi.rs b/libs/scrap/src/common/dxgi.rs index 963f39de..287d8588 100644 --- a/libs/scrap/src/common/dxgi.rs +++ b/libs/scrap/src/common/dxgi.rs @@ -61,7 +61,7 @@ impl TraitCapturer for Capturer { } } -pub struct Frame<'a>(&'a [u8]); +pub struct Frame<'a>(pub &'a [u8]); impl<'a> ops::Deref for Frame<'a> { type Target = [u8]; diff --git a/libs/scrap/src/common/vpxcodec.rs b/libs/scrap/src/common/vpxcodec.rs index 47b3df3a..5164886a 100644 --- a/libs/scrap/src/common/vpxcodec.rs +++ b/libs/scrap/src/common/vpxcodec.rs @@ -4,7 +4,7 @@ use hbb_common::anyhow::{anyhow, Context}; use hbb_common::message_proto::{EncodedVideoFrame, EncodedVideoFrames, Message, VideoFrame}; -use hbb_common::{ResultType, get_time}; +use hbb_common::{get_time, ResultType}; use crate::codec::EncoderApi; use crate::STRIDE_ALIGN; @@ -233,7 +233,9 @@ impl EncoderApi for VpxEncoder { impl VpxEncoder { pub fn encode(&mut self, pts: i64, data: &[u8], stride_align: usize) -> Result { - assert!(2 * data.len() >= 3 * self.width * self.height); + if 2 * data.len() < 3 * self.width * self.height { + return Err(Error::FailedCall("len not enough".to_string())); + } let mut image = Default::default(); call_vpx_ptr!(vpx_img_wrap( diff --git a/src/core_main.rs b/src/core_main.rs index 443ef92e..889015c0 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -80,6 +80,11 @@ pub fn core_main() -> Option> { .ok(); } } + #[cfg(windows)] + if !crate::platform::is_installed() && (_is_elevate || _is_run_as_system) { + crate::platform::elevate_or_run_as_system(click_setup, _is_elevate, _is_run_as_system); + return None; + } if args.is_empty() { std::thread::spawn(move || crate::start_server(false)); } else { @@ -128,6 +133,13 @@ pub fn core_main() -> Option> { } else if args[0] == "--tray" { crate::tray::start_tray(); return None; + } else if args[0] == "--portable-service" { + crate::platform::elevate_or_run_as_system( + click_setup, + _is_elevate, + _is_run_as_system, + ); + return None; } } if args[0] == "--remove" { diff --git a/src/flutter.rs b/src/flutter.rs index 91b2ce7e..a69473e5 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -406,7 +406,7 @@ pub fn session_start_(id: &str, event_stream: StreamSink) -> ResultTy *session.event_stream.write().unwrap() = Some(event_stream); let session = session.clone(); std::thread::spawn(move || { - // if flutter : disable keyboard listen + // if flutter : disable keyboard listen crate::client::disable_keyboard_listening(); io_loop(session); }); @@ -469,6 +469,10 @@ pub mod connection_manager { fn change_language(&self) { self.push_event("language", vec![]); } + + fn show_elevation(&self, show: bool) { + self.push_event("show_elevation", vec![("show", &show.to_string())]); + } } impl FlutterHandler { diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 3a2e3f58..856c4ed2 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1010,6 +1010,14 @@ pub fn cm_switch_permission(conn_id: i32, name: String, enabled: bool) { crate::ui_cm_interface::switch_permission(conn_id, name, enabled) } +pub fn cm_can_elevate() -> SyncReturn { + SyncReturn(crate::ui_cm_interface::can_elevate()) +} + +pub fn cm_elevate_portable(conn_id: i32) { + crate::ui_cm_interface::elevate_portable(conn_id); +} + pub fn main_get_icon() -> String { #[cfg(not(any(target_os = "android", target_os = "ios", feature = "cli")))] return ui_interface::get_icon(); diff --git a/src/ipc.rs b/src/ipc.rs index 229bcf16..d2d57f8c 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -130,6 +130,19 @@ pub enum DataControl { }, } +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(tag = "t", content = "c")] +pub enum DataPortableService { + Ping, + Pong, + ConnCount(Option), + Mouse(Vec), + Key(Vec), + RequestStart, + WillClose, + CmShowElevation(bool), +} + #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(tag = "t", content = "c")] pub enum Data { @@ -187,6 +200,7 @@ pub enum Data { Language(String), Empty, Disconnected, + DataPortableService(DataPortableService), } #[tokio::main(flavor = "current_thread")] diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 7240f2a9..b622123a 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("This PC", "此电脑"), ("or", "或"), ("Continue with", "使用"), + ("Elevate", "提权"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index b51cb69e..5c086bfb 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("This PC", ""), ("or", ""), ("Continue with", ""), + ("Elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index c4d633b9..90670804 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("This PC", ""), ("or", ""), ("Continue with", ""), + ("Elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index 9eb90ebc..6ebea6b2 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("This PC", ""), ("or", ""), ("Continue with", ""), + ("Elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index e7a35d93..2dce72f6 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("This PC", ""), ("or", ""), ("Continue with", ""), + ("Elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 2eef088c..2e70a519 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("This PC", "Este PC"), ("or", "o"), ("Continue with", "Continuar con"), + ("Elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index fc738c2b..811ec911 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("This PC", "Ce PC"), ("or", "ou"), ("Continue with", "Continuer avec"), + ("Elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 6a75b695..863fa373 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("This PC", "Ez a számítógép"), ("or", "vagy"), ("Continue with", "Folytatás a következővel"), + ("Elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 6f328f12..78fccc9f 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("This PC", ""), ("or", ""), ("Continue with", ""), + ("Elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 75e7859e..35c09a0b 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("This PC", ""), ("or", ""), ("Continue with", ""), + ("Elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 0e693137..21344eb1 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("This PC", ""), ("or", ""), ("Continue with", ""), + ("Elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 601db354..3b726902 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("This PC", ""), ("or", ""), ("Continue with", ""), + ("Elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 359e14f5..1dc50580 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("This PC", ""), ("or", ""), ("Continue with", ""), + ("Elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 382b254f..7ed98913 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("This PC", ""), ("or", ""), ("Continue with", ""), + ("Elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 49679bfd..8b4c980f 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("This PC", ""), ("or", ""), ("Continue with", ""), + ("Elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 3a60dc31..0d5594ea 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("This PC", "Este PC"), ("or", "ou"), ("Continue with", "Continuar com"), + ("Elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 7d7b0c6a..22c246f8 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("This PC", "Этот компьютер"), ("or", "или"), ("Continue with", "Продолжить с"), + ("Elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 4dfd8b02..618ede5c 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("This PC", ""), ("or", ""), ("Continue with", ""), + ("Elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 7c1f18df..4ff8f9b8 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("This PC", ""), ("or", ""), ("Continue with", ""), + ("Elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index f856182f..6fb89a09 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("This PC", ""), ("or", ""), ("Continue with", ""), + ("Elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 6a196feb..1150199c 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("This PC", ""), ("or", ""), ("Continue with", ""), + ("Elevate", "提權"), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 95d19b26..cc0ef653 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("This PC", ""), ("or", ""), ("Continue with", ""), + ("Elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index c95498ca..9773d765 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -389,5 +389,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("This PC", ""), ("or", ""), ("Continue with", ""), + ("Elevate", ""), ].iter().cloned().collect(); } diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 97dfbcc2..b418d090 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -63,7 +63,7 @@ pub fn get_cursor() -> ResultType> { unsafe { let mut ci: CURSORINFO = mem::MaybeUninit::uninit().assume_init(); ci.cbSize = std::mem::size_of::() as _; - if GetCursorInfo(&mut ci) == FALSE { + if crate::portable_service::client::get_cursor_info(&mut ci) == FALSE { return Err(io::Error::last_os_error().into()); } if ci.flags & CURSOR_SHOWING == 0 { @@ -1542,9 +1542,11 @@ pub fn elevate_or_run_as_system(is_setup: bool, is_elevate: bool, is_run_as_syst } else { "--run-as-system" }; - if is_root() { - log::debug!("portable run as system user"); + if is_run_as_system { + log::info!("run portable service"); + crate::portable_service::server::run_portable_service(); + } } else { match is_elevated(None) { Ok(elevated) => { diff --git a/src/server.rs b/src/server.rs index 6e549c9f..7e00532f 100644 --- a/src/server.rs +++ b/src/server.rs @@ -49,6 +49,8 @@ pub const NAME_POS: &'static str = ""; } mod connection; +#[cfg(windows)] +pub mod portable_service; mod service; mod video_qos; pub mod video_service; @@ -60,6 +62,7 @@ type ConnMap = HashMap; lazy_static::lazy_static! { pub static ref CHILD_PROCESS: Childs = Default::default(); + pub static ref CONN_COUNT: Arc> = Default::default(); } pub struct Server { @@ -259,6 +262,7 @@ impl Server { } } self.connections.insert(conn.id(), conn); + *CONN_COUNT.lock().unwrap() = self.connections.len(); } pub fn remove_connection(&mut self, conn: &ConnInner) { @@ -266,6 +270,7 @@ impl Server { s.on_unsubscribe(conn.id()); } self.connections.remove(&conn.id()); + *CONN_COUNT.lock().unwrap() = self.connections.len(); } pub fn close_connections(&mut self) { diff --git a/src/server/connection.rs b/src/server/connection.rs index 891c4888..c77f6ebd 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -236,8 +236,12 @@ impl Connection { #[cfg(not(any(target_os = "android", target_os = "ios")))] std::thread::spawn(move || Self::handle_input(rx_input, tx_cloned)); let mut second_timer = time::interval(Duration::from_secs(1)); + #[cfg(windows)] let mut last_uac = false; + #[cfg(windows)] let mut last_foreground_window_elevated = false; + #[cfg(windows)] + let is_installed = crate::platform::is_installed(); loop { tokio::select! { @@ -341,6 +345,12 @@ impl Connection { }; conn.send(msg_out).await; } + #[cfg(windows)] + ipc::Data::DataPortableService(ipc::DataPortableService::RequestStart) => { + if let Err(e) = crate::portable_service::client::start_portable_service() { + log::error!("Failed to start portable service from cm:{:?}", e); + } + } _ => {} } }, @@ -417,23 +427,36 @@ impl Connection { } }, _ = second_timer.tick() => { - let uac = crate::video_service::IS_UAC_RUNNING.lock().unwrap().clone(); - if last_uac != uac { - last_uac = uac; - let mut misc = Misc::new(); - misc.set_uac(uac); - let mut msg = Message::new(); - msg.set_misc(misc); - conn.inner.send(msg.into()); - } - let foreground_window_elevated = crate::video_service::IS_FOREGROUND_WINDOW_ELEVATED.lock().unwrap().clone(); - if last_foreground_window_elevated != foreground_window_elevated { - last_foreground_window_elevated = foreground_window_elevated; - let mut misc = Misc::new(); - misc.set_foreground_window_elevated(foreground_window_elevated); - let mut msg = Message::new(); - msg.set_misc(misc); - conn.inner.send(msg.into()); + #[cfg(windows)] + { + use crate::portable_service::client::{PORTABLE_SERVICE_STATUS, PortableServiceStatus::*}; + let uac = crate::video_service::IS_UAC_RUNNING.lock().unwrap().clone(); + if last_uac != uac { + last_uac = uac; + if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == NotStarted { + let mut misc = Misc::new(); + misc.set_uac(uac); + let mut msg = Message::new(); + msg.set_misc(misc); + conn.inner.send(msg.into()); + } + } + let foreground_window_elevated = crate::video_service::IS_FOREGROUND_WINDOW_ELEVATED.lock().unwrap().clone(); + if last_foreground_window_elevated != foreground_window_elevated { + last_foreground_window_elevated = foreground_window_elevated; + if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == NotStarted { + let mut misc = Misc::new(); + misc.set_foreground_window_elevated(foreground_window_elevated); + let mut msg = Message::new(); + msg.set_misc(misc); + conn.inner.send(msg.into()); + } + } + if !is_installed { + let show_elevation = PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == NotStarted; + conn.send_to_cm(ipc::Data::DataPortableService(ipc::DataPortableService::CmShowElevation(show_elevation))); + + } } } _ = test_delay_timer.tick() => { diff --git a/src/server/input_service.rs b/src/server/input_service.rs index f2591df0..af4b7d85 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -287,7 +287,23 @@ pub fn handle_mouse(evt: &MouseEvent, conn: i32) { QUEUE.exec_async(move || handle_mouse_(&evt, conn)); return; } - handle_mouse_(evt, conn); + if !active_mouse_(conn) { + return; + } + let evt_type = evt.mask & 0x7; + if evt_type == 0 { + let time = get_time(); + *LATEST_PEER_INPUT_CURSOR.lock().unwrap() = Input { + time, + conn, + x: evt.x, + y: evt.y, + }; + } + #[cfg(windows)] + crate::portable_service::client::handle_mouse(evt); + #[cfg(not(windows))] + handle_mouse_(evt); } pub fn fix_key_down_timeout_loop() { @@ -415,8 +431,7 @@ fn active_mouse_(conn: i32) -> bool { let lock = LATEST_PEER_INPUT_CURSOR.lock().unwrap(); (lock.x, lock.y) }; - let mut can_active = - in_actived_dist(last_in_x, x) && in_actived_dist(last_in_y, y); + let mut can_active = in_actived_dist(last_in_x, x) && in_actived_dist(last_in_y, y); // The cursor may not have been moved to last input position if system is busy now. // While this is not a common case, we check it again after some time later. if !can_active { @@ -425,8 +440,7 @@ fn active_mouse_(conn: i32) -> bool { std::thread::sleep(std::time::Duration::from_micros(10)); // Sleep here can also somehow suppress delay accumulation. if let Some((x2, y2)) = crate::get_cursor_pos() { - can_active = - in_actived_dist(last_in_x, x2) && in_actived_dist(last_in_y, y2); + can_active = in_actived_dist(last_in_x, x2) && in_actived_dist(last_in_y, y2); } } if !can_active { @@ -440,15 +454,11 @@ fn active_mouse_(conn: i32) -> bool { } } -fn handle_mouse_(evt: &MouseEvent, conn: i32) { +pub fn handle_mouse_(evt: &MouseEvent) { if EXITING.load(Ordering::SeqCst) { return; } - if !active_mouse_(conn) { - return; - } - #[cfg(windows)] crate::platform::windows::try_change_desktop(); let buttons = evt.mask >> 3; @@ -477,14 +487,6 @@ fn handle_mouse_(evt: &MouseEvent, conn: i32) { } match evt_type { 0 => { - let time = get_time(); - *LATEST_PEER_INPUT_CURSOR.lock().unwrap() = Input { - time, - conn, - x: evt.x, - y: evt.y, - }; - en.mouse_move_to(evt.x, evt.y); } 1 => match buttons { @@ -698,6 +700,9 @@ pub fn handle_key(evt: &KeyEvent) { QUEUE.exec_async(move || handle_key_(&evt)); return; } + #[cfg(windows)] + crate::portable_service::client::handle_key(evt); + #[cfg(not(windows))] handle_key_(evt); } @@ -949,7 +954,7 @@ fn legacy_keyboard_mode(evt: &KeyEvent) { } } -fn handle_key_(evt: &KeyEvent) { +pub fn handle_key_(evt: &KeyEvent) { if EXITING.load(Ordering::SeqCst) { return; } diff --git a/src/server/portable_service.rs b/src/server/portable_service.rs new file mode 100644 index 00000000..1de2a1c8 --- /dev/null +++ b/src/server/portable_service.rs @@ -0,0 +1,779 @@ +use core::slice; +use hbb_common::{ + allow_err, + anyhow::anyhow, + bail, + config::Config, + log, + message_proto::{KeyEvent, MouseEvent}, + protobuf::Message, + sleep, + tokio::{self, sync::mpsc}, + ResultType, +}; +use scrap::{Capturer, Frame, TraitCapturer}; +use shared_memory::*; +use std::{ + mem::size_of, + ops::{Deref, DerefMut}, + sync::{Arc, Mutex}, + time::Duration, +}; +use winapi::{ + shared::minwindef::{BOOL, FALSE, TRUE}, + um::winuser::{self, CURSORINFO, PCURSORINFO}, +}; + +use crate::{ + ipc::{self, new_listener, Connection, Data, DataPortableService}, + video_service::get_current_display, +}; + +use super::video_qos; + +const SIZE_COUNTER: usize = size_of::() * 2; +const FRAME_ALIGN: usize = 64; + +const ADDR_CURSOR_PARA: usize = 0; +const ADDR_CURSOR_COUNTER: usize = ADDR_CURSOR_PARA + size_of::(); + +const ADDR_CAPTURER_PARA: usize = ADDR_CURSOR_COUNTER + SIZE_COUNTER; +const ADDR_CAPTURE_FRAME_SIZE: usize = ADDR_CAPTURER_PARA + size_of::(); +const ADDR_CAPTURE_WOULDBLOCK: usize = ADDR_CAPTURE_FRAME_SIZE + size_of::(); +const ADDR_CAPTURE_FRAME_COUNTER: usize = ADDR_CAPTURE_WOULDBLOCK + size_of::(); + +const ADDR_CAPTURE_FRAME: usize = + (ADDR_CAPTURE_FRAME_COUNTER + SIZE_COUNTER + FRAME_ALIGN - 1) / FRAME_ALIGN * FRAME_ALIGN; + +const IPC_PROFIX: &str = "_portable_service"; +pub const SHMEM_NAME: &str = "_portable_service"; +const MAX_NACK: usize = 3; +const IPC_CONN_TIMEOUT: Duration = Duration::from_secs(3); + +pub enum PortableServiceStatus { + NonStart, + Running, +} + +impl Default for PortableServiceStatus { + fn default() -> Self { + Self::NonStart + } +} + +pub struct SharedMemory { + inner: Shmem, +} + +unsafe impl Send for SharedMemory {} +unsafe impl Sync for SharedMemory {} + +impl Deref for SharedMemory { + type Target = Shmem; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for SharedMemory { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +impl SharedMemory { + pub fn create(name: &str, size: usize) -> ResultType { + let flink = Self::flink(name.to_string()); + let shmem = match ShmemConf::new() + .size(size) + .flink(&flink) + .force_create_flink() + .create() + { + Ok(m) => m, + Err(ShmemError::LinkExists) => { + bail!( + "Unable to force create shmem flink {}, which should not happen.", + flink + ) + } + Err(e) => { + bail!("Unable to create shmem flink {} : {}", flink, e); + } + }; + log::info!("Create shared memory, size:{}, flink:{}", size, flink); + Self::set_all_perm(&flink); + Ok(SharedMemory { inner: shmem }) + } + + pub fn open_existing(name: &str) -> ResultType { + let flink = Self::flink(name.to_string()); + let shmem = match ShmemConf::new().flink(&flink).allow_raw(true).open() { + Ok(m) => m, + Err(e) => { + bail!("Unable to open existing shmem flink {} : {}", flink, e); + } + }; + log::info!("open existing shared memory, flink:{:?}", flink); + Ok(SharedMemory { inner: shmem }) + } + + pub fn write(&self, addr: usize, data: &[u8]) { + unsafe { + assert!(addr + data.len() <= self.inner.len()); + let ptr = self.inner.as_ptr().add(addr); + let shared_mem_slice = slice::from_raw_parts_mut(ptr, data.len()); + shared_mem_slice.copy_from_slice(data); + } + } + + fn flink(name: String) -> String { + let mut shmem_flink = format!("shared_memory{}", name); + if cfg!(windows) { + let df = "C:\\ProgramData"; + let df = if std::path::Path::new(df).exists() { + df.to_owned() + } else { + std::env::var("TEMP").unwrap_or("C:\\Windows\\TEMP".to_owned()) + }; + let df = format!("{}\\{}", df, *hbb_common::config::APP_NAME.read().unwrap()); + std::fs::create_dir(&df).ok(); + shmem_flink = format!("{}\\{}", df, shmem_flink); + } else { + shmem_flink = Config::ipc_path("").replace("ipc", "") + &shmem_flink; + } + return shmem_flink; + } + + fn set_all_perm(_p: &str) { + #[cfg(not(windows))] + { + use std::os::unix::fs::PermissionsExt; + std::fs::set_permissions(_p, std::fs::Permissions::from_mode(0o0777)).ok(); + } + } +} + +mod utils { + use core::slice; + use std::mem::size_of; + + pub fn i32_to_vec(i: i32) -> Vec { + i.to_ne_bytes().to_vec() + } + + pub fn ptr_to_i32(ptr: *const u8) -> i32 { + unsafe { + let v = slice::from_raw_parts(ptr, size_of::()); + i32::from_ne_bytes([v[0], v[1], v[2], v[3]]) + } + } + + pub fn counter_ready(counter: *const u8) -> bool { + unsafe { + let wptr = counter; + let rptr = counter.add(size_of::()); + let iw = ptr_to_i32(wptr); + let ir = ptr_to_i32(rptr); + if ir != iw { + std::ptr::copy_nonoverlapping(wptr, rptr as *mut _, size_of::()); + true + } else { + false + } + } + } + + pub fn increase_counter(ptr: *mut u8) { + unsafe { + let i = ptr_to_i32(ptr); + let v = i32_to_vec(i + 1); + std::ptr::copy_nonoverlapping(v.as_ptr(), ptr, size_of::()); + } + } + + pub fn align(v: usize, align: usize) -> usize { + (v + align - 1) / align * align + } +} + +// functions called in seperate SYSTEM user process. +pub mod server { + use hbb_common::tokio::time::Instant; + + use super::*; + + lazy_static::lazy_static! { + static ref EXIT: Arc> = Default::default(); + } + + pub fn run_portable_service() { + let shmem = Arc::new(SharedMemory::open_existing(SHMEM_NAME).unwrap()); + let shmem1 = shmem.clone(); + let shmem2 = shmem.clone(); + let mut threads = vec![]; + threads.push(std::thread::spawn(|| { + run_get_cursor_info(shmem1); + })); + threads.push(std::thread::spawn(|| { + run_capture(shmem2); + })); + threads.push(std::thread::spawn(|| { + run_ipc_server(); + })); + threads.push(std::thread::spawn(|| { + run_exit_check(); + })); + for th in threads.drain(..) { + th.join().unwrap(); + log::info!("all thread joined"); + } + } + + fn run_exit_check() { + loop { + if EXIT.lock().unwrap().clone() { + std::thread::sleep(Duration::from_secs(1)); + log::info!("exit from seperate check thread"); + std::process::exit(0); + } + std::thread::sleep(Duration::from_secs(1)); + } + } + + fn run_get_cursor_info(shmem: Arc) { + loop { + if EXIT.lock().unwrap().clone() { + break; + } + unsafe { + let para = shmem.as_ptr().add(ADDR_CURSOR_PARA) as *mut CURSORINFO; + (*para).cbSize = size_of::() as _; + let result = winuser::GetCursorInfo(para); + if result == TRUE { + utils::increase_counter(shmem.as_ptr().add(ADDR_CURSOR_COUNTER)); + } + } + // more frequent in case of `Error of mouse_cursor service` + std::thread::sleep(Duration::from_millis(15)); + } + } + + fn run_capture(shmem: Arc) { + let mut c = None; + let mut last_current_display = usize::MAX; + let mut last_use_yuv = false; + let mut last_timeout_ms: i32 = 33; + let mut spf = Duration::from_millis(last_timeout_ms as _); + loop { + if EXIT.lock().unwrap().clone() { + break; + } + let start = std::time::Instant::now(); + unsafe { + let para_ptr = shmem.as_ptr().add(ADDR_CAPTURER_PARA); + let para = para_ptr as *const CapturerPara; + let current_display = (*para).current_display; + let use_yuv = (*para).use_yuv; + let timeout_ms = (*para).timeout_ms; + if c.is_none() { + let use_yuv = true; + *crate::video_service::CURRENT_DISPLAY.lock().unwrap() = current_display; + let (_, _current, display) = get_current_display().unwrap(); + match Capturer::new(display, use_yuv) { + Ok(mut v) => { + c = { + last_current_display = current_display; + last_use_yuv = use_yuv; + // dxgi failed at loadFrame on my PC. + // to-do: try dxgi on another PC. + v.set_gdi(); + Some(v) + } + } + Err(e) => { + log::error!("Failed to create gdi capturer:{:?}", e); + std::thread::sleep(std::time::Duration::from_secs(1)); + continue; + } + } + } else { + if current_display != last_current_display || use_yuv != last_use_yuv { + log::info!( + "display:{}->{}, use_yuv:{}->{}", + last_current_display, + current_display, + last_use_yuv, + use_yuv + ); + c = None; + continue; + } + if timeout_ms != last_timeout_ms + && timeout_ms >= 1000 / video_qos::MAX_FPS as i32 + && timeout_ms <= 1000 / video_qos::MIN_FPS as i32 + { + last_timeout_ms = timeout_ms; + spf = Duration::from_millis(timeout_ms as _); + } + } + match c.as_mut().unwrap().frame(spf) { + Ok(f) => { + let len = f.0.len(); + let len_slice = utils::i32_to_vec(len as _); + shmem.write(ADDR_CAPTURE_FRAME_SIZE, &len_slice); + shmem.write(ADDR_CAPTURE_FRAME, f.0); + shmem.write(ADDR_CAPTURE_WOULDBLOCK, &utils::i32_to_vec(TRUE)); + utils::increase_counter(shmem.as_ptr().add(ADDR_CAPTURE_FRAME_COUNTER)); + } + Err(e) => { + if e.kind() != std::io::ErrorKind::WouldBlock { + log::error!("capture frame failed:{:?}", e); + crate::platform::try_change_desktop(); + c = None; + shmem.write(ADDR_CAPTURE_WOULDBLOCK, &utils::i32_to_vec(FALSE)); + continue; + } else { + shmem.write(ADDR_CAPTURE_WOULDBLOCK, &utils::i32_to_vec(TRUE)); + } + } + } + } + let elapsed = start.elapsed(); + if elapsed < spf { + std::thread::sleep(spf - elapsed); + } + } + } + + #[tokio::main(flavor = "current_thread")] + async fn run_ipc_server() { + use DataPortableService::*; + + let postfix = IPC_PROFIX; + let last_recv_time = Arc::new(Mutex::new(Instant::now())); + let mut interval = tokio::time::interval(Duration::from_secs(1)); + + match new_listener(postfix).await { + Ok(mut incoming) => loop { + tokio::select! { + Some(result) = incoming.next() => { + match result { + Ok(stream) => { + log::info!("Got new connection"); + let last_recv_time_cloned = last_recv_time.clone(); + tokio::spawn(async move { + let mut stream = Connection::new(stream); + let postfix = postfix.to_owned(); + let mut timer = tokio::time::interval(Duration::from_secs(1)); + let mut nack = 0; + let mut old_conn_count = 0; + loop { + tokio::select! { + res = stream.next() => { + if res.is_ok() { + *last_recv_time_cloned.lock().unwrap() = Instant::now(); + } + match res { + Err(err) => { + log::error!( + "ipc{} connection closed: {}", + postfix, + err + ); + *EXIT.lock().unwrap() = true; + break; + } + Ok(Some(Data::DataPortableService(data))) => match data { + Ping => { + allow_err!( + stream + .send(&Data::DataPortableService(Pong)) + .await + ); + } + Pong => { + nack = 0; + } + ConnCount(Some(n)) => { + if old_conn_count != 0 && n == 0 { + log::info!("Connection count decrease to 0, exit"); + stream.send(&Data::DataPortableService(WillClose)).await.ok(); + *EXIT.lock().unwrap() = true; + break; + } + old_conn_count = n; + } + Mouse(v) => { + if let Ok(evt) = MouseEvent::parse_from_bytes(&v) { + crate::input_service::handle_mouse_(&evt); + } + } + Key(v) => { + if let Ok(evt) = KeyEvent::parse_from_bytes(&v) { + crate::input_service::handle_key_(&evt); + } + } + _ => {} + }, + _ => {} + } + } + _ = timer.tick() => { + nack+=1; + if nack > MAX_NACK { + log::info!("max ping nack, exit"); + *EXIT.lock().unwrap() = true; + break; + } + stream.send(&Data::DataPortableService(Ping)).await.ok(); + stream.send(&Data::DataPortableService(ConnCount(None))).await.ok(); + } + } + } + }); + } + Err(err) => { + log::error!("Couldn't get portable client: {:?}", err); + *EXIT.lock().unwrap() = true; + } + } + } + _ = interval.tick() => { + if last_recv_time.lock().unwrap().elapsed() > IPC_CONN_TIMEOUT { + log::error!("receive data timeout"); + *EXIT.lock().unwrap() = true; + } + if EXIT.lock().unwrap().clone() { + break; + } + } + } + }, + Err(err) => { + log::error!("Failed to start cm ipc server: {}", err); + *EXIT.lock().unwrap() = true; + } + } + } +} + +// functions called in main process. +pub mod client { + use hbb_common::anyhow::Context; + + use super::*; + + lazy_static::lazy_static! { + pub static ref SHMEM: Arc>> = Default::default(); + pub static ref PORTABLE_SERVICE_STATUS: Arc> = Default::default(); + static ref SENDER : Mutex> = Mutex::new(client::start_ipc_client()); + } + + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum PortableServiceStatus { + NotStarted, + Starting, + Running, + } + + impl Default for PortableServiceStatus { + fn default() -> Self { + Self::NotStarted + } + } + + pub(crate) fn start_portable_service() -> ResultType<()> { + if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == PortableServiceStatus::NotStarted { + if SHMEM.lock().unwrap().is_none() { + let displays = scrap::Display::all()?; + if displays.is_empty() { + bail!("no display available!"); + } + let mut max_pixel = 0; + let align = 64; + for d in displays { + let pixel = utils::align(d.width(), align) * utils::align(d.height(), align); + if max_pixel < pixel { + max_pixel = pixel; + } + } + let shmem_size = utils::align(ADDR_CAPTURE_FRAME + max_pixel * 4, align); + // os error 112, no enough space + *SHMEM.lock().unwrap() = Some(crate::portable_service::SharedMemory::create( + crate::portable_service::SHMEM_NAME, + shmem_size, + )?); + shutdown_hooks::add_shutdown_hook(drop_shmem); + } + if crate::common::run_me(vec!["--portable-service"]).is_err() { + *SHMEM.lock().unwrap() = None; + bail!("Failed to run portable service process"); + } + *PORTABLE_SERVICE_STATUS.lock().unwrap() = PortableServiceStatus::Starting; + let _sender = SENDER.lock().unwrap(); + } + Ok(()) + } + + extern "C" fn drop_shmem() { + log::info!("drop shared memory"); + *SHMEM.lock().unwrap() = None; + } + + pub struct CapturerPortable; + + impl CapturerPortable { + pub fn new(current_display: usize, use_yuv: bool) -> Self + where + Self: Sized, + { + Self::set_para(CapturerPara { + current_display, + use_yuv, + timeout_ms: 33, + }); + CapturerPortable {} + } + + fn set_para(para: CapturerPara) { + let mut option = SHMEM.lock().unwrap(); + let shmem = option.as_mut().unwrap(); + let para_ptr = ¶ as *const CapturerPara as *const u8; + let para_data; + unsafe { + para_data = slice::from_raw_parts(para_ptr, size_of::()); + } + shmem.write(ADDR_CAPTURER_PARA, para_data); + } + } + + impl TraitCapturer for CapturerPortable { + fn set_use_yuv(&mut self, use_yuv: bool) { + let mut option = SHMEM.lock().unwrap(); + let shmem = option.as_mut().unwrap(); + unsafe { + let para_ptr = shmem.as_ptr().add(ADDR_CAPTURER_PARA); + let para = para_ptr as *const CapturerPara; + if use_yuv != (*para).use_yuv { + Self::set_para(CapturerPara { + current_display: (*para).current_display, + use_yuv, + timeout_ms: (*para).timeout_ms, + }); + } + } + } + + fn frame<'a>(&'a mut self, timeout: Duration) -> std::io::Result> { + let mut option = SHMEM.lock().unwrap(); + let shmem = option.as_mut().unwrap(); + unsafe { + let base = shmem.as_ptr(); + let para_ptr = base.add(ADDR_CAPTURER_PARA); + let para = para_ptr as *const CapturerPara; + if timeout.as_millis() != (*para).timeout_ms as _ { + Self::set_para(CapturerPara { + current_display: (*para).current_display, + use_yuv: (*para).use_yuv, + timeout_ms: timeout.as_millis() as _, + }); + } + if utils::counter_ready(base.add(ADDR_CAPTURE_FRAME_COUNTER)) { + let frame_len_ptr = base.add(ADDR_CAPTURE_FRAME_SIZE); + let frame_len = utils::ptr_to_i32(frame_len_ptr); + let frame_ptr = base.add(ADDR_CAPTURE_FRAME); + let data = slice::from_raw_parts(frame_ptr, frame_len as usize); + Ok(Frame(data)) + } else { + let ptr = base.add(ADDR_CAPTURE_WOULDBLOCK); + let wouldblock = utils::ptr_to_i32(ptr); + if wouldblock == TRUE { + Err(std::io::Error::new( + std::io::ErrorKind::WouldBlock, + "wouldblock error".to_string(), + )) + } else { + Err(std::io::Error::new( + std::io::ErrorKind::Other, + "other error".to_string(), + )) + } + } + } + } + + fn is_gdi(&self) -> bool { + true + } + + fn set_gdi(&mut self) -> bool { + true + } + } + + pub(super) fn start_ipc_client() -> mpsc::UnboundedSender { + let (tx, rx) = mpsc::unbounded_channel::(); + std::thread::spawn(move || start_ipc_client_async(rx)); + tx + } + + #[tokio::main(flavor = "current_thread")] + async fn start_ipc_client_async(rx: mpsc::UnboundedReceiver) { + use DataPortableService::*; + let mut rx = rx; + let mut connect_failed = 0; + loop { + if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == PortableServiceStatus::NotStarted + { + sleep(1.).await; + continue; + } + if let Ok(mut c) = ipc::connect(1000, IPC_PROFIX).await { + let mut nack = 0; + let mut timer = tokio::time::interval(Duration::from_secs(1)); + loop { + tokio::select! { + res = c.next() => { + match res { + Err(err) => { + log::error!("ipc connection closed: {}", err); + break; + } + Ok(Some(Data::DataPortableService(data))) => { + match data { + Ping => { + c.send(&Data::DataPortableService(Pong)).await.ok(); + } + Pong => { + nack = 0; + *PORTABLE_SERVICE_STATUS.lock().unwrap() = PortableServiceStatus::Running; + }, + ConnCount(None) => { + let cnt = crate::server::CONN_COUNT.lock().unwrap().clone(); + c.send(&Data::DataPortableService(ConnCount(Some(cnt)))).await.ok(); + }, + WillClose => { + log::info!("portable service will close, set status to not started"); + *PORTABLE_SERVICE_STATUS.lock().unwrap() = PortableServiceStatus::NotStarted; + break; + } + _=>{} + } + } + _ => {} + } + } + _ = timer.tick() => { + nack+=1; + if nack > MAX_NACK { + // In fact, this will not happen, ipc will be closed before max nack. + log::error!("max ipc nack, set status to not started"); + *PORTABLE_SERVICE_STATUS.lock().unwrap() = PortableServiceStatus::NotStarted; + break; + } + c.send(&Data::DataPortableService(Ping)).await.ok(); + } + Some(data) = rx.recv() => { + allow_err!(c.send(&data).await); + } + + } + } + } else { + connect_failed += 1; + if connect_failed > IPC_CONN_TIMEOUT.as_secs() { + connect_failed = 0; + *PORTABLE_SERVICE_STATUS.lock().unwrap() = PortableServiceStatus::NotStarted; + log::info!( + "connect failed {} times, set status to not started", + connect_failed + ); + } + log::info!( + "client ip connect failed, status:{:?}", + PORTABLE_SERVICE_STATUS.lock().unwrap().clone(), + ); + } + sleep(1.).await; + } + } + + fn client_ipc_send(data: Data) -> ResultType<()> { + let sender = SENDER.lock().unwrap(); + sender + .send(data) + .map_err(|e| anyhow!("ipc send error:{:?}", e)) + } + + fn get_cursor_info_(shmem: &mut SharedMemory, pci: PCURSORINFO) -> BOOL { + unsafe { + let shmem_addr_para = shmem.as_ptr().add(ADDR_CURSOR_PARA); + if utils::counter_ready(shmem.as_ptr().add(ADDR_CURSOR_COUNTER)) { + std::ptr::copy_nonoverlapping(shmem_addr_para, pci as _, size_of::()); + return TRUE; + } + FALSE + } + } + + fn handle_mouse_(evt: &MouseEvent) -> ResultType<()> { + let mut v = vec![]; + evt.write_to_vec(&mut v)?; + client_ipc_send(Data::DataPortableService(DataPortableService::Mouse(v))) + } + + fn handle_key_(evt: &KeyEvent) -> ResultType<()> { + let mut v = vec![]; + evt.write_to_vec(&mut v)?; + client_ipc_send(Data::DataPortableService(DataPortableService::Key(v))) + } + + pub fn create_capturer( + current_display: usize, + display: scrap::Display, + use_yuv: bool, + ) -> ResultType> { + if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == PortableServiceStatus::Running { + log::info!("Create shared memeory capturer"); + return Ok(Box::new(CapturerPortable::new(current_display, use_yuv))); + } else { + log::debug!("Create capturer dxgi|gdi"); + return Ok(Box::new( + Capturer::new(display, use_yuv).with_context(|| "Failed to create capturer")?, + )); + } + } + + pub fn get_cursor_info(pci: PCURSORINFO) -> BOOL { + if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == PortableServiceStatus::Running { + get_cursor_info_(&mut SHMEM.lock().unwrap().as_mut().unwrap(), pci) + } else { + unsafe { winuser::GetCursorInfo(pci) } + } + } + + pub fn handle_mouse(evt: &MouseEvent) { + if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == PortableServiceStatus::Running { + handle_mouse_(evt).ok(); + } else { + crate::input_service::handle_mouse_(evt); + } + } + + pub fn handle_key(evt: &KeyEvent) { + if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == PortableServiceStatus::Running { + handle_key_(evt).ok(); + } else { + crate::input_service::handle_key_(evt); + } + } +} + +#[repr(C)] +struct CapturerPara { + current_display: usize, + use_yuv: bool, + timeout_ms: i32, +} diff --git a/src/server/video_qos.rs b/src/server/video_qos.rs index ba67d3fc..d7559615 100644 --- a/src/server/video_qos.rs +++ b/src/server/video_qos.rs @@ -1,8 +1,8 @@ use super::*; use std::time::Duration; -const FPS: u8 = 30; -const MIN_FPS: u8 = 10; -const MAX_FPS: u8 = 120; +pub const FPS: u8 = 30; +pub const MIN_FPS: u8 = 10; +pub const MAX_FPS: u8 = 120; trait Percent { fn as_percent(&self) -> u32; } diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 81ab494e..f48fefee 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -19,6 +19,8 @@ // https://slhck.info/video/2017/03/01/rate-control.html use super::{video_qos::VideoQoS, *}; +#[cfg(windows)] +use crate::portable_service::client::{PortableServiceStatus, PORTABLE_SERVICE_STATUS}; use hbb_common::tokio::sync::{ mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, Mutex as TokioMutex, @@ -49,7 +51,7 @@ pub const SCRAP_X11_REF_URL: &str = "https://rustdesk.com/docs/en/manual/linux/# pub const NAME: &'static str = "video"; lazy_static::lazy_static! { - static ref CURRENT_DISPLAY: Arc> = Arc::new(Mutex::new(usize::MAX)); + pub static ref CURRENT_DISPLAY: Arc> = Arc::new(Mutex::new(usize::MAX)); static ref LAST_ACTIVE: Arc> = Arc::new(Mutex::new(Instant::now())); static ref SWITCH: Arc> = Default::default(); static ref FRAME_FETCHED_NOTIFIER: (UnboundedSender<(i32, Option)>, Arc)>>>) = { @@ -188,6 +190,7 @@ fn create_capturer( privacy_mode_id: i32, display: Display, use_yuv: bool, + current: usize, ) -> ResultType> { #[cfg(not(windows))] let c: Option> = None; @@ -244,17 +247,18 @@ fn create_capturer( } } - let c = match c { - Some(c1) => c1, + match c { + Some(c1) => return Ok(c1), None => { - let c1 = - Capturer::new(display, use_yuv).with_context(|| "Failed to create capturer")?; log::debug!("Create capturer dxgi|gdi"); - Box::new(c1) + #[cfg(windows)] + return crate::portable_service::client::create_capturer(current, display, use_yuv); + #[cfg(not(windows))] + return Ok(Box::new( + Capturer::new(display, use_yuv).with_context(|| "Failed to create capturer")?, + )); } }; - - Ok(c) } #[cfg(windows)] @@ -277,8 +281,8 @@ fn ensure_close_virtual_device() -> ResultType<()> { 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 _ { - if let Ok((_, _, display)) = get_current_display() { - if let Ok(_) = create_capturer(privacy_mode_id, display, true) { + if let Ok((_, current, display)) = get_current_display() { + if let Ok(_) = create_capturer(privacy_mode_id, display, true, current) { return true; } } @@ -369,7 +373,7 @@ fn get_capturer(use_yuv: bool) -> ResultType { } else { log::info!("In privacy mode, the peer side cannot watch the screen"); } - let capturer = create_capturer(captuerer_privacy_mode_id, display, use_yuv)?; + let capturer = create_capturer(captuerer_privacy_mode_id, display, use_yuv, current)?; Ok(CapturerInfo { origin, width, @@ -468,6 +472,11 @@ fn run(sp: GenericService) -> ResultType<()> { let recorder: Arc>> = Default::default(); #[cfg(windows)] start_uac_elevation_check(); + #[cfg(windows)] + let portable_service_status = crate::portable_service::client::PORTABLE_SERVICE_STATUS + .lock() + .unwrap() + .clone(); #[cfg(target_os = "linux")] let mut would_block_count = 0u32; @@ -498,10 +507,17 @@ fn run(sp: GenericService) -> ResultType<()> { if codec_name != Encoder::current_hw_encoder_name() { bail!("SWITCH"); } + #[cfg(windows)] + if portable_service_status != PORTABLE_SERVICE_STATUS.lock().unwrap().clone() { + bail!("SWITCH"); + } check_privacy_mode_changed(&sp, c.privacy_mode_id)?; #[cfg(windows)] { - if crate::platform::windows::desktop_changed() { + if crate::platform::windows::desktop_changed() + && PORTABLE_SERVICE_STATUS.lock().unwrap().clone() + == PortableServiceStatus::NotStarted + { bail!("Desktop changed"); } } @@ -874,7 +890,7 @@ pub(super) fn get_current_display_2(mut all: Vec) -> ResultType<(usize, return Ok((n, current, all.remove(current))); } -fn get_current_display() -> ResultType<(usize, usize, Display)> { +pub fn get_current_display() -> ResultType<(usize, usize, Display)> { get_current_display_2(try_get_displays()?) } diff --git a/src/ui/cm.css b/src/ui/cm.css index fbbd5896..fccdb155 100644 --- a/src/ui/cm.css +++ b/src/ui/cm.css @@ -68,7 +68,7 @@ div.permissions { } div.permissions > div { - size: 48px; + size: 42px; background: color(accent); } diff --git a/src/ui/cm.rs b/src/ui/cm.rs index 3472a184..7c0e3fe2 100644 --- a/src/ui/cm.rs +++ b/src/ui/cm.rs @@ -51,6 +51,10 @@ impl InvokeUiCM for SciterHandler { fn change_language(&self) { // TODO } + + fn show_elevation(&self, show: bool) { + self.call("showElevation", &make_args!(show)); + } } impl SciterHandler { @@ -123,6 +127,14 @@ impl SciterConnectionManager { fn t(&self, name: String) -> String { crate::client::translate(name) } + + fn can_elevate(&self) -> bool { + crate::ui_cm_interface::can_elevate() + } + + fn elevate_portable(&self, id: i32) { + crate::ui_cm_interface::elevate_portable(id); + } } impl sciter::EventHandler for SciterConnectionManager { @@ -141,5 +153,7 @@ impl sciter::EventHandler for SciterConnectionManager { fn authorize(i32); fn switch_permission(i32, String, bool); fn send_msg(i32, String); + fn can_elevate(); + fn elevate_portable(i32); } } diff --git a/src/ui/cm.tis b/src/ui/cm.tis index 2b42b719..5238ab91 100644 --- a/src/ui/cm.tis +++ b/src/ui/cm.tis @@ -3,6 +3,7 @@ view.windowFrame = is_osx ? #extended : #solid; var body; var connections = []; var show_chat = false; +var show_elevation = true; class Body: Reactor.Component { @@ -27,6 +28,7 @@ class Body: Reactor.Component }; var right_style = show_chat ? "" : "display: none"; var disconnected = c.disconnected; + var show_elevation_btn = handler.can_elevate() && show_elevation; // below size:* is work around for Linux, it alreayd set in css, but not work, shit sciter return
@@ -55,10 +57,12 @@ class Body: Reactor.Component {c.port_forward ?
Port Forwarding: {c.port_forward}
: ""}
- {auth ? "" : } - {auth ? "" : } - {auth && !disconnected ? : ""} - {auth && disconnected ? : ""} + {!auth && show_elevation_btn ? : "" } + {auth ? "" : } + {auth ? "" : } + {auth && !disconnected && show_elevation_btn ? : "" } + {auth && !disconnected ? : ""} + {auth && disconnected ? : ""}
{c.is_file_transfer || c.port_forward ? "" :
{svg_chat}
}
@@ -144,6 +148,32 @@ class Body: Reactor.Component }); } + event click $(button#elevate_accept) { + var { cid, connection } = this; + checkClickTime(function() { + connection.authorized = true; + show_elevation = false; + body.update(); + handler.elevate_portable(cid); + handler.authorize(cid); + self.timer(30ms, function() { + view.windowState = View.WINDOW_MINIMIZED; + }); + }); + } + + event click $(button#elevate) { + var { cid, connection } = this; + checkClickTime(function() { + show_elevation = false; + body.update(); + handler.elevate_portable(cid); + self.timer(30ms, function() { + view.windowState = View.WINDOW_MINIMIZED; + }); + }); + } + event click $(button#dismiss) { var cid = this.cid; checkClickTime(function() { @@ -386,6 +416,13 @@ handler.newMessage = function(id, text) { update(); } +handler.showElevation = function(show) { + if (show != show_elevation) { + show_elevation = show; + update(); + } +} + view << event statechange { adjustBorder(); } diff --git a/src/ui_cm_interface.rs b/src/ui_cm_interface.rs index 72225b3f..b1e4db7f 100644 --- a/src/ui_cm_interface.rs +++ b/src/ui_cm_interface.rs @@ -85,6 +85,8 @@ pub trait InvokeUiCM: Send + Clone + 'static + Sized { fn change_theme(&self, dark: String); fn change_language(&self); + + fn show_elevation(&self, show: bool); } impl Deref for ConnectionManager { @@ -171,6 +173,10 @@ impl ConnectionManager { self.ui_handler.remove_connection(id, close); } + + fn show_elevation(&self, show: bool) { + self.ui_handler.show_elevation(show); + } } #[inline] @@ -362,6 +368,9 @@ impl IpcTaskRunner { LocalConfig::set_option("lang".to_owned(), lang); self.cm.change_language(); } + Data::DataPortableService(ipc::DataPortableService::CmShowElevation(show)) => { + self.cm.show_elevation(show); + } _ => { } @@ -757,3 +766,28 @@ fn cm_inner_send(id: i32, data: Data) { } } } + +pub fn can_elevate() -> bool { + #[cfg(windows)] + { + use crate::portable_service::client::{ + PortableServiceStatus::NotStarted, PORTABLE_SERVICE_STATUS, + }; + return !crate::platform::is_installed() + && PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == NotStarted; + } + #[cfg(not(windows))] + return false; +} + +pub fn elevate_portable(id: i32) { + #[cfg(windows)] + { + let lock = CLIENTS.read().unwrap(); + if let Some(s) = lock.get(&id) { + allow_err!(s.tx.send(ipc::Data::DataPortableService( + ipc::DataPortableService::RequestStart + ))); + } + } +} diff --git a/src/ui_interface.rs b/src/ui_interface.rs index a334fb6f..0e443ad6 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -10,8 +10,7 @@ use hbb_common::password_security; use hbb_common::{ allow_err, config::{self, Config, LocalConfig, PeerConfig}, - directories_next, log, - sleep, + directories_next, log, sleep, tokio::{self, sync::mpsc, time}, }; @@ -376,7 +375,7 @@ pub fn is_installed() -> bool { #[cfg(any(target_os = "android", target_os = "ios"))] #[inline] -pub fn is_installed() -> bool { +pub fn is_installed() -> bool { false } From e186eec5df9b238def11dcf81af06cf26ae6dc55 Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 10 Nov 2022 17:06:27 +0800 Subject: [PATCH 02/11] portable service: optimize flutter cm buttons Signed-off-by: 21pages --- flutter/lib/desktop/pages/server_page.dart | 264 +++++++++------------ 1 file changed, 112 insertions(+), 152 deletions(-) diff --git a/flutter/lib/desktop/pages/server_page.dart b/flutter/lib/desktop/pages/server_page.dart index aac6ee01..16abe0b6 100644 --- a/flutter/lib/desktop/pages/server_page.dart +++ b/flutter/lib/desktop/pages/server_page.dart @@ -76,7 +76,6 @@ class _DesktopServerPageState extends State mainAxisAlignment: MainAxisAlignment.start, children: [ Expanded(child: ConnectionManager()), - SizedBox.fromSize(size: Size(0, 15.0)), ], ), ), @@ -486,6 +485,8 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> { } } +const double bigMargin = 15; + class _CmControlPanel extends StatelessWidget { final Client client; @@ -503,180 +504,139 @@ class _CmControlPanel extends StatelessWidget { buildAuthorized(BuildContext context) { final bool canElevate = bind.cmCanElevate(); final model = Provider.of(context); - final offstage = !(canElevate && model.showElevation); - final width = offstage ? 200.0 : 100.0; - return Row( - mainAxisAlignment: MainAxisAlignment.center, + final showElevation = canElevate && model.showElevation; + return Column( + mainAxisAlignment: MainAxisAlignment.end, children: [ Offstage( - offstage: offstage, - child: Ink( - width: width, - height: 40, - decoration: BoxDecoration( - color: Colors.green[700], - borderRadius: BorderRadius.circular(10)), - child: InkWell( - onTap: () => - checkClickTime(client.id, () => handleElevate(context)), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.security_sharp, - color: Colors.white, - ), - Text( - translate("Elevate"), - style: TextStyle(color: Colors.white), - ), - ], - )), - ), + offstage: !showElevation, + child: buildButton(context, color: Colors.green[700], onClick: () { + handleElevate(context); + windowManager.minimize(); + }, + icon: Icon( + Icons.security_sharp, + color: Colors.white, + ), + text: 'Elevate', + textColor: Colors.white), ), - Offstage( - offstage: offstage, - child: SizedBox( - width: 30, - )), - Ink( - width: width, - height: 40, - decoration: BoxDecoration( - color: Colors.redAccent, borderRadius: BorderRadius.circular(10)), - child: InkWell( - onTap: () => - checkClickTime(client.id, () => handleDisconnect(context)), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - translate("Disconnect"), - style: TextStyle(color: Colors.white), - ), - ], - )), + Row( + children: [ + Expanded( + child: buildButton(context, + color: Colors.redAccent, + onClick: handleDisconnect, + text: 'Disconnect', + textColor: Colors.white)), + ], ) ], - ); + ) + .marginOnly(bottom: showElevation ? 0 : bigMargin) + .marginSymmetric(horizontal: showElevation ? 0 : bigMargin); } buildDisconnected(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Ink( - width: 200, - height: 40, - decoration: BoxDecoration( - color: MyTheme.accent, borderRadius: BorderRadius.circular(10)), - child: InkWell( - onTap: () => handleClose(context), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - translate("Close"), - style: TextStyle(color: Colors.white), - ), - ], - )), - ) + Expanded( + child: buildButton(context, + color: MyTheme.accent, + onClick: handleClose, + text: 'Close', + textColor: Colors.white)), ], - ); + ).marginOnly(bottom: 15).marginSymmetric(horizontal: bigMargin); } buildUnAuthorized(BuildContext context) { final bool canElevate = bind.cmCanElevate(); final model = Provider.of(context); - final offstage = !(canElevate && model.showElevation); - final width = offstage ? 100.0 : 85.0; - final spacerWidth = offstage ? 30.0 : 5.0; - return Row( - mainAxisAlignment: MainAxisAlignment.center, + final showElevation = canElevate && model.showElevation; + return Column( + mainAxisAlignment: MainAxisAlignment.end, children: [ Offstage( - offstage: offstage, - child: Ink( - height: 40, - width: width, - decoration: BoxDecoration( - color: Colors.green[700], - borderRadius: BorderRadius.circular(10)), - child: InkWell( - onTap: () => checkClickTime(client.id, () { - handleAccept(context); - handleElevate(context); - windowManager.minimize(); - }), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.security_sharp, - color: Colors.white, - ), - Text( - translate("Accept"), - style: TextStyle(color: Colors.white), - ), - ], - )), - ), + offstage: !showElevation, + child: buildButton(context, color: Colors.green[700], onClick: () { + handleAccept(context); + handleElevate(context); + windowManager.minimize(); + }, + text: 'Accept', + icon: Icon( + Icons.security_sharp, + color: Colors.white, + ), + textColor: Colors.white), ), - Offstage( - offstage: offstage, - child: SizedBox( - width: spacerWidth, - )), - Ink( - width: width, - height: 40, - decoration: BoxDecoration( - color: MyTheme.accent, borderRadius: BorderRadius.circular(10)), - child: InkWell( - onTap: () => checkClickTime(client.id, () { - handleAccept(context); - windowManager.minimize(); - }), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - translate("Accept"), - style: TextStyle(color: Colors.white), - ), - ], - )), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + child: buildButton(context, color: MyTheme.accent, onClick: () { + handleAccept(context); + windowManager.minimize(); + }, text: 'Accept', textColor: Colors.white)), + Expanded( + child: buildButton(context, + color: Colors.transparent, + border: Border.all(color: Colors.grey), + onClick: handleDisconnect, + text: 'Cancel', + textColor: null)), + ], ), - SizedBox( - width: spacerWidth, - ), - Ink( - width: width, - height: 40, - decoration: BoxDecoration( - color: Colors.transparent, - borderRadius: BorderRadius.circular(10), - border: Border.all(color: Colors.grey)), - child: InkWell( - onTap: () => - checkClickTime(client.id, () => handleDisconnect(context)), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - translate("Cancel"), - style: TextStyle(), - ), - ], - )), - ) ], - ); + ) + .marginOnly(bottom: showElevation ? 0 : bigMargin) + .marginSymmetric(horizontal: showElevation ? 0 : bigMargin); } - void handleDisconnect(BuildContext context) { + buildButton( + BuildContext context, { + required Color? color, + required Function() onClick, + Icon? icon, + BoxBorder? border, + required String text, + required Color? textColor, + }) { + Widget textWidget; + if (icon != null) { + textWidget = Text( + translate(text), + style: TextStyle(color: textColor), + textAlign: TextAlign.center, + ); + } else { + textWidget = Expanded( + child: Text( + translate(text), + style: TextStyle(color: textColor), + textAlign: TextAlign.center, + ), + ); + } + return Container( + height: 35, + decoration: BoxDecoration( + color: color, borderRadius: BorderRadius.circular(4), border: border), + child: InkWell( + onTap: () => checkClickTime(client.id, onClick), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Offstage(offstage: icon == null, child: icon), + textWidget, + ], + )), + ).marginAll(4); + } + + void handleDisconnect() { bind.cmCloseConnection(connId: client.id); } @@ -691,7 +651,7 @@ class _CmControlPanel extends StatelessWidget { bind.cmElevatePortable(connId: client.id); } - void handleClose(BuildContext context) async { + void handleClose() async { await bind.cmRemoveDisconnectedConnection(connId: client.id); if (await bind.cmGetClientsLength() == 0) { windowManager.close(); From 9f73b89f217f20924a4767f822fb0c0e11973dd3 Mon Sep 17 00:00:00 2001 From: 21pages Date: Fri, 11 Nov 2022 11:40:23 +0800 Subject: [PATCH 03/11] portable-service: exchange ipc server/client Signed-off-by: 21pages --- src/server/connection.rs | 48 ++-- src/server/portable_service.rs | 426 +++++++++++++++------------------ src/server/video_service.rs | 38 +-- src/ui_cm_interface.rs | 8 +- 4 files changed, 239 insertions(+), 281 deletions(-) diff --git a/src/server/connection.rs b/src/server/connection.rs index c77f6ebd..8674c6d9 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -429,31 +429,31 @@ impl Connection { _ = second_timer.tick() => { #[cfg(windows)] { - use crate::portable_service::client::{PORTABLE_SERVICE_STATUS, PortableServiceStatus::*}; - let uac = crate::video_service::IS_UAC_RUNNING.lock().unwrap().clone(); - if last_uac != uac { - last_uac = uac; - if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == NotStarted { - let mut misc = Misc::new(); - misc.set_uac(uac); - let mut msg = Message::new(); - msg.set_misc(misc); - conn.inner.send(msg.into()); - } - } - let foreground_window_elevated = crate::video_service::IS_FOREGROUND_WINDOW_ELEVATED.lock().unwrap().clone(); - if last_foreground_window_elevated != foreground_window_elevated { - last_foreground_window_elevated = foreground_window_elevated; - if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == NotStarted { - let mut misc = Misc::new(); - misc.set_foreground_window_elevated(foreground_window_elevated); - let mut msg = Message::new(); - msg.set_misc(misc); - conn.inner.send(msg.into()); - } - } if !is_installed { - let show_elevation = PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == NotStarted; + let portable_service_running = crate::portable_service::client::PORTABLE_SERVICE_RUNNING.lock().unwrap().clone(); + let uac = crate::video_service::IS_UAC_RUNNING.lock().unwrap().clone(); + if last_uac != uac { + last_uac = uac; + if !portable_service_running { + let mut misc = Misc::new(); + misc.set_uac(uac); + let mut msg = Message::new(); + msg.set_misc(misc); + conn.inner.send(msg.into()); + } + } + let foreground_window_elevated = crate::video_service::IS_FOREGROUND_WINDOW_ELEVATED.lock().unwrap().clone(); + if last_foreground_window_elevated != foreground_window_elevated { + last_foreground_window_elevated = foreground_window_elevated; + if !portable_service_running { + let mut misc = Misc::new(); + misc.set_foreground_window_elevated(foreground_window_elevated); + let mut msg = Message::new(); + msg.set_misc(misc); + conn.inner.send(msg.into()); + } + } + let show_elevation = !portable_service_running; conn.send_to_cm(ipc::Data::DataPortableService(ipc::DataPortableService::CmShowElevation(show_elevation))); } diff --git a/src/server/portable_service.rs b/src/server/portable_service.rs index 1de2a1c8..a666b56d 100644 --- a/src/server/portable_service.rs +++ b/src/server/portable_service.rs @@ -7,7 +7,6 @@ use hbb_common::{ log, message_proto::{KeyEvent, MouseEvent}, protobuf::Message, - sleep, tokio::{self, sync::mpsc}, ResultType, }; @@ -17,7 +16,7 @@ use std::{ mem::size_of, ops::{Deref, DerefMut}, sync::{Arc, Mutex}, - time::Duration, + time::{Duration, Instant}, }; use winapi::{ shared::minwindef::{BOOL, FALSE, TRUE}, @@ -48,18 +47,6 @@ const ADDR_CAPTURE_FRAME: usize = const IPC_PROFIX: &str = "_portable_service"; pub const SHMEM_NAME: &str = "_portable_service"; const MAX_NACK: usize = 3; -const IPC_CONN_TIMEOUT: Duration = Duration::from_secs(3); - -pub enum PortableServiceStatus { - NonStart, - Running, -} - -impl Default for PortableServiceStatus { - fn default() -> Self { - Self::NonStart - } -} pub struct SharedMemory { inner: Shmem, @@ -200,8 +187,6 @@ mod utils { // functions called in seperate SYSTEM user process. pub mod server { - use hbb_common::tokio::time::Instant; - use super::*; lazy_static::lazy_static! { @@ -220,7 +205,7 @@ pub mod server { run_capture(shmem2); })); threads.push(std::thread::spawn(|| { - run_ipc_server(); + run_ipc_client(); })); threads.push(std::thread::spawn(|| { run_exit_check(); @@ -270,7 +255,7 @@ pub mod server { if EXIT.lock().unwrap().clone() { break; } - let start = std::time::Instant::now(); + let start = Instant::now(); unsafe { let para_ptr = shmem.as_ptr().add(ADDR_CAPTURER_PARA); let para = para_ptr as *const CapturerPara; @@ -278,7 +263,6 @@ pub mod server { let use_yuv = (*para).use_yuv; let timeout_ms = (*para).timeout_ms; if c.is_none() { - let use_yuv = true; *crate::video_service::CURRENT_DISPLAY.lock().unwrap() = current_display; let (_, _current, display) = get_current_display().unwrap(); match Capturer::new(display, use_yuv) { @@ -348,114 +332,78 @@ pub mod server { } #[tokio::main(flavor = "current_thread")] - async fn run_ipc_server() { + async fn run_ipc_client() { use DataPortableService::*; let postfix = IPC_PROFIX; - let last_recv_time = Arc::new(Mutex::new(Instant::now())); - let mut interval = tokio::time::interval(Duration::from_secs(1)); - match new_listener(postfix).await { - Ok(mut incoming) => loop { - tokio::select! { - Some(result) = incoming.next() => { - match result { - Ok(stream) => { - log::info!("Got new connection"); - let last_recv_time_cloned = last_recv_time.clone(); - tokio::spawn(async move { - let mut stream = Connection::new(stream); - let postfix = postfix.to_owned(); - let mut timer = tokio::time::interval(Duration::from_secs(1)); - let mut nack = 0; - let mut old_conn_count = 0; - loop { - tokio::select! { - res = stream.next() => { - if res.is_ok() { - *last_recv_time_cloned.lock().unwrap() = Instant::now(); - } - match res { - Err(err) => { - log::error!( - "ipc{} connection closed: {}", - postfix, - err - ); - *EXIT.lock().unwrap() = true; - break; - } - Ok(Some(Data::DataPortableService(data))) => match data { - Ping => { - allow_err!( - stream - .send(&Data::DataPortableService(Pong)) - .await - ); - } - Pong => { - nack = 0; - } - ConnCount(Some(n)) => { - if old_conn_count != 0 && n == 0 { - log::info!("Connection count decrease to 0, exit"); - stream.send(&Data::DataPortableService(WillClose)).await.ok(); - *EXIT.lock().unwrap() = true; - break; - } - old_conn_count = n; - } - Mouse(v) => { - if let Ok(evt) = MouseEvent::parse_from_bytes(&v) { - crate::input_service::handle_mouse_(&evt); - } - } - Key(v) => { - if let Ok(evt) = KeyEvent::parse_from_bytes(&v) { - crate::input_service::handle_key_(&evt); - } - } - _ => {} - }, - _ => {} - } - } - _ = timer.tick() => { - nack+=1; - if nack > MAX_NACK { - log::info!("max ping nack, exit"); - *EXIT.lock().unwrap() = true; - break; - } - stream.send(&Data::DataPortableService(Ping)).await.ok(); - stream.send(&Data::DataPortableService(ConnCount(None))).await.ok(); - } + match ipc::connect(1000, postfix).await { + Ok(mut stream) => { + let mut timer = tokio::time::interval(Duration::from_secs(1)); + let mut nack = 0; + loop { + tokio::select! { + res = stream.next() => { + match res { + Err(err) => { + log::error!( + "ipc{} connection closed: {}", + postfix, + err + ); + break; + } + Ok(Some(Data::DataPortableService(data))) => match data { + Ping => { + allow_err!( + stream + .send(&Data::DataPortableService(Pong)) + .await + ); + } + Pong => { + nack = 0; + } + ConnCount(Some(n)) => { + if n == 0 { + log::info!("Connnection count equals 0, exit"); + stream.send(&Data::DataPortableService(WillClose)).await.ok(); + break; } } - }); - } - Err(err) => { - log::error!("Couldn't get portable client: {:?}", err); - *EXIT.lock().unwrap() = true; + Mouse(v) => { + if let Ok(evt) = MouseEvent::parse_from_bytes(&v) { + crate::input_service::handle_mouse_(&evt); + } + } + Key(v) => { + if let Ok(evt) = KeyEvent::parse_from_bytes(&v) { + crate::input_service::handle_key_(&evt); + } + } + _ => {} + }, + _ => {} } } - } - _ = interval.tick() => { - if last_recv_time.lock().unwrap().elapsed() > IPC_CONN_TIMEOUT { - log::error!("receive data timeout"); - *EXIT.lock().unwrap() = true; - } - if EXIT.lock().unwrap().clone() { - break; + _ = timer.tick() => { + nack+=1; + if nack > MAX_NACK { + log::info!("max ping nack, exit"); + break; + } + stream.send(&Data::DataPortableService(Ping)).await.ok(); + stream.send(&Data::DataPortableService(ConnCount(None))).await.ok(); } } } - }, - Err(err) => { - log::error!("Failed to start cm ipc server: {}", err); - *EXIT.lock().unwrap() = true; + } + Err(e) => { + log::error!("Failed to connect portable service ipc:{:?}", e); } } + + *EXIT.lock().unwrap() = true; } } @@ -466,54 +414,46 @@ pub mod client { use super::*; lazy_static::lazy_static! { - pub static ref SHMEM: Arc>> = Default::default(); - pub static ref PORTABLE_SERVICE_STATUS: Arc> = Default::default(); - static ref SENDER : Mutex> = Mutex::new(client::start_ipc_client()); - } - - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - pub enum PortableServiceStatus { - NotStarted, - Starting, - Running, - } - - impl Default for PortableServiceStatus { - fn default() -> Self { - Self::NotStarted - } + pub static ref PORTABLE_SERVICE_RUNNING: Arc> = Default::default(); + static ref SHMEM: Arc>> = Default::default(); + static ref SENDER : Mutex> = Mutex::new(client::start_ipc_server()); } pub(crate) fn start_portable_service() -> ResultType<()> { - if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == PortableServiceStatus::NotStarted { - if SHMEM.lock().unwrap().is_none() { - let displays = scrap::Display::all()?; - if displays.is_empty() { - bail!("no display available!"); - } - let mut max_pixel = 0; - let align = 64; - for d in displays { - let pixel = utils::align(d.width(), align) * utils::align(d.height(), align); - if max_pixel < pixel { - max_pixel = pixel; - } - } - let shmem_size = utils::align(ADDR_CAPTURE_FRAME + max_pixel * 4, align); - // os error 112, no enough space - *SHMEM.lock().unwrap() = Some(crate::portable_service::SharedMemory::create( - crate::portable_service::SHMEM_NAME, - shmem_size, - )?); - shutdown_hooks::add_shutdown_hook(drop_shmem); - } - if crate::common::run_me(vec!["--portable-service"]).is_err() { - *SHMEM.lock().unwrap() = None; - bail!("Failed to run portable service process"); - } - *PORTABLE_SERVICE_STATUS.lock().unwrap() = PortableServiceStatus::Starting; - let _sender = SENDER.lock().unwrap(); + if PORTABLE_SERVICE_RUNNING.lock().unwrap().clone() { + bail!("already running"); } + if SHMEM.lock().unwrap().is_none() { + let displays = scrap::Display::all()?; + if displays.is_empty() { + bail!("no display available!"); + } + let mut max_pixel = 0; + let align = 64; + for d in displays { + let pixel = utils::align(d.width(), align) * utils::align(d.height(), align); + if max_pixel < pixel { + max_pixel = pixel; + } + } + let shmem_size = utils::align(ADDR_CAPTURE_FRAME + max_pixel * 4, align); + // os error 112, no enough space + *SHMEM.lock().unwrap() = Some(crate::portable_service::SharedMemory::create( + crate::portable_service::SHMEM_NAME, + shmem_size, + )?); + shutdown_hooks::add_shutdown_hook(drop_shmem); + } + let mut option = SHMEM.lock().unwrap(); + let shmem = option.as_mut().unwrap(); + unsafe { + libc::memset(shmem.as_ptr() as _, 0, shmem.len() as _); + } + if crate::common::run_me(vec!["--portable-service"]).is_err() { + *SHMEM.lock().unwrap() = None; + bail!("Failed to run portable service process"); + } + let _sender = SENDER.lock().unwrap(); Ok(()) } @@ -613,94 +553,98 @@ pub mod client { } } - pub(super) fn start_ipc_client() -> mpsc::UnboundedSender { + pub(super) fn start_ipc_server() -> mpsc::UnboundedSender { let (tx, rx) = mpsc::unbounded_channel::(); - std::thread::spawn(move || start_ipc_client_async(rx)); + std::thread::spawn(move || start_ipc_server_async(rx)); tx } #[tokio::main(flavor = "current_thread")] - async fn start_ipc_client_async(rx: mpsc::UnboundedReceiver) { + async fn start_ipc_server_async(rx: mpsc::UnboundedReceiver) { use DataPortableService::*; - let mut rx = rx; - let mut connect_failed = 0; - loop { - if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == PortableServiceStatus::NotStarted - { - sleep(1.).await; - continue; - } - if let Ok(mut c) = ipc::connect(1000, IPC_PROFIX).await { - let mut nack = 0; - let mut timer = tokio::time::interval(Duration::from_secs(1)); - loop { - tokio::select! { - res = c.next() => { - match res { - Err(err) => { - log::error!("ipc connection closed: {}", err); - break; - } - Ok(Some(Data::DataPortableService(data))) => { - match data { - Ping => { - c.send(&Data::DataPortableService(Pong)).await.ok(); - } - Pong => { - nack = 0; - *PORTABLE_SERVICE_STATUS.lock().unwrap() = PortableServiceStatus::Running; - }, - ConnCount(None) => { - let cnt = crate::server::CONN_COUNT.lock().unwrap().clone(); - c.send(&Data::DataPortableService(ConnCount(Some(cnt)))).await.ok(); - }, - WillClose => { - log::info!("portable service will close, set status to not started"); - *PORTABLE_SERVICE_STATUS.lock().unwrap() = PortableServiceStatus::NotStarted; - break; - } - _=>{} - } - } - _ => {} - } - } - _ = timer.tick() => { - nack+=1; - if nack > MAX_NACK { - // In fact, this will not happen, ipc will be closed before max nack. - log::error!("max ipc nack, set status to not started"); - *PORTABLE_SERVICE_STATUS.lock().unwrap() = PortableServiceStatus::NotStarted; - break; - } - c.send(&Data::DataPortableService(Ping)).await.ok(); - } - Some(data) = rx.recv() => { - allow_err!(c.send(&data).await); - } + let rx = Arc::new(tokio::sync::Mutex::new(rx)); + let postfix = IPC_PROFIX; + match new_listener(postfix).await { + Ok(mut incoming) => loop { + { + tokio::select! { + Some(result) = incoming.next() => { + match result { + Ok(stream) => { + log::info!("Got portable service ipc connection"); + let rx_clone = rx.clone(); + tokio::spawn(async move { + let mut stream = Connection::new(stream); + let postfix = postfix.to_owned(); + let mut timer = tokio::time::interval(Duration::from_secs(1)); + let mut nack = 0; + let mut rx = rx_clone.lock().await; + loop { + tokio::select! { + res = stream.next() => { + match res { + Err(err) => { + log::info!( + "ipc{} connection closed: {}", + postfix, + err + ); + break; + } + Ok(Some(Data::DataPortableService(data))) => match data { + Ping => { + stream.send(&Data::DataPortableService(Pong)).await.ok(); + } + Pong => { + nack = 0; + *PORTABLE_SERVICE_RUNNING.lock().unwrap() = true; + }, + ConnCount(None) => { + let cnt = crate::server::CONN_COUNT.lock().unwrap().clone(); + stream.send(&Data::DataPortableService(ConnCount(Some(cnt)))).await.ok(); + }, + WillClose => { + log::info!("portable service will close"); + break; + } + _=>{} + } + _=>{} + } + } + _ = timer.tick() => { + nack+=1; + if nack > MAX_NACK { + // In fact, this will not happen, ipc will be closed before max nack. + log::error!("max ipc nack"); + break; + } + stream.send(&Data::DataPortableService(Ping)).await.ok(); + } + Some(data) = rx.recv() => { + allow_err!(stream.send(&data).await); + } + } + } + *PORTABLE_SERVICE_RUNNING.lock().unwrap() = false; + }); + } + Err(err) => { + log::error!("Couldn't get portable client: {:?}", err); + } + } + } } } - } else { - connect_failed += 1; - if connect_failed > IPC_CONN_TIMEOUT.as_secs() { - connect_failed = 0; - *PORTABLE_SERVICE_STATUS.lock().unwrap() = PortableServiceStatus::NotStarted; - log::info!( - "connect failed {} times, set status to not started", - connect_failed - ); - } - log::info!( - "client ip connect failed, status:{:?}", - PORTABLE_SERVICE_STATUS.lock().unwrap().clone(), - ); + }, + Err(err) => { + log::error!("Failed to start portable service ipc server: {}", err); } - sleep(1.).await; } } - fn client_ipc_send(data: Data) -> ResultType<()> { + fn ipc_send(data: Data) -> ResultType<()> { let sender = SENDER.lock().unwrap(); sender .send(data) @@ -721,21 +665,25 @@ pub mod client { fn handle_mouse_(evt: &MouseEvent) -> ResultType<()> { let mut v = vec![]; evt.write_to_vec(&mut v)?; - client_ipc_send(Data::DataPortableService(DataPortableService::Mouse(v))) + ipc_send(Data::DataPortableService(DataPortableService::Mouse(v))) } fn handle_key_(evt: &KeyEvent) -> ResultType<()> { let mut v = vec![]; evt.write_to_vec(&mut v)?; - client_ipc_send(Data::DataPortableService(DataPortableService::Key(v))) + ipc_send(Data::DataPortableService(DataPortableService::Key(v))) } pub fn create_capturer( current_display: usize, display: scrap::Display, use_yuv: bool, + portable_service_running: bool, ) -> ResultType> { - if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == PortableServiceStatus::Running { + if portable_service_running != PORTABLE_SERVICE_RUNNING.lock().unwrap().clone() { + log::info!("portable service status mismatch"); + } + if portable_service_running { log::info!("Create shared memeory capturer"); return Ok(Box::new(CapturerPortable::new(current_display, use_yuv))); } else { @@ -747,7 +695,7 @@ pub mod client { } pub fn get_cursor_info(pci: PCURSORINFO) -> BOOL { - if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == PortableServiceStatus::Running { + if PORTABLE_SERVICE_RUNNING.lock().unwrap().clone() { get_cursor_info_(&mut SHMEM.lock().unwrap().as_mut().unwrap(), pci) } else { unsafe { winuser::GetCursorInfo(pci) } @@ -755,7 +703,7 @@ pub mod client { } pub fn handle_mouse(evt: &MouseEvent) { - if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == PortableServiceStatus::Running { + if PORTABLE_SERVICE_RUNNING.lock().unwrap().clone() { handle_mouse_(evt).ok(); } else { crate::input_service::handle_mouse_(evt); @@ -763,7 +711,7 @@ pub mod client { } pub fn handle_key(evt: &KeyEvent) { - if PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == PortableServiceStatus::Running { + if PORTABLE_SERVICE_RUNNING.lock().unwrap().clone() { handle_key_(evt).ok(); } else { crate::input_service::handle_key_(evt); diff --git a/src/server/video_service.rs b/src/server/video_service.rs index f48fefee..43ce013f 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -20,7 +20,7 @@ use super::{video_qos::VideoQoS, *}; #[cfg(windows)] -use crate::portable_service::client::{PortableServiceStatus, PORTABLE_SERVICE_STATUS}; +use crate::portable_service::client::PORTABLE_SERVICE_RUNNING; use hbb_common::tokio::sync::{ mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, Mutex as TokioMutex, @@ -191,6 +191,7 @@ fn create_capturer( display: Display, use_yuv: bool, current: usize, + _portable_service_running: bool, ) -> ResultType> { #[cfg(not(windows))] let c: Option> = None; @@ -252,7 +253,12 @@ fn create_capturer( None => { log::debug!("Create capturer dxgi|gdi"); #[cfg(windows)] - return crate::portable_service::client::create_capturer(current, display, use_yuv); + return crate::portable_service::client::create_capturer( + current, + display, + use_yuv, + _portable_service_running, + ); #[cfg(not(windows))] return Ok(Box::new( Capturer::new(display, use_yuv).with_context(|| "Failed to create capturer")?, @@ -282,7 +288,7 @@ 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 _ { if let Ok((_, current, display)) = get_current_display() { - if let Ok(_) = create_capturer(privacy_mode_id, display, true, current) { + if let Ok(_) = create_capturer(privacy_mode_id, display, true, current, false) { return true; } } @@ -331,7 +337,7 @@ impl DerefMut for CapturerInfo { } } -fn get_capturer(use_yuv: bool) -> ResultType { +fn get_capturer(use_yuv: bool, portable_service_running: bool) -> ResultType { #[cfg(target_os = "linux")] { if !scrap::is_x11() { @@ -373,7 +379,13 @@ fn get_capturer(use_yuv: bool) -> ResultType { } else { log::info!("In privacy mode, the peer side cannot watch the screen"); } - let capturer = create_capturer(captuerer_privacy_mode_id, display, use_yuv, current)?; + let capturer = create_capturer( + captuerer_privacy_mode_id, + display, + use_yuv, + current, + portable_service_running, + )?; Ok(CapturerInfo { origin, width, @@ -393,8 +405,12 @@ fn run(sp: GenericService) -> ResultType<()> { // ensure_inited() is needed because release_resouce() may be called. #[cfg(target_os = "linux")] super::wayland::ensure_inited()?; + #[cfg(windows)] + let last_portable_service_running = PORTABLE_SERVICE_RUNNING.lock().unwrap().clone(); + #[cfg(not(windows))] + let last_portable_service_running = false; - let mut c = get_capturer(true)?; + let mut c = get_capturer(true, last_portable_service_running)?; let mut video_qos = VIDEO_QOS.lock().unwrap(); video_qos.set_size(c.width as _, c.height as _); @@ -472,11 +488,6 @@ fn run(sp: GenericService) -> ResultType<()> { let recorder: Arc>> = Default::default(); #[cfg(windows)] start_uac_elevation_check(); - #[cfg(windows)] - let portable_service_status = crate::portable_service::client::PORTABLE_SERVICE_STATUS - .lock() - .unwrap() - .clone(); #[cfg(target_os = "linux")] let mut would_block_count = 0u32; @@ -508,15 +519,14 @@ fn run(sp: GenericService) -> ResultType<()> { bail!("SWITCH"); } #[cfg(windows)] - if portable_service_status != PORTABLE_SERVICE_STATUS.lock().unwrap().clone() { + if last_portable_service_running != PORTABLE_SERVICE_RUNNING.lock().unwrap().clone() { bail!("SWITCH"); } check_privacy_mode_changed(&sp, c.privacy_mode_id)?; #[cfg(windows)] { if crate::platform::windows::desktop_changed() - && PORTABLE_SERVICE_STATUS.lock().unwrap().clone() - == PortableServiceStatus::NotStarted + && !PORTABLE_SERVICE_RUNNING.lock().unwrap().clone() { bail!("Desktop changed"); } diff --git a/src/ui_cm_interface.rs b/src/ui_cm_interface.rs index b1e4db7f..26b26bf9 100644 --- a/src/ui_cm_interface.rs +++ b/src/ui_cm_interface.rs @@ -770,11 +770,11 @@ fn cm_inner_send(id: i32, data: Data) { pub fn can_elevate() -> bool { #[cfg(windows)] { - use crate::portable_service::client::{ - PortableServiceStatus::NotStarted, PORTABLE_SERVICE_STATUS, - }; return !crate::platform::is_installed() - && PORTABLE_SERVICE_STATUS.lock().unwrap().clone() == NotStarted; + && !crate::portable_service::client::PORTABLE_SERVICE_RUNNING + .lock() + .unwrap() + .clone(); } #[cfg(not(windows))] return false; From bee19bfe176c7032d59248182e1d7767037f7446 Mon Sep 17 00:00:00 2001 From: 21pages Date: Fri, 11 Nov 2022 20:44:16 +0800 Subject: [PATCH 04/11] portable-service: optimize sciter cm buttons Signed-off-by: 21pages --- src/ui/cm.css | 42 +++++++++++++++++++++++++++++++++--------- src/ui/cm.tis | 18 +++++++++++------- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/src/ui/cm.css b/src/ui/cm.css index fccdb155..ff4d422e 100644 --- a/src/ui/cm.css +++ b/src/ui/cm.css @@ -112,20 +112,44 @@ icon.recording { background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAANpJREFUWEftltENAiEMhtsJ1NcynG6gI+gGugEOR591gppeQoIYSDBILxEeydH/57u2FMF4obE+TAOTwLoIhBDOAHBExG2n6rgR0akW640AM0sn4SWMiDycc7s8JjN7Ijro/k8NqAAR5RoeAPZxv2ggP9hCJiWZxtGbq3hqbJiBVHy4gVx8qAER8Yi4JFy6huVAKXemgb8icI+1b5KEitq0DOO/Nm1EEX1TK27p/bVvv36MOhl4EtHHbFF7jq8AoG1z08OAiFycczrkFNe6RrIet26NMQlMAuYEXiayryF/QQktAAAAAElFTkSuQmCC'); } -div.buttons { - width: *; - border-spacing: 0.5em; - text-align: center; +div.outer_buttons { + flow:vertical; + border-spacing:8; } -div.buttons button { - width: 80px; - height: 40px; - margin: 0.5em; +div.inner_buttons { + flow:horizontal; + border-spacing:8; +} + +button.control { + width: *; +} + +button.elevate { + background:green; +} + +button.elevate:active { + background: rgb(2, 104, 2); + border-color: color(hover-border); +} + +button.elevate>span { + flow:horizontal; + width: *; +} + +button.elevate>span>span { + margin-left:*; + margin-right:*; +} + +button.elevate>span>span>span { + vertical-align: middle; } button#disconnect { - width: 160px; background: color(blood-red); border: none; } diff --git a/src/ui/cm.tis b/src/ui/cm.tis index 5238ab91..035b5865 100644 --- a/src/ui/cm.tis +++ b/src/ui/cm.tis @@ -4,6 +4,7 @@ var body; var connections = []; var show_chat = false; var show_elevation = true; +var svg_elevate = ; class Body: Reactor.Component { @@ -56,14 +57,17 @@ class Body: Reactor.Component } {c.port_forward ?
Port Forwarding: {c.port_forward}
: ""}
-
- {!auth && show_elevation_btn ? : "" } - {auth ? "" : } - {auth ? "" : } - {auth && !disconnected && show_elevation_btn ? : "" } - {auth && !disconnected ? : ""} - {auth && disconnected ? : ""} +
+ {!auth && !disconnected && show_elevation_btn ? : "" } + {auth && !disconnected && show_elevation_btn ? : "" } +
+ {!auth ? : "" } + {!auth ? : "" } +
+ {auth && !disconnected ? : "" } + {auth && disconnected ? : "" }
+
{c.is_file_transfer || c.port_forward ? "" :
{svg_chat}
}
From 4d492cb2c6bf83f904625a9c87de99115b23f905 Mon Sep 17 00:00:00 2001 From: 21pages Date: Sat, 12 Nov 2022 17:18:26 +0800 Subject: [PATCH 05/11] portable-service: fix set capture para dead lock Signed-off-by: 21pages --- src/server/portable_service.rs | 45 ++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/server/portable_service.rs b/src/server/portable_service.rs index a666b56d..d78038ec 100644 --- a/src/server/portable_service.rs +++ b/src/server/portable_service.rs @@ -469,17 +469,20 @@ pub mod client { where Self: Sized, { - Self::set_para(CapturerPara { - current_display, - use_yuv, - timeout_ms: 33, - }); + let mut option = SHMEM.lock().unwrap(); + let shmem = option.as_mut().unwrap(); + Self::set_para( + shmem, + CapturerPara { + current_display, + use_yuv, + timeout_ms: 33, + }, + ); CapturerPortable {} } - fn set_para(para: CapturerPara) { - let mut option = SHMEM.lock().unwrap(); - let shmem = option.as_mut().unwrap(); + fn set_para(shmem: &mut SharedMemory, para: CapturerPara) { let para_ptr = ¶ as *const CapturerPara as *const u8; let para_data; unsafe { @@ -497,11 +500,14 @@ pub mod client { let para_ptr = shmem.as_ptr().add(ADDR_CAPTURER_PARA); let para = para_ptr as *const CapturerPara; if use_yuv != (*para).use_yuv { - Self::set_para(CapturerPara { - current_display: (*para).current_display, - use_yuv, - timeout_ms: (*para).timeout_ms, - }); + Self::set_para( + shmem, + CapturerPara { + current_display: (*para).current_display, + use_yuv, + timeout_ms: (*para).timeout_ms, + }, + ); } } } @@ -514,11 +520,14 @@ pub mod client { let para_ptr = base.add(ADDR_CAPTURER_PARA); let para = para_ptr as *const CapturerPara; if timeout.as_millis() != (*para).timeout_ms as _ { - Self::set_para(CapturerPara { - current_display: (*para).current_display, - use_yuv: (*para).use_yuv, - timeout_ms: timeout.as_millis() as _, - }); + Self::set_para( + shmem, + CapturerPara { + current_display: (*para).current_display, + use_yuv: (*para).use_yuv, + timeout_ms: timeout.as_millis() as _, + }, + ); } if utils::counter_ready(base.add(ADDR_CAPTURE_FRAME_COUNTER)) { let frame_len_ptr = base.add(ADDR_CAPTURE_FRAME_SIZE); From 8c529a1159dc98b2c3d9866a03c15d56b062023d Mon Sep 17 00:00:00 2001 From: 21pages Date: Sun, 13 Nov 2022 16:33:22 +0800 Subject: [PATCH 06/11] portable-service: add yuv set flag to fix start splash Signed-off-by: 21pages --- src/server/portable_service.rs | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/server/portable_service.rs b/src/server/portable_service.rs index d78038ec..9256c9a7 100644 --- a/src/server/portable_service.rs +++ b/src/server/portable_service.rs @@ -261,7 +261,13 @@ pub mod server { let para = para_ptr as *const CapturerPara; let current_display = (*para).current_display; let use_yuv = (*para).use_yuv; + let use_yuv_set = (*para).use_yuv_set; let timeout_ms = (*para).timeout_ms; + if !use_yuv_set { + c = None; + std::thread::sleep(spf); + continue; + } if c.is_none() { *crate::video_service::CURRENT_DISPLAY.lock().unwrap() = current_display; let (_, _current, display) = get_current_display().unwrap(); @@ -476,6 +482,7 @@ pub mod client { CapturerPara { current_display, use_yuv, + use_yuv_set: false, timeout_ms: 33, }, ); @@ -499,16 +506,15 @@ pub mod client { unsafe { let para_ptr = shmem.as_ptr().add(ADDR_CAPTURER_PARA); let para = para_ptr as *const CapturerPara; - if use_yuv != (*para).use_yuv { - Self::set_para( - shmem, - CapturerPara { - current_display: (*para).current_display, - use_yuv, - timeout_ms: (*para).timeout_ms, - }, - ); - } + Self::set_para( + shmem, + CapturerPara { + current_display: (*para).current_display, + use_yuv, + use_yuv_set: true, + timeout_ms: (*para).timeout_ms, + }, + ); } } @@ -525,6 +531,7 @@ pub mod client { CapturerPara { current_display: (*para).current_display, use_yuv: (*para).use_yuv, + use_yuv_set: (*para).use_yuv_set, timeout_ms: timeout.as_millis() as _, }, ); @@ -732,5 +739,6 @@ pub mod client { struct CapturerPara { current_display: usize, use_yuv: bool, + use_yuv_set: bool, timeout_ms: i32, } From 3f77b6bc6498e92f5b132bbdefba7059c21c2325 Mon Sep 17 00:00:00 2001 From: 21pages Date: Sun, 13 Nov 2022 17:01:09 +0800 Subject: [PATCH 07/11] portable service: sync capture counter to make continuous frame, which will decrease fps Signed-off-by: 21pages --- src/server/portable_service.rs | 41 +++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/src/server/portable_service.rs b/src/server/portable_service.rs index 9256c9a7..9e3f11bc 100644 --- a/src/server/portable_service.rs +++ b/src/server/portable_service.rs @@ -172,11 +172,28 @@ mod utils { } } - pub fn increase_counter(ptr: *mut u8) { + pub fn counter_equal(counter: *const u8) -> bool { unsafe { - let i = ptr_to_i32(ptr); - let v = i32_to_vec(i + 1); - std::ptr::copy_nonoverlapping(v.as_ptr(), ptr, size_of::()); + let wptr = counter; + let rptr = counter.add(size_of::()); + let iw = ptr_to_i32(wptr); + let ir = ptr_to_i32(rptr); + iw == ir + } + } + + pub fn increase_counter(counter: *mut u8) { + unsafe { + let wptr = counter; + let rptr = counter.add(size_of::()); + let iw = ptr_to_i32(counter); + let ir = ptr_to_i32(counter); + let v = i32_to_vec(iw + 1); + std::ptr::copy_nonoverlapping(v.as_ptr(), wptr, size_of::()); + if ir == iw + 1 { + let v = i32_to_vec(iw); + std::ptr::copy_nonoverlapping(v.as_ptr(), rptr, size_of::()); + } } } @@ -212,7 +229,7 @@ pub mod server { })); for th in threads.drain(..) { th.join().unwrap(); - log::info!("all thread joined"); + log::info!("thread joined"); } } @@ -251,11 +268,11 @@ pub mod server { let mut last_use_yuv = false; let mut last_timeout_ms: i32 = 33; let mut spf = Duration::from_millis(last_timeout_ms as _); + let mut first_frame_captured = false; loop { if EXIT.lock().unwrap().clone() { break; } - let start = Instant::now(); unsafe { let para_ptr = shmem.as_ptr().add(ADDR_CAPTURER_PARA); let para = para_ptr as *const CapturerPara; @@ -276,6 +293,7 @@ pub mod server { c = { last_current_display = current_display; last_use_yuv = use_yuv; + first_frame_captured = false; // dxgi failed at loadFrame on my PC. // to-do: try dxgi on another PC. v.set_gdi(); @@ -308,6 +326,12 @@ pub mod server { spf = Duration::from_millis(timeout_ms as _); } } + if first_frame_captured { + if !utils::counter_equal(shmem.as_ptr().add(ADDR_CAPTURE_FRAME_COUNTER)) { + std::thread::sleep(spf); + continue; + } + } match c.as_mut().unwrap().frame(spf) { Ok(f) => { let len = f.0.len(); @@ -316,6 +340,7 @@ pub mod server { shmem.write(ADDR_CAPTURE_FRAME, f.0); shmem.write(ADDR_CAPTURE_WOULDBLOCK, &utils::i32_to_vec(TRUE)); utils::increase_counter(shmem.as_ptr().add(ADDR_CAPTURE_FRAME_COUNTER)); + first_frame_captured = true; } Err(e) => { if e.kind() != std::io::ErrorKind::WouldBlock { @@ -330,10 +355,6 @@ pub mod server { } } } - let elapsed = start.elapsed(); - if elapsed < spf { - std::thread::sleep(spf - elapsed); - } } } From 45bfb0e22e4440080bfdd7927ff3358adf0a288e Mon Sep 17 00:00:00 2001 From: 21pages Date: Sun, 13 Nov 2022 18:38:24 +0800 Subject: [PATCH 08/11] portable-service: run background Signed-off-by: 21pages --- src/platform/windows.rs | 21 +++++++++++++++++++++ src/server/portable_service.rs | 7 ++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/platform/windows.rs b/src/platform/windows.rs index b418d090..34f13289 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -1480,6 +1480,27 @@ pub fn get_user_token(session_id: u32, as_user: bool) -> HANDLE { } } +pub fn run_background(exe: &str, arg: &str) -> ResultType { + let wexe = wide_string(exe); + let warg; + unsafe { + let ret = ShellExecuteW( + NULL as _, + NULL as _, + wexe.as_ptr() as _, + if arg.is_empty() { + NULL as _ + } else { + warg = wide_string(arg); + warg.as_ptr() as _ + }, + NULL as _, + SW_HIDE, + ); + return Ok(ret as i32 > 32); + } +} + pub fn run_uac(exe: &str, arg: &str) -> ResultType { let wop = wide_string("runas"); let wexe = wide_string(exe); diff --git a/src/server/portable_service.rs b/src/server/portable_service.rs index 9e3f11bc..54f7a92d 100644 --- a/src/server/portable_service.rs +++ b/src/server/portable_service.rs @@ -476,7 +476,12 @@ pub mod client { unsafe { libc::memset(shmem.as_ptr() as _, 0, shmem.len() as _); } - if crate::common::run_me(vec!["--portable-service"]).is_err() { + if crate::platform::run_background( + &std::env::current_exe()?.to_string_lossy().to_string(), + "--portable-service", + ) + .is_err() + { *SHMEM.lock().unwrap() = None; bail!("Failed to run portable service process"); } From abd665153b331de07646eb3499130a4af7906627 Mon Sep 17 00:00:00 2001 From: 21pages Date: Sun, 13 Nov 2022 20:15:38 +0800 Subject: [PATCH 09/11] portable-service: try dxgi before gdi, which not controlled by video_service Signed-off-by: 21pages --- src/server/portable_service.rs | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/server/portable_service.rs b/src/server/portable_service.rs index 54f7a92d..48cdcbe5 100644 --- a/src/server/portable_service.rs +++ b/src/server/portable_service.rs @@ -47,6 +47,7 @@ const ADDR_CAPTURE_FRAME: usize = const IPC_PROFIX: &str = "_portable_service"; pub const SHMEM_NAME: &str = "_portable_service"; const MAX_NACK: usize = 3; +const MAX_DXGI_FAIL_TIME: usize = 5; pub struct SharedMemory { inner: Shmem, @@ -269,6 +270,7 @@ pub mod server { let mut last_timeout_ms: i32 = 33; let mut spf = Duration::from_millis(last_timeout_ms as _); let mut first_frame_captured = false; + let mut dxgi_failed_times = 0; loop { if EXIT.lock().unwrap().clone() { break; @@ -294,9 +296,10 @@ pub mod server { last_current_display = current_display; last_use_yuv = use_yuv; first_frame_captured = false; - // dxgi failed at loadFrame on my PC. - // to-do: try dxgi on another PC. - v.set_gdi(); + if dxgi_failed_times > MAX_DXGI_FAIL_TIME { + dxgi_failed_times = 0; + v.set_gdi(); + } Some(v) } } @@ -341,14 +344,26 @@ pub mod server { shmem.write(ADDR_CAPTURE_WOULDBLOCK, &utils::i32_to_vec(TRUE)); utils::increase_counter(shmem.as_ptr().add(ADDR_CAPTURE_FRAME_COUNTER)); first_frame_captured = true; + dxgi_failed_times = 0; } Err(e) => { if e.kind() != std::io::ErrorKind::WouldBlock { - log::error!("capture frame failed:{:?}", e); - crate::platform::try_change_desktop(); - c = None; - shmem.write(ADDR_CAPTURE_WOULDBLOCK, &utils::i32_to_vec(FALSE)); - continue; + // DXGI_ERROR_INVALID_CALL after each success on Microsoft GPU driver + // log::error!("capture frame failed:{:?}", e); + if crate::platform::windows::desktop_changed() { + crate::platform::try_change_desktop(); + c = None; + std::thread::sleep(spf); + continue; + } + if !c.as_ref().unwrap().is_gdi() { + dxgi_failed_times += 1; + } + if dxgi_failed_times > MAX_DXGI_FAIL_TIME { + c = None; + shmem.write(ADDR_CAPTURE_WOULDBLOCK, &utils::i32_to_vec(FALSE)); + std::thread::sleep(spf); + } } else { shmem.write(ADDR_CAPTURE_WOULDBLOCK, &utils::i32_to_vec(TRUE)); } @@ -512,6 +527,7 @@ pub mod client { timeout_ms: 33, }, ); + shmem.write(ADDR_CAPTURE_WOULDBLOCK, &utils::i32_to_vec(TRUE)); CapturerPortable {} } @@ -586,6 +602,7 @@ pub mod client { } } + // control by itself fn is_gdi(&self) -> bool { true } From ca8cb5a3b0f74af7c2c9764905b0239a27135963 Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 15 Nov 2022 16:49:55 +0800 Subject: [PATCH 10/11] portable-service: better prompt message Signed-off-by: 21pages --- flutter/lib/common.dart | 21 ++++++---- flutter/lib/desktop/pages/remote_page.dart | 2 +- .../lib/desktop/widgets/remote_menubar.dart | 2 +- .../lib/mobile/pages/file_manager_page.dart | 3 +- flutter/lib/mobile/pages/remote_page.dart | 4 +- flutter/lib/mobile/widgets/dialog.dart | 6 +-- flutter/lib/models/model.dart | 11 ++++- src/client/io_loop.rs | 42 ++++++++++--------- src/flutter.rs | 7 +++- src/lang/cn.rs | 5 +-- src/lang/cs.rs | 5 +-- src/lang/da.rs | 5 +-- src/lang/de.rs | 5 +-- src/lang/en.rs | 4 +- src/lang/eo.rs | 5 +-- src/lang/es.rs | 5 +-- src/lang/fa.rs | 5 +-- src/lang/fr.rs | 5 +-- src/lang/hu.rs | 5 +-- src/lang/id.rs | 5 +-- src/lang/it.rs | 5 +-- src/lang/ja.rs | 5 +-- src/lang/ko.rs | 5 +-- src/lang/kz.rs | 5 +-- src/lang/pl.rs | 5 +-- src/lang/pt_PT.rs | 5 +-- src/lang/ptbr.rs | 5 +-- src/lang/ru.rs | 5 +-- src/lang/sk.rs | 5 +-- src/lang/template.rs | 5 +-- src/lang/tr.rs | 5 +-- src/lang/tw.rs | 5 +-- src/lang/ua.rs | 5 +-- src/lang/vn.rs | 5 +-- src/server/connection.rs | 9 ++-- src/ui/common.tis | 8 ++++ src/ui/remote.rs | 9 +++- src/ui_session_interface.rs | 7 ++-- 38 files changed, 131 insertions(+), 124 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 93fe0fee..483aba38 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -627,7 +627,7 @@ class CustomAlertDialog extends StatelessWidget { } } -void msgBox(String type, String title, String text, String link, +void msgBox(String id, String type, String title, String text, String link, OverlayDialogManager dialogManager, {bool? hasCancel}) { dialogManager.dismissAll(); @@ -672,14 +672,17 @@ void msgBox(String type, String title, String text, String link, if (link.isNotEmpty) { buttons.insert(0, msgBoxButton(translate('JumpLink'), jumplink)); } - dialogManager.show((setState, close) => CustomAlertDialog( - title: _msgBoxTitle(title), - content: SelectableText(translate(text), - style: const TextStyle(fontSize: 15)), - actions: buttons, - onSubmit: hasOk ? submit : null, - onCancel: hasCancel == true ? cancel : null, - )); + dialogManager.show( + (setState, close) => CustomAlertDialog( + title: _msgBoxTitle(title), + content: + SelectableText(translate(text), style: const TextStyle(fontSize: 15)), + actions: buttons, + onSubmit: hasOk ? submit : null, + onCancel: hasCancel == true ? cancel : null, + ), + tag: '$id-$type-$title-$text-$link', + ); } Widget msgBoxButton(String text, void Function() onPressed) { diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 2b8c9994..dae3fa61 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -163,7 +163,7 @@ class _RemotePageState extends State super.build(context); return WillPopScope( onWillPop: () async { - clientClose(_ffi.dialogManager); + clientClose(widget.id, _ffi.dialogManager); return false; }, child: MultiProvider(providers: [ diff --git a/flutter/lib/desktop/widgets/remote_menubar.dart b/flutter/lib/desktop/widgets/remote_menubar.dart index 75311c4c..cdbeb0be 100644 --- a/flutter/lib/desktop/widgets/remote_menubar.dart +++ b/flutter/lib/desktop/widgets/remote_menubar.dart @@ -489,7 +489,7 @@ class _RemoteMenubarState extends State { return IconButton( tooltip: translate('Close'), onPressed: () { - clientClose(widget.ffi.dialogManager); + clientClose(widget.id, widget.ffi.dialogManager); }, icon: const Icon( Icons.close, diff --git a/flutter/lib/mobile/pages/file_manager_page.dart b/flutter/lib/mobile/pages/file_manager_page.dart index 3221cdba..6e5c9148 100644 --- a/flutter/lib/mobile/pages/file_manager_page.dart +++ b/flutter/lib/mobile/pages/file_manager_page.dart @@ -63,7 +63,8 @@ class _FileManagerPageState extends State { leading: Row(children: [ IconButton( icon: Icon(Icons.close), - onPressed: () => clientClose(gFFI.dialogManager)), + onPressed: () => + clientClose(widget.id, gFFI.dialogManager)), ]), centerTitle: true, title: ToggleSwitch( diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 07304d2d..719b7dc2 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -223,7 +223,7 @@ class _RemotePageState extends State { return WillPopScope( onWillPop: () async { - clientClose(gFFI.dialogManager); + clientClose(widget.id, gFFI.dialogManager); return false; }, child: getRawPointerAndKeyBody(Scaffold( @@ -304,7 +304,7 @@ class _RemotePageState extends State { color: Colors.white, icon: Icon(Icons.clear), onPressed: () { - clientClose(gFFI.dialogManager); + clientClose(widget.id, gFFI.dialogManager); }, ) ] + diff --git a/flutter/lib/mobile/widgets/dialog.dart b/flutter/lib/mobile/widgets/dialog.dart index 03b36ecf..96f96658 100644 --- a/flutter/lib/mobile/widgets/dialog.dart +++ b/flutter/lib/mobile/widgets/dialog.dart @@ -5,9 +5,9 @@ import '../../common.dart'; import '../../models/model.dart'; import '../../models/platform_model.dart'; -void clientClose(OverlayDialogManager dialogManager) { - msgBox( - '', 'Close', 'Are you sure to close the connection?', '', dialogManager); +void clientClose(String id, OverlayDialogManager dialogManager) { + msgBox(id, '', 'Close', 'Are you sure to close the connection?', '', + dialogManager); } void showSuccess() { diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index fb4f8b4f..a39bc7d0 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -195,6 +195,8 @@ class FfiModel with ChangeNotifier { } else if (name == 'show_elevation') { final show = evt['show'].toString() == 'true'; parent.target?.serverModel.setShowElevation(show); + } else if (name == 'cancel_msgbox') { + cancelMsgBox(evt, peerId); } }; } @@ -231,6 +233,13 @@ class FfiModel with ChangeNotifier { notifyListeners(); } + cancelMsgBox(Map evt, String id) { + if (parent.target == null) return; + final dialogManager = parent.target!.dialogManager; + final tag = '$id-${evt['tag']}'; + dialogManager.dismissByTag(tag); + } + /// Handle the message box event based on [evt] and [id]. handleMsgBox(Map evt, String id) { if (parent.target == null) return; @@ -256,7 +265,7 @@ class FfiModel with ChangeNotifier { showMsgBox(String id, String type, String title, String text, String link, bool hasRetry, OverlayDialogManager dialogManager, {bool? hasCancel}) { - msgBox(type, title, text, link, dialogManager, hasCancel: hasCancel); + msgBox(id, type, title, text, link, dialogManager, hasCancel: hasCancel); _timer?.cancel(); if (hasRetry) { _timer = Timer(Duration(seconds: _reconnects), () { diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 04d1d4d2..6eb44310 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -20,18 +20,14 @@ use hbb_common::fs::{ use hbb_common::message_proto::permission_info::Permission; use hbb_common::protobuf::Message as _; use hbb_common::rendezvous_proto::ConnType; +#[cfg(windows)] +use hbb_common::tokio::sync::Mutex as TokioMutex; use hbb_common::tokio::{ self, sync::mpsc, time::{self, Duration, Instant, Interval}, }; -#[cfg(windows)] -use hbb_common::tokio::sync::Mutex as TokioMutex; -use hbb_common::{ - allow_err, - message_proto::*, - sleep, -}; +use hbb_common::{allow_err, message_proto::*, sleep}; use hbb_common::{fs, log, Stream}; use std::collections::HashMap; @@ -998,23 +994,31 @@ impl Remote { } } Some(misc::Union::Uac(uac)) => { + let msgtype = "custom-uac-nocancel"; + let title = "Prompt"; + let text = "Please wait for confirmation of UAC..."; + let link = ""; if uac { - self.handler.msgbox( - "custom-uac-nocancel", - "Warning", - "uac_warning", - "", - ); + self.handler.msgbox(msgtype, title, text, link); + } else { + self.handler + .cancel_msgbox( + &format!("{}-{}-{}-{}", msgtype, title, text, link,), + ); } } Some(misc::Union::ForegroundWindowElevated(elevated)) => { + let msgtype = "custom-elevated-foreground-nocancel"; + let title = "Prompt"; + let text = "elevated_foreground_window_tip"; + let link = ""; if elevated { - self.handler.msgbox( - "custom-elevated-foreground-nocancel", - "Warning", - "elevated_foreground_window_warning", - "", - ); + self.handler.msgbox(msgtype, title, text, link); + } else { + self.handler + .cancel_msgbox( + &format!("{}-{}-{}-{}", msgtype, title, text, link,), + ); } } _ => {} diff --git a/src/flutter.rs b/src/flutter.rs index a69473e5..9c420862 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -228,8 +228,7 @@ impl InvokeUiSession for FlutterHandler { id: i32, entries: &Vec, path: String, - #[allow(unused_variables)] - is_local: bool, + #[allow(unused_variables)] is_local: bool, only_count: bool, ) { // TODO opt @@ -327,6 +326,10 @@ impl InvokeUiSession for FlutterHandler { ); } + fn cancel_msgbox(&self, tag: &str) { + self.push_event("cancel_msgbox", vec![("tag", tag)]); + } + fn new_message(&self, msg: String) { self.push_event("chat_client_mode", vec![("text", &msg)]); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index b622123a..68c5dbf6 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -371,9 +371,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Deny LAN Discovery", "拒绝局域网发现"), ("Write a message", "输入聊天消息"), ("Prompt", "提示"), - ("elevation_prompt", "以当前用户权限运行软件,可能导致远端在访问本机时,没有足够的权限来操作部分窗口。"), - ("uac_warning", "暂时无法访问远端设备,因为远端设备正在请求用户账户权限,请等待对方关闭UAC窗口。为避免这个问题,建议在远端设备上安装或者以管理员权限运行本软件。"), - ("elevated_foreground_window_warning", "暂时无法使用鼠标键盘,因为远端桌面的当前窗口需要更高的权限才能操作, 可以请求对方最小化当前窗口。为避免这个问题,建议在远端设备上安装或者以管理员权限运行本软件。"), + ("Please wait for confirmation of UAC...", "请等待对方确认UAC..."), + ("elevated_foreground_window_tip", "远端桌面的当前窗口需要更高的权限才能操作, 暂时无法使用鼠标键盘, 可以请求对方最小化当前窗口, 或者在连接管理窗口点击提升。为避免这个问题,建议在远端设备上安装本软件。"), ("Disconnected", "会话已结束"), ("Other", "其他"), ("Confirm before closing multiple tabs", "关闭多个标签页时向您确认"), diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 5c086bfb..98bb9c5d 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -371,9 +371,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Deny LAN Discovery", ""), ("Write a message", ""), ("Prompt", ""), - ("elevation_prompt", ""), - ("uac_warning", ""), - ("elevated_foreground_window_warning", ""), + ("Please wait for confirmation of UAC...", ""), + ("elevated_foreground_window_tip", ""), ("Disconnected", ""), ("Other", ""), ("Confirm before closing multiple tabs", ""), diff --git a/src/lang/da.rs b/src/lang/da.rs index 90670804..b98e5ba3 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -371,9 +371,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Deny LAN Discovery", "Afvis LAN Discovery"), ("Write a message", "Skriv en besked"), ("Prompt", ""), - ("elevation_prompt", ""), - ("uac_warning", ""), - ("elevated_foreground_window_warning", ""), + ("Please wait for confirmation of UAC...", ""), + ("elevated_foreground_window_tip", ""), ("Disconnected", "Afbrudt"), ("Other", ""), ("Confirm before closing multiple tabs", ""), diff --git a/src/lang/de.rs b/src/lang/de.rs index 6ebea6b2..1e808391 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -371,9 +371,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Deny LAN Discovery", "LAN-Erkennung verbieten"), ("Write a message", "Nachricht schreiben"), ("Prompt", ""), - ("elevation_prompt", ""), - ("uac_warning", ""), - ("elevated_foreground_window_warning", ""), + ("Please wait for confirmation of UAC...", ""), + ("elevated_foreground_window_tip", ""), ("Disconnected", ""), ("Other", ""), ("Confirm before closing multiple tabs", ""), diff --git a/src/lang/en.rs b/src/lang/en.rs index 3415fa46..ee68d443 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -30,9 +30,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("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]"), ("remote_restarting_tip", "Remote device is restarting, please close this message box and reconnect with permanent password after a while"), ("Are you sure to close the connection?", "Are you sure you want to close the connection?"), - ("elevation_prompt", "Running software without privilege elevation may cause problems when remote users operate certain windows."), - ("uac_warning", "Temporarily denied access due to elevation request, please wait for the remote user to accept the UAC dialog. To avoid this problem, it is recommended to install the software on the remote device or run it with administrator privileges."), - ("elevated_foreground_window_warning", "Temporarily unable to use the mouse and keyboard, because the current window of the remote desktop requires higher privilege to operate, you can request the remote user to minimize the current window. To avoid this problem, it is recommended to install the software on the remote device or run it with administrator privileges."), + ("elevated_foreground_window_tip", "The current window of the remote desktop requires higher privilege to operate, so it's unable to use the mouse and keyboard temporarily. You can request the remote user to minimize the current window, or click elevation button on the connection management window. To avoid this problem, it is recommended to install the software on the remote device."), ("JumpLink", "View"), ("Stop service", "Stop Service"), ("or", ""), diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 2dce72f6..93394a91 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -371,9 +371,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Deny LAN Discovery", ""), ("Write a message", ""), ("Prompt", ""), - ("elevation_prompt", ""), - ("uac_warning", ""), - ("elevated_foreground_window_warning", ""), + ("Please wait for confirmation of UAC...", ""), + ("elevated_foreground_window_tip", ""), ("Disconnected", ""), ("Other", ""), ("Confirm before closing multiple tabs", ""), diff --git a/src/lang/es.rs b/src/lang/es.rs index 2e70a519..a00009b7 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -371,9 +371,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Deny LAN Discovery", "Denegar descubrimiento de LAN"), ("Write a message", "Escribir un mensaje"), ("Prompt", ""), - ("elevation_prompt", ""), - ("uac_warning", ""), - ("elevated_foreground_window_warning", ""), + ("Please wait for confirmation of UAC...", ""), + ("elevated_foreground_window_tip", ""), ("Disconnected", "Desconectado"), ("Other", "Otro"), ("Confirm before closing multiple tabs", "Confirmar antes de cerrar múltiples pestañas"), diff --git a/src/lang/fa.rs b/src/lang/fa.rs index f9a15dc9..b6c3d002 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -371,9 +371,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Deny LAN Discovery", "غیر فعالسازی جستجو در شبکه"), ("Write a message", "یک پیام بنویسید"), ("Prompt", ""), - ("elevation_prompt", "اجرای نرم‌افزار بدون افزایش امتیاز می‌تواند باعث ایجاد مشکلاتی در هنگام کار کردن کاربران راه دور با ویندوزهای خاص شود"), - ("uac_warning", "به دلیل درخواست دسترسی سطح بالا، به طور موقت از دسترسی رد شد. منتظر بمانید تا کاربر راه دور گفتگوی UAC را بپذیرد. برای جلوگیری از این مشکل، توصیه می شود نرم افزار را روی دستگاه از راه دور نصب کنید یا آن را با دسترسی مدیر اجرا کنید."), - ("elevated_foreground_window_warning", "به طور موقت استفاده از ماوس و صفحه کلید امکان پذیر نیست زیرا پنجره دسکتاپ از راه دور فعلی برای کار کردن به دسترسی های بالاتر نیاز دارد، می توانید از کاربر راه دور بخواهید که پنجره فعلی را به حداقل برساند. برای جلوگیری از این مشکل، توصیه می شود نرم افزار را روی یک دستگاه راه دور نصب کنید یا آن را با دسترسی مدیر اجرا کنید"), + ("Please wait for confirmation of UAC...", ""), + ("elevated_foreground_window_tip", ""), ("Disconnected", "قطع ارتباط"), ("Other", "دیگر"), ("Confirm before closing multiple tabs", "بستن چندین برگه را تأیید کنید"), diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 811ec911..ff4e2e08 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -371,9 +371,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Deny LAN Discovery", "Interdir la découverte réseau local"), ("Write a message", "Ecrire un message"), ("Prompt", ""), - ("elevation_prompt", ""), - ("uac_warning", ""), - ("elevated_foreground_window_warning", ""), + ("Please wait for confirmation of UAC...", ""), + ("elevated_foreground_window_tip", ""), ("Disconnected", "Déconnecté"), ("Other", "Divers"), ("Confirm before closing multiple tabs", "Confirmer avant de fermer plusieurs onglets"), diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 863fa373..9bd5de21 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -371,9 +371,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Deny LAN Discovery", "Felfedezés tiltása"), ("Write a message", "Üzenet írása"), ("Prompt", ""), - ("elevation_prompt", "A szoftver jogosultságnövelés nélküli futtatása problémákat okozhat, ha távoli felhasználók bizonyos ablakokat működtetnek."), - ("uac_warning", "Kérjük, várja meg, amíg a távoli felhasználó elfogadja az UAC párbeszédpanelt. A probléma elkerülése érdekében javasoljuk, hogy telepítse a szoftvert a távoli eszközre, vagy futtassa rendszergazdai jogosultságokkal."), - ("elevated_foreground_window_warning", "Átmenetileg nem tudja használni az egeret és a billentyűzetet, mert a távoli asztal aktuális ablakának működéséhez magasabb jogosultság szükséges, ezért kérheti a távoli felhasználót, hogy minimalizálja az aktuális ablakot. A probléma elkerülése érdekében javasoljuk, hogy telepítse a szoftvert a távoli eszközre, vagy futtassa rendszergazdai jogosultságokkal."), + ("Please wait for confirmation of UAC...", ""), + ("elevated_foreground_window_tip", ""), ("Disconnected", "Szétkapcsolva"), ("Other", "Egyéb"), ("Confirm before closing multiple tabs", "Biztos, hogy bezárja az összes lapot?"), diff --git a/src/lang/id.rs b/src/lang/id.rs index 78fccc9f..7d4ae163 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -371,9 +371,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Deny LAN Discovery", "Tolak Penemuan LAN"), ("Write a message", "Menulis pesan"), ("Prompt", ""), - ("elevation_prompt", ""), - ("uac_warning", ""), - ("elevated_foreground_window_warning", ""), + ("Please wait for confirmation of UAC...", ""), + ("elevated_foreground_window_tip", ""), ("Disconnected", "Terputus"), ("Other", "Lainnya"), ("Confirm before closing multiple tabs", "Konfirmasi sebelum menutup banyak tab"), diff --git a/src/lang/it.rs b/src/lang/it.rs index 35c09a0b..3490a9b7 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -371,9 +371,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Deny LAN Discovery", ""), ("Write a message", ""), ("Prompt", ""), - ("elevation_prompt", ""), - ("uac_warning", ""), - ("elevated_foreground_window_warning", ""), + ("Please wait for confirmation of UAC...", ""), + ("elevated_foreground_window_tip", ""), ("Disconnected", ""), ("Other", ""), ("Confirm before closing multiple tabs", ""), diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 21344eb1..2953f80c 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -371,9 +371,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Deny LAN Discovery", ""), ("Write a message", ""), ("Prompt", ""), - ("elevation_prompt", ""), - ("uac_warning", ""), - ("elevated_foreground_window_warning", ""), + ("Please wait for confirmation of UAC...", ""), + ("elevated_foreground_window_tip", ""), ("Disconnected", ""), ("Other", "他の"), ("Confirm before closing multiple tabs", "同時に複数のタブを閉じる前に確認する"), diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 3b726902..2473ef2d 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -371,9 +371,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Deny LAN Discovery", ""), ("Write a message", ""), ("Prompt", ""), - ("elevation_prompt", ""), - ("uac_warning", ""), - ("elevated_foreground_window_warning", ""), + ("Please wait for confirmation of UAC...", ""), + ("elevated_foreground_window_tip", ""), ("Disconnected", ""), ("Other", ""), ("Confirm before closing multiple tabs", ""), diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 1dc50580..1ce728db 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -371,9 +371,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Deny LAN Discovery", ""), ("Write a message", ""), ("Prompt", ""), - ("elevation_prompt", ""), - ("uac_warning", ""), - ("elevated_foreground_window_warning", ""), + ("Please wait for confirmation of UAC...", ""), + ("elevated_foreground_window_tip", ""), ("Disconnected", ""), ("Other", ""), ("Confirm before closing multiple tabs", ""), diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 7ed98913..3bcc464c 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -371,9 +371,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Deny LAN Discovery", "Zablokuj Wykrywanie LAN"), ("Write a message", "Napisz wiadomość"), ("Prompt", "Monit"), - ("elevation_prompt", "Monit o podwyższeniu uprawnień"), - ("uac_warning", "Ostrzeżenie UAC"), - ("elevated_foreground_window_warning", "Pierwszoplanowe okno ostrzeżenia o podwyższeniu uprawnień"), + ("Please wait for confirmation of UAC...", ""), + ("elevated_foreground_window_tip", ""), ("Disconnected", "Rozłączone"), ("Other", "Inne"), ("Confirm before closing multiple tabs", "Potwierdź przed zamknięciem wielu kart"), diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 8b4c980f..e7bb0e73 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -371,9 +371,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Deny LAN Discovery", ""), ("Write a message", ""), ("Prompt", ""), - ("elevation_prompt", ""), - ("uac_warning", ""), - ("elevated_foreground_window_warning", ""), + ("Please wait for confirmation of UAC...", ""), + ("elevated_foreground_window_tip", ""), ("Disconnected", "Desconectado"), ("Other", "Outro"), ("Confirm before closing multiple tabs", "Confirme antes de fechar vários separadores"), diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 0d5594ea..bc35cfcb 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -371,9 +371,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Deny LAN Discovery", "Negar descoberta da LAN"), ("Write a message", "Escrever uma mensagem"), ("Prompt", "Prompt de comando"), - ("elevation_prompt", "Prompt de comando (Admin)"), - ("uac_warning", "Aviso UAC"), - ("elevated_foreground_window_warning", "Aviso de janela de primeiro plano elevado"), + ("Please wait for confirmation of UAC...", ""), + ("elevated_foreground_window_tip", ""), ("Disconnected", "Desconectado"), ("Other", "Outro"), ("Confirm before closing multiple tabs", "Confirmar antes de fechar múltiplas abas"), diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 22c246f8..9e9a6082 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -371,9 +371,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Deny LAN Discovery", "Запретить обнаружение в локальной сети"), ("Write a message", "Написать сообщение"), ("Prompt", "Подсказка"), - ("elevation_prompt", "Запуск программного обеспечения без повышения привилегий может вызвать проблемы, когда удалённые пользователи работают с определёнными окнами."), - ("uac_warning", "Временно отказано в доступе из-за запроса на повышение прав. Подождите, пока удалённый пользователь примет диалоговое окно UAC. Чтобы избежать этой проблемы, рекомендуется устанавливать программное обеспечение на удалённое устройство или запускать его с правами администратора."), - ("elevated_foreground_window_warning", "Временно невозможно использовать мышь и клавиатуру, поскольку текущее окно удалённого рабочего стола требует более высоких привилегий для работы, вы можете попросить удалённого пользователя свернуть текущее окно. Чтобы избежать этой проблемы, рекомендуется устанавливать программное обеспечение на удалённое устройство или запускать его с правами администратора."), + ("Please wait for confirmation of UAC...", ""), + ("elevated_foreground_window_tip", ""), ("Disconnected", "Отключено"), ("Other", "Другое"), ("Confirm before closing multiple tabs", "Подтверждение закрытия несколько вкладок"), diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 618ede5c..3970ef3b 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -371,9 +371,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Deny LAN Discovery", ""), ("Write a message", ""), ("Prompt", ""), - ("elevation_prompt", ""), - ("uac_warning", ""), - ("elevated_foreground_window_warning", ""), + ("Please wait for confirmation of UAC...", ""), + ("elevated_foreground_window_tip", ""), ("Disconnected", ""), ("Other", ""), ("Confirm before closing multiple tabs", ""), diff --git a/src/lang/template.rs b/src/lang/template.rs index 4ff8f9b8..d9fd7b9b 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -371,9 +371,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Deny LAN Discovery", ""), ("Write a message", ""), ("Prompt", ""), - ("elevation_prompt", ""), - ("uac_warning", ""), - ("elevated_foreground_window_warning", ""), + ("Please wait for confirmation of UAC...", ""), + ("elevated_foreground_window_tip", ""), ("Disconnected", ""), ("Other", ""), ("Confirm before closing multiple tabs", ""), diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 6fb89a09..cd2c8b26 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -371,9 +371,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Deny LAN Discovery", ""), ("Write a message", ""), ("Prompt", ""), - ("elevation_prompt", ""), - ("uac_warning", ""), - ("elevated_foreground_window_warning", ""), + ("Please wait for confirmation of UAC...", ""), + ("elevated_foreground_window_tip", ""), ("Disconnected", ""), ("Other", ""), ("Confirm before closing multiple tabs", ""), diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 1150199c..da57cf07 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -371,9 +371,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Deny LAN Discovery", "拒絕局域網發現"), ("Write a message", "輸入聊天消息"), ("Prompt", "提示"), - ("elevation_prompt", "以當前用戶權限運行軟件,可能導致遠端在訪問本機時,沒有足夠的權限來操作部分窗口。"), - ("uac_warning", "暂时无法访问远端设备,因为远端设备正在请求用户账户权限,请等待对方关闭UAC窗口。为避免这个问题,建议在远端设备上安装或者以管理员权限运行本软件。"), - ("elevated_foreground_window_warning", "暫時無法使用鼠標鍵盤,因為遠端桌面的當前窗口需要更高的權限才能操作, 可以請求對方最小化當前窗口。為避免這個問題,建議在遠端設備上安裝或者以管理員權限運行本軟件。"), + ("Please wait for confirmation of UAC...", "請等待對方確認UAC"), + ("elevated_foreground_window_tip", "遠端桌面的當前窗口需要更高的權限才能操作, 暫時無法使用鼠標鍵盤, 可以請求對方最小化當前窗口, 或者在連接管理窗口點擊提升。為避免這個問題,建議在遠端設備上安裝本軟件。"), ("Disconnected", "會話已結束"), ("Other", "其他"), ("Confirm before closing multiple tabs", "關閉多個分頁前跟我確認"), diff --git a/src/lang/ua.rs b/src/lang/ua.rs index cc0ef653..10119d1e 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -371,9 +371,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Deny LAN Discovery", "Заборонити виявлення локальної мережі"), ("Write a message", "Написати повідомлення"), ("Prompt", ""), - ("elevation_prompt", ""), - ("uac_warning", ""), - ("elevated_foreground_window_warning", ""), + ("Please wait for confirmation of UAC...", ""), + ("elevated_foreground_window_tip", ""), ("Disconnected", ""), ("Other", ""), ("Confirm before closing multiple tabs", ""), diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 9773d765..8bb164a8 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -371,9 +371,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Deny LAN Discovery", ""), ("Write a message", ""), ("Prompt", ""), - ("elevation_prompt", ""), - ("uac_warning", ""), - ("elevated_foreground_window_warning", ""), + ("Please wait for confirmation of UAC...", ""), + ("elevated_foreground_window_tip", ""), ("Disconnected", ""), ("Other", ""), ("Confirm before closing multiple tabs", ""), diff --git a/src/server/connection.rs b/src/server/connection.rs index 8674c6d9..cb2ddc2c 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -26,12 +26,9 @@ use hbb_common::{ use scrap::android::call_main_service_mouse_input; use serde_json::{json, value::Value}; use sha2::{Digest, Sha256}; -use std::sync::{ - atomic::AtomicI64, - mpsc as std_mpsc, -}; #[cfg(not(any(target_os = "android", target_os = "ios")))] use std::sync::atomic::Ordering; +use std::sync::{atomic::AtomicI64, mpsc as std_mpsc}; #[cfg(not(any(target_os = "android", target_os = "ios")))] use system_shutdown; @@ -434,7 +431,7 @@ impl Connection { let uac = crate::video_service::IS_UAC_RUNNING.lock().unwrap().clone(); if last_uac != uac { last_uac = uac; - if !portable_service_running { + if !uac || !portable_service_running{ let mut misc = Misc::new(); misc.set_uac(uac); let mut msg = Message::new(); @@ -445,7 +442,7 @@ impl Connection { let foreground_window_elevated = crate::video_service::IS_FOREGROUND_WINDOW_ELEVATED.lock().unwrap().clone(); if last_foreground_window_elevated != foreground_window_elevated { last_foreground_window_elevated = foreground_window_elevated; - if !portable_service_running { + if !foreground_window_elevated || !portable_service_running { let mut misc = Misc::new(); misc.set_foreground_window_elevated(foreground_window_elevated); let mut msg = Message::new(); diff --git a/src/ui/common.tis b/src/ui/common.tis index 76e0fb84..7507d489 100644 --- a/src/ui/common.tis +++ b/src/ui/common.tis @@ -232,6 +232,7 @@ class ChatBox: Reactor.Component { /******************** start of msgbox ****************************************/ var remember_password = false; +var last_msgbox_tag = ""; function msgbox(type, title, content, link="", callback=null, height=180, width=500, hasRetry=false, contentStyle="") { $(body).scrollTo(0, 0); if (!type) { @@ -264,6 +265,7 @@ function msgbox(type, title, content, link="", callback=null, height=180, width= } else if (type.indexOf("custom") < 0 && !is_port_forward && !callback) { callback = function() { view.close(); } } + last_msgbox_tag = type + "-" + title + "-" + content + "-" + link; $(#msgbox).content(); } @@ -276,6 +278,12 @@ handler.msgbox = function(type, title, text, link = "", hasRetry=false) { self.timer(60ms, function() { msgbox(type, title, text, link, null, 180, 500, hasRetry); }); } +handler.cancel_msgbox = function(tag) { + if (last_msgbox_tag == tag) { + closeMsgbox(); + } +} + var reconnectTimeout = 1000; handler.msgbox_retry = function(type, title, text, link, hasRetry) { handler.msgbox(type, title, text, link, hasRetry); diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 62df8525..66b46cf8 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -231,7 +231,14 @@ impl InvokeUiSession for SciterHandler { } fn msgbox(&self, msgtype: &str, title: &str, text: &str, link: &str, retry: bool) { - self.call2("msgbox_retry", &make_args!(msgtype, title, text, link, retry)); + self.call2( + "msgbox_retry", + &make_args!(msgtype, title, text, link, retry), + ); + } + + fn cancel_msgbox(&self, tag: &str) { + self.call("cancel_msgbox", &make_args!(tag)); } fn new_message(&self, msg: String) { diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 4467fee0..5119a6e1 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -1,11 +1,11 @@ -#[cfg(not(any(target_os = "android", target_os = "ios")))] -use crate::client::{get_key_state, SERVER_KEYBOARD_ENABLED}; use crate::client::io_loop::Remote; use crate::client::{ check_if_retry, handle_hash, handle_login_from_ui, handle_test_delay, input_os_password, load_config, send_mouse, start_video_audio_threads, FileManager, Key, LoginConfigHandler, QualityStatus, KEY_MAP, }; +#[cfg(not(any(target_os = "android", target_os = "ios")))] +use crate::client::{get_key_state, SERVER_KEYBOARD_ENABLED}; #[cfg(target_os = "linux")] use crate::common::IS_X11; use crate::{client::Data, client::Interface}; @@ -15,9 +15,9 @@ use hbb_common::rendezvous_proto::ConnType; use hbb_common::tokio::{self, sync::mpsc}; use hbb_common::{allow_err, message_proto::*}; use hbb_common::{fs, get_version_number, log, Stream}; +use rdev::{Event, EventType, EventType::*, Key as RdevKey}; #[cfg(not(any(target_os = "android", target_os = "ios")))] use rdev::{Keyboard as RdevKeyboard, KeyboardState}; -use rdev::{Event, EventType, EventType::*, Key as RdevKey}; use std::collections::{HashMap, HashSet}; use std::ops::{Deref, DerefMut}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; @@ -1120,6 +1120,7 @@ pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default { fn msgbox(&self, msgtype: &str, title: &str, text: &str, link: &str, retry: bool); #[cfg(any(target_os = "android", target_os = "ios"))] fn clipboard(&self, content: String); + fn cancel_msgbox(&self, tag: &str); } impl Deref for Session { From 2a65d948aabbd9ac807ecfac9be999a8afc2aadc Mon Sep 17 00:00:00 2001 From: 21pages Date: Wed, 16 Nov 2022 13:15:05 +0800 Subject: [PATCH 11/11] portable-service: little fix Signed-off-by: 21pages --- src/lang/pl.rs | 2 +- src/server/input_service.rs | 14 +++++++------- src/server/portable_service.rs | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 0e2616a8..ffe94432 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -387,7 +387,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Show RustDesk", "Pokaż RustDesk"), ("This PC", "Ten komputer"), ("or", "albo"), - ("Continue with", "Kontynuuj z"),""), + ("Continue with", "Kontynuuj z"), ("Elevate", ""), ].iter().cloned().collect(); } diff --git a/src/server/input_service.rs b/src/server/input_service.rs index af4b7d85..af9441ae 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -280,13 +280,6 @@ fn get_modifier_state(key: Key, en: &mut Enigo) -> bool { } pub fn handle_mouse(evt: &MouseEvent, conn: i32) { - #[cfg(target_os = "macos")] - if !*IS_SERVER { - // having GUI, run main GUI thread, otherwise crash - let evt = evt.clone(); - QUEUE.exec_async(move || handle_mouse_(&evt, conn)); - return; - } if !active_mouse_(conn) { return; } @@ -300,6 +293,13 @@ pub fn handle_mouse(evt: &MouseEvent, conn: i32) { y: evt.y, }; } + #[cfg(target_os = "macos")] + if !*IS_SERVER { + // having GUI, run main GUI thread, otherwise crash + let evt = evt.clone(); + QUEUE.exec_async(move || handle_mouse_(&evt)); + return; + } #[cfg(windows)] crate::portable_service::client::handle_mouse(evt); #[cfg(not(windows))] diff --git a/src/server/portable_service.rs b/src/server/portable_service.rs index 48cdcbe5..21b501f1 100644 --- a/src/server/portable_service.rs +++ b/src/server/portable_service.rs @@ -16,7 +16,7 @@ use std::{ mem::size_of, ops::{Deref, DerefMut}, sync::{Arc, Mutex}, - time::{Duration, Instant}, + time::Duration, }; use winapi::{ shared::minwindef::{BOOL, FALSE, TRUE},