diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index e2a3c6f0b..827fd3b3f 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -31,6 +31,7 @@ const String kWindowEventShow = "show"; const String kWindowConnect = "connect"; const String kUniLinksPrefix = "rustdesk://"; +const String kUrlActionClose = "close"; const String kTabLabelHomePage = "Home"; const String kTabLabelSettingPage = "Settings"; diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 4fe89d3dc..5348090f4 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -20,11 +20,13 @@ import 'package:flutter_hbb/plugin/event.dart'; import 'package:flutter_hbb/plugin/desc.dart'; import 'package:flutter_hbb/plugin/widget.dart'; import 'package:flutter_hbb/common/shared_state.dart'; +import 'package:flutter_hbb/utils/multi_window_manager.dart'; import 'package:tuple/tuple.dart'; import 'package:image/image.dart' as img2; import 'package:flutter_custom_cursor/cursor_manager.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; +import 'package:window_manager/window_manager.dart'; import '../common.dart'; import '../utils/image.dart' as img; @@ -210,13 +212,7 @@ class FfiModel with ChangeNotifier { } else if (name == 'portable_service_running') { parent.target?.elevationModel.onPortableServiceRunning(evt); } else if (name == 'on_url_scheme_received') { - final url = evt['url'].toString(); - // If we invoke uri with blank path, we just bring the main window to the top. - if (url.isEmpty) { - window_on_top(null); - } else { - parseRustdeskUri(url); - } + onUrlSchemeReceived(evt); } else if (name == 'on_voice_call_waiting') { // Waiting for the response from the peer. parent.target?.chatModel.onVoiceCallWaiting(); @@ -249,6 +245,30 @@ class FfiModel with ChangeNotifier { }; } + onUrlSchemeReceived(Map evt) { + final url = evt['url'].toString().trim(); + // If we invoke uri with blank path, we just bring the main window to the top. + if (url.isEmpty) { + window_on_top(null); + } else if (url.startsWith(kUniLinksPrefix)) { + parseRustdeskUri(url); + } else { + // action + switch (url) { + case kUrlActionClose: + debugPrint("closing all instances"); + Future.microtask(() async { + await rustDeskWinManager.closeAllSubWindows(); + windowManager.close(); + }); + break; + default: + debugPrint("Unknown url received: $url"); + break; + } + } + } + /// Bind the event listener to receive events from the Rust core. updateEventListener(String peerId) { platformFFI.setEventCallback(startEventListener(peerId)); diff --git a/flutter/lib/utils/multi_window_manager.dart b/flutter/lib/utils/multi_window_manager.dart index 1aec57680..ccd976fa6 100644 --- a/flutter/lib/utils/multi_window_manager.dart +++ b/flutter/lib/utils/multi_window_manager.dart @@ -218,6 +218,8 @@ class RustDeskMultiWindowManager { } await WindowController.fromWindowId(wId).setPreventClose(false); await WindowController.fromWindowId(wId).close(); + // unregister the sub window in the main window. + unregisterActiveWindow(wId); } catch (e) { debugPrint("$e"); return; diff --git a/src/ipc.rs b/src/ipc.rs index a2194c337..34567b77a 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -36,6 +36,8 @@ pub enum PrivacyModeState { OffByPeer, OffUnknown, } +// IPC actions here. +pub const IPC_ACTION_CLOSE: &str = "close"; #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(tag = "t", content = "c")] @@ -871,6 +873,14 @@ pub async fn send_url_scheme(url: String) -> ResultType<()> { Ok(()) } +// Emit `close` events to ipc. +pub fn close_all_instances() -> ResultType { + match crate::ipc::send_url_scheme(IPC_ACTION_CLOSE.to_owned()) { + Ok(_) => Ok(true), + Err(err) => Err(err), + } +} + #[cfg(test)] mod test { use super::*; diff --git a/src/platform/macos.rs b/src/platform/macos.rs index 251211c29..954d858bd 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -211,6 +211,7 @@ pub fn uninstall(show_new_window: bool) -> bool { ); if uninstalled { crate::ipc::set_option("stop-service", "Y"); + let _ = crate::ipc::close_all_instances(); // leave ipc a little time std::thread::sleep(std::time::Duration::from_millis(300)); std::process::Command::new("launchctl")