diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index e370fa9e4..ff69302f2 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -322,10 +322,12 @@ void window_on_top(int? id) { windowManager.restore(); windowManager.show(); windowManager.focus(); + rustDeskWinManager.registerActiveWindow(0); } else { WindowController.fromWindowId(id) ..focus() ..show(); + rustDeskWinManager.call(WindowType.Main, kWindowEventShow, {"id": id}); } } @@ -1360,3 +1362,34 @@ Future reloadAllWindows() async { // ignore } } + +/// Indicate the flutter app is running in portable mode. +/// +/// [Note] +/// Portable build is only avaliable on Windows. +bool isRunningInPortableMode() { + if (!Platform.isWindows) { + return false; + } + return bool.hasEnvironment(kEnvPortableExecutable); +} + +/// Window status callback +void onActiveWindowChanged() async { + print( + "[MultiWindowHandler] active window changed: ${rustDeskWinManager.getActiveWindows()}"); + if (rustDeskWinManager.getActiveWindows().isEmpty) { + // close all sub windows + try { + await Future.wait([ + saveWindowPosition(WindowType.Main), + rustDeskWinManager.closeAllSubWindows() + ]); + } catch (err) { + debugPrint("$err"); + } finally { + await windowManager.setPreventClose(false); + await windowManager.close(); + } + } +} diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index c13a87d41..ea1142033 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -10,6 +10,8 @@ const String kAppTypeDesktopFileTransfer = "file transfer"; const String kAppTypeDesktopPortForward = "port forward"; const String kWindowActionRebuild = "rebuild"; +const String kWindowEventHide = "hide"; +const String kWindowEventShow = "show"; const String kUniLinksPrefix = "rustdesk://"; const String kActionNewConnection = "connection/new/"; @@ -19,6 +21,9 @@ const String kTabLabelSettingPage = "Settings"; const String kWindowPrefix = "wm_"; +// the executable name of the portable version +const String kEnvPortableExecutable = "RUSTDESK_APPNAME"; + const Color kColorWarn = Color.fromARGB(255, 245, 133, 59); const int kMobileDefaultDisplayWidth = 720; diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart index 5e9f6b935..88f000e6e 100644 --- a/flutter/lib/desktop/pages/desktop_home_page.dart +++ b/flutter/lib/desktop/pages/desktop_home_page.dart @@ -44,20 +44,9 @@ class _DesktopHomePageState extends State @override void onWindowClose() async { super.onWindowClose(); - // close all sub windows - if (await windowManager.isPreventClose()) { - try { - await Future.wait([ - saveWindowPosition(WindowType.Main), - rustDeskWinManager.closeAllSubWindows() - ]); - } catch (err) { - debugPrint("$err"); - } finally { - await windowManager.setPreventClose(false); - await windowManager.close(); - } - } + // hide window on close + await windowManager.hide(); + rustDeskWinManager.unregisterActiveWindow(0); } @override @@ -437,9 +426,11 @@ class _DesktopHomePageState extends State // initTray(); trayManager.addListener(this); windowManager.addListener(this); + rustDeskWinManager.registerActiveWindowListener(onActiveWindowChanged); + rustDeskWinManager.registerActiveWindow(0); rustDeskWinManager.setMethodHandler((call, fromWindowId) async { debugPrint( - "call ${call.method} with args ${call.arguments} from window $fromWindowId"); + "[Main] call ${call.method} with args ${call.arguments} from window $fromWindowId"); if (call.method == "main_window_on_top") { window_on_top(null); } else if (call.method == "get_window_info") { @@ -465,6 +456,10 @@ class _DesktopHomePageState extends State } } else if (call.method == kWindowActionRebuild) { reloadCurrentWindow(); + } else if (call.method == kWindowEventShow) { + rustDeskWinManager.registerActiveWindow(call.arguments["id"]); + } else if (call.method == kWindowEventHide) { + rustDeskWinManager.unregisterActiveWindow(call.arguments["id"]); } }); Future.delayed(Duration.zero, () { @@ -475,7 +470,8 @@ class _DesktopHomePageState extends State @override void dispose() { - destoryTray(); + // destoryTray(); + rustDeskWinManager.unregisterActiveWindowListener(onActiveWindowChanged); trayManager.removeListener(this); windowManager.removeListener(this); _uniLinksSubscription?.cancel(); diff --git a/flutter/lib/desktop/pages/file_manager_tab_page.dart b/flutter/lib/desktop/pages/file_manager_tab_page.dart index 66a14bd37..44440d4b1 100644 --- a/flutter/lib/desktop/pages/file_manager_tab_page.dart +++ b/flutter/lib/desktop/pages/file_manager_tab_page.dart @@ -97,6 +97,7 @@ class _FileManagerTabPageState extends State { void onRemoveId(String id) { if (tabController.state.value.tabs.isEmpty) { WindowController.fromWindowId(windowId()).hide(); + rustDeskWinManager.call(WindowType.Main, kWindowEventHide, {"id": windowId()}); } } diff --git a/flutter/lib/desktop/pages/port_forward_tab_page.dart b/flutter/lib/desktop/pages/port_forward_tab_page.dart index c0eea86e6..94f652a76 100644 --- a/flutter/lib/desktop/pages/port_forward_tab_page.dart +++ b/flutter/lib/desktop/pages/port_forward_tab_page.dart @@ -51,7 +51,7 @@ class _PortForwardTabPageState extends State { rustDeskWinManager.setMethodHandler((call, fromWindowId) async { debugPrint( - "call ${call.method} with args ${call.arguments} from window $fromWindowId"); + "[Port Forward] call ${call.method} with args ${call.arguments} from window $fromWindowId"); // for simplify, just replace connectionId if (call.method == "new_port_forward") { final args = jsonDecode(call.arguments); @@ -108,6 +108,7 @@ class _PortForwardTabPageState extends State { void onRemoveId(String id) { if (tabController.state.value.tabs.isEmpty) { WindowController.fromWindowId(windowId()).hide(); + rustDeskWinManager.call(WindowType.Main, kWindowEventHide, {"id": windowId()}); } } diff --git a/flutter/lib/desktop/pages/remote_tab_page.dart b/flutter/lib/desktop/pages/remote_tab_page.dart index 9c9d56916..94c54c7b5 100644 --- a/flutter/lib/desktop/pages/remote_tab_page.dart +++ b/flutter/lib/desktop/pages/remote_tab_page.dart @@ -73,7 +73,7 @@ class _ConnectionTabPageState extends State { rustDeskWinManager.setMethodHandler((call, fromWindowId) async { print( - "call ${call.method} with args ${call.arguments} from window $fromWindowId"); + "[Remote Page] call ${call.method} with args ${call.arguments} from window $fromWindowId"); // for simplify, just replace connectionId if (call.method == "new_remote_desktop") { @@ -324,6 +324,7 @@ class _ConnectionTabPageState extends State { void onRemoveId(String id) { if (tabController.state.value.tabs.isEmpty) { WindowController.fromWindowId(windowId()).hide(); + rustDeskWinManager.call(WindowType.Main, kWindowEventHide, {"id": windowId()}); } ConnectionTypeState.delete(id); _update_remote_count(); diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index 8b7ee4d0e..ba61d58d7 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -541,8 +541,11 @@ class WindowActionPanelState extends State Future.delayed(Duration.zero, () async { if (widget.isMainWindow) { await windowManager.hide(); + rustDeskWinManager.unregisterActiveWindow(0); } else { await WindowController.fromWindowId(windowId!).hide(); + rustDeskWinManager.call( + WindowType.Main, kWindowEventHide, {"id": windowId!}); } }); } diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index afc98a1e1..0d45702b9 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:io'; import 'package:desktop_multi_window/desktop_multi_window.dart'; import 'package:flutter/material.dart'; @@ -139,11 +140,30 @@ void runMultiWindow( String title, ) async { await initEnv(appType); + late Widget widget; + switch (appType) { + case kAppTypeDesktopRemote: + widget = DesktopRemoteScreen( + params: argument, + ); + break; + case kAppTypeDesktopFileTransfer: + widget = DesktopFileTransferScreen( + params: argument, + ); + break; + case kAppTypeDesktopPortForward: + widget = DesktopPortForwardScreen( + params: argument, + ); + break; + default: + // no such appType + exit(0); + } _runApp( title, - DesktopRemoteScreen( - params: argument, - ), + widget, MyTheme.currentThemeMode(), ); } diff --git a/flutter/lib/utils/multi_window_manager.dart b/flutter/lib/utils/multi_window_manager.dart index a88f19a57..c268912db 100644 --- a/flutter/lib/utils/multi_window_manager.dart +++ b/flutter/lib/utils/multi_window_manager.dart @@ -33,6 +33,8 @@ class RustDeskMultiWindowManager { static final instance = RustDeskMultiWindowManager._(); + final List _activeWindows = List.empty(growable: true); + final List _windowActiveCallbacks = List.empty(growable: true); int? _remoteDesktopWindowId; int? _fileTransferWindowId; int? _portForwardWindowId; @@ -57,6 +59,7 @@ class RustDeskMultiWindowManager { ..center() ..setTitle("rustdesk - remote desktop") ..show(); + registerActiveWindow(remoteDesktopController.windowId); _remoteDesktopWindowId = remoteDesktopController.windowId; } else { return call(WindowType.RemoteDesktop, "new_remote_desktop", msg); @@ -82,6 +85,7 @@ class RustDeskMultiWindowManager { ..center() ..setTitle("rustdesk - file transfer") ..show(); + registerActiveWindow(fileTransferController.windowId); _fileTransferWindowId = fileTransferController.windowId; } else { return call(WindowType.FileTransfer, "new_file_transfer", msg); @@ -107,6 +111,7 @@ class RustDeskMultiWindowManager { ..center() ..setTitle("rustdesk - port forward") ..show(); + registerActiveWindow(portForwardController.windowId); _portForwardWindowId = portForwardController.windowId; } else { return call(WindowType.PortForward, "new_port_forward", msg); @@ -167,6 +172,56 @@ class RustDeskMultiWindowManager { } } } + + Future> getAllSubWindowIds() async { + try { + final windows = await DesktopMultiWindow.getAllSubWindowIds(); + return windows; + } catch (err) { + if (err is AssertionError) { + return []; + } else { + rethrow; + } + } + } + + List getActiveWindows() { + return _activeWindows; + } + + void _notifyActiveWindow() { + for (final callback in _windowActiveCallbacks) { + callback.call(); + } + } + + void registerActiveWindow(int windowId) { + if (_activeWindows.contains(windowId)) { + // ignore + } else { + _activeWindows.add(windowId); + _notifyActiveWindow(); + } + + } + + void unregisterActiveWindow(int windowId) { + if (!_activeWindows.contains(windowId)) { + // ignore + } else { + _activeWindows.remove(windowId); + _notifyActiveWindow(); + } + } + + void registerActiveWindowListener(VoidCallback callback) { + _windowActiveCallbacks.add(callback); + } + + void unregisterActiveWindowListener(VoidCallback callback) { + _windowActiveCallbacks.remove(callback); + } } final rustDeskWinManager = RustDeskMultiWindowManager.instance;