diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index dbf563de2..8edd3a356 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -268,10 +268,12 @@ hideCmWindow({bool isStartup = false}) async { await windowManager.hide(); }); } else { - await windowManager.setOpacity(0); - bind.mainHideDocker(); - await windowManager.minimize(); - await windowManager.hide(); + if (await windowManager.getOpacity() != 0) { + await windowManager.setOpacity(0); + bind.mainHideDocker(); + await windowManager.minimize(); + await windowManager.hide(); + } } } diff --git a/flutter/lib/models/server_model.dart b/flutter/lib/models/server_model.dart index 8f56ffbe2..716c535ad 100644 --- a/flutter/lib/models/server_model.dart +++ b/flutter/lib/models/server_model.dart @@ -36,6 +36,7 @@ class ServerModel with ChangeNotifier { String _verificationMethod = ""; String _temporaryPasswordLength = ""; String _approveMode = ""; + int _zeroClientLengthCounter = 0; late String _emptyIdShow; late final IDTextEditingController _serverId; @@ -120,6 +121,17 @@ class ServerModel with ChangeNotifier { _emptyIdShow = translate("Generating ..."); _serverId = IDTextEditingController(text: _emptyIdShow); + // initital _hideCm at startup + final verificationMethod = + bind.mainGetOptionSync(key: "verification-method"); + final approveMode = bind.mainGetOptionSync(key: 'approve-mode'); + _hideCm = option2bool( + 'allow-hide-cm', bind.mainGetOptionSync(key: 'allow-hide-cm')); + if (!(approveMode == 'password' && + verificationMethod == kUsePermanentPassword)) { + _hideCm = false; + } + timerCallback() async { final connectionStatus = jsonDecode(await bind.mainGetConnectStatus()) as Map; @@ -134,6 +146,17 @@ class ServerModel with ChangeNotifier { if (res != null) { debugPrint("clients not match!"); updateClientState(res); + } else { + if (_clients.isEmpty) { + hideCmWindow(); + if (_zeroClientLengthCounter++ == 12) { + // 6 second + windowManager.close(); + } + } else { + _zeroClientLengthCounter = 0; + if (!_hideCm) showCmWindow(); + } } } @@ -422,6 +445,7 @@ class ServerModel with ChangeNotifier { return; } + final oldClientLenght = _clients.length; _clients.clear(); tabController.state.value.tabs.clear(); @@ -434,6 +458,16 @@ class ServerModel with ChangeNotifier { debugPrint("Failed to decode clientJson '$clientJson', error $e"); } } + if (desktopType == DesktopType.cm) { + if (_clients.isEmpty) { + hideCmWindow(); + } else if (!_hideCm) { + showCmWindow(); + } + } + if (_clients.length != oldClientLenght) { + notifyListeners(); + } } void addConnection(Map evt) { @@ -461,6 +495,9 @@ class ServerModel with ChangeNotifier { _clients.removeAt(index_disconnected); tabController.remove(index_disconnected); } + if (desktopType == DesktopType.cm && !_hideCm) { + showCmWindow(); + } scrollToBottom(); notifyListeners(); if (isAndroid && !client.authorized) showLoginDialog(client); @@ -581,6 +618,9 @@ class ServerModel with ChangeNotifier { parent.target?.dialogManager.dismissByTag(getLoginDialogTag(id)); parent.target?.invokeMethod("cancel_notification", id); } + if (desktopType == DesktopType.cm && _clients.isEmpty) { + hideCmWindow(); + } notifyListeners(); } catch (e) { debugPrint("onClientRemove failed,error:$e"); diff --git a/src/server/connection.rs b/src/server/connection.rs index 7d1768f4c..22459096f 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -135,6 +135,14 @@ struct Session { random_password: String, } +#[cfg(not(any(target_os = "android", target_os = "ios")))] +struct StartCmIpcPara { + rx_to_cm: mpsc::UnboundedReceiver, + tx_from_cm: mpsc::UnboundedSender, + rx_desktop_ready: mpsc::Receiver<()>, + tx_cm_stream_ready: mpsc::Sender<()>, +} + pub struct Connection { inner: ConnInner, stream: super::Stream, @@ -193,6 +201,8 @@ pub struct Connection { linux_headless_handle: LinuxHeadlessHandle, closed: bool, delay_response_instant: Instant, + #[cfg(not(any(target_os = "android", target_os = "ios")))] + start_cm_ipc_para: Option, } impl ConnInner { @@ -324,6 +334,13 @@ impl Connection { linux_headless_handle, closed: false, delay_response_instant: Instant::now(), + #[cfg(not(any(target_os = "android", target_os = "ios")))] + start_cm_ipc_para: Some(StartCmIpcPara { + rx_to_cm, + tx_from_cm, + rx_desktop_ready, + tx_cm_stream_ready, + }), }; let addr = hbb_common::try_into_v4(addr); if !conn.on_open(addr).await { @@ -332,14 +349,6 @@ impl Connection { sleep(1.).await; return; } - #[cfg(not(any(target_os = "android", target_os = "ios")))] - tokio::spawn(async move { - if let Err(err) = - start_ipc(rx_to_cm, tx_from_cm, rx_desktop_ready, tx_cm_stream_ready).await - { - log::error!("ipc to connection manager exit: {}", err); - } - }); #[cfg(target_os = "android")] start_channel(rx_to_cm, tx_from_cm); if !conn.keyboard { @@ -1316,6 +1325,24 @@ impl Connection { self.video_ack_required = lr.video_ack_required; } + #[cfg(not(any(target_os = "android", target_os = "ios")))] + fn try_start_cm_ipc(&mut self) { + if let Some(p) = self.start_cm_ipc_para.take() { + tokio::spawn(async move { + if let Err(err) = start_ipc( + p.rx_to_cm, + p.tx_from_cm, + p.rx_desktop_ready, + p.tx_cm_stream_ready, + ) + .await + { + log::error!("ipc to connection manager exit: {}", err); + } + }); + } + } + async fn on_message(&mut self, msg: Message) -> bool { if let Some(message::Union::LoginRequest(lr)) = msg.union { self.handle_login_request_without_validation(&lr).await; @@ -1379,6 +1406,9 @@ impl Connection { } } + #[cfg(not(any(target_os = "android", target_os = "ios")))] + self.try_start_cm_ipc(); + #[cfg(any( feature = "flatpak", feature = "appimage", @@ -1543,6 +1573,8 @@ impl Connection { self.from_switch = true; self.try_start_cm(lr.my_id.clone(), lr.my_name.clone(), true); self.send_logon_response().await; + #[cfg(not(any(target_os = "android", target_os = "ios")))] + self.try_start_cm_ipc(); } } }