From f495bf105f5b4c1e8966dd3f028fd555dbaae2e6 Mon Sep 17 00:00:00 2001 From: dignow Date: Tue, 1 Aug 2023 22:19:38 +0800 Subject: [PATCH 01/16] refact, separate remote window Signed-off-by: dignow --- flutter/lib/common.dart | 8 +- flutter/lib/consts.dart | 4 + .../lib/desktop/pages/desktop_home_page.dart | 8 +- .../desktop/pages/desktop_setting_page.dart | 4 +- flutter/lib/desktop/pages/remote_page.dart | 36 +-- .../lib/desktop/widgets/tabbar_widget.dart | 3 + .../lib/models/desktop_render_texture.dart | 46 +++ flutter/lib/utils/multi_window_manager.dart | 274 ++++++++++-------- src/flutter.rs | 11 +- src/flutter_ffi.rs | 15 +- src/ui.rs | 69 +---- src/ui_interface.rs | 52 +++- 12 files changed, 297 insertions(+), 233 deletions(-) create mode 100644 flutter/lib/models/desktop_render_texture.dart diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index b467f8484..522f93c6f 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -19,7 +19,6 @@ import 'package:flutter_hbb/utils/multi_window_manager.dart'; import 'package:flutter_hbb/utils/platform_channel.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; -import 'package:texture_rgba_renderer/texture_rgba_renderer.dart'; import 'package:uni_links/uni_links.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:uuid/uuid.dart'; @@ -47,11 +46,6 @@ var isMobile = isAndroid || isIOS; var version = ""; int androidVersion = 0; -/// Incriment count for textureId. -int _textureId = 0; -int get newTextureId => _textureId++; -final textureRenderer = TextureRgbaRenderer(); - /// only available for Windows target int windowsBuildNumber = 0; DesktopType? desktopType; @@ -1698,7 +1692,7 @@ bool handleUriLink({List? cmdArgs, Uri? uri, String? uriString}) { Future.delayed(Duration.zero, () { rustDeskWinManager.newRemoteDesktop(id!, password: password, - switch_uuid: switchUuid, + switchUuid: switchUuid, forceRelay: forceRelay); }); break; diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index 9dff28c22..7acd0f04b 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -22,6 +22,8 @@ const String kAppTypeDesktopRemote = "remote"; const String kAppTypeDesktopFileTransfer = "file transfer"; const String kAppTypeDesktopPortForward = "port forward"; +const bool kCloseMultiWindowByHide = true; + const String kWindowMainWindowOnTop = "main_window_on_top"; const String kWindowGetWindowInfo = "get_window_info"; const String kWindowDisableGrabKeyboard = "disable_grab_keyboard"; @@ -30,6 +32,8 @@ const String kWindowEventHide = "hide"; const String kWindowEventShow = "show"; const String kWindowConnect = "connect"; +const String kOptionSeparateRemoteWindow = "enable-separate-remote-window"; + const String kUniLinksPrefix = "rustdesk://"; const String kUrlActionClose = "close"; diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart index 32ca6c20d..2318a5b81 100644 --- a/flutter/lib/desktop/pages/desktop_home_page.dart +++ b/flutter/lib/desktop/pages/desktop_home_page.dart @@ -554,7 +554,13 @@ class _DesktopHomePageState extends State } else if (call.method == kWindowEventShow) { await rustDeskWinManager.registerActiveWindow(call.arguments["id"]); } else if (call.method == kWindowEventHide) { - await rustDeskWinManager.unregisterActiveWindow(call.arguments["id"]); + final wId = call.arguments['id']; + final isSeparateWindowEnabled = + mainGetBoolOptionSync(kOptionSeparateRemoteWindow); + if (isSeparateWindowEnabled && !kCloseMultiWindowByHide) { + await rustDeskWinManager.destroyWindow(wId); + } + await rustDeskWinManager.unregisterActiveWindow(wId); } else if (call.method == kWindowConnect) { await connectMainDesktop( call.arguments['id'], diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index db3840918..61b5ad072 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -316,7 +316,9 @@ class _GeneralState extends State<_General> { _OptionCheckBox(context, 'Confirm before closing multiple tabs', 'enable-confirm-closing-tabs', isServer: false), - _OptionCheckBox(context, 'Adaptive Bitrate', 'enable-abr') + _OptionCheckBox(context, 'Adaptive Bitrate', 'enable-abr'), + _OptionCheckBox( + context, 'Separate remote window', kOptionSeparateRemoteWindow, isServer: false), ]; // though this is related to GUI, but opengl problem affects all users, so put in config rather than local children.add(Tooltip( diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 35705a283..d9f4d4ec2 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -18,6 +18,7 @@ import '../../common/widgets/remote_input.dart'; import '../../common.dart'; import '../../common/widgets/dialog.dart'; import '../../models/model.dart'; +import '../../models/desktop_render_texture.dart'; import '../../models/platform_model.dart'; import '../../common/shared_state.dart'; import '../../utils/image.dart'; @@ -66,9 +67,7 @@ class _RemotePageState extends State late RxBool _zoomCursor; late RxBool _remoteCursorMoved; late RxBool _keyboardEnabled; - late RxInt _textureId; - late int _textureKey; - final useTextureRender = bind.mainUseTextureRender(); + late RenderTexture _renderTexture; final _blockableOverlayState = BlockableOverlayState(); @@ -86,8 +85,6 @@ class _RemotePageState extends State _showRemoteCursor = ShowRemoteCursorState.find(id); _keyboardEnabled = KeyboardEnabledState.find(id); _remoteCursorMoved = RemoteCursorMovedState.find(id); - _textureKey = newTextureId; - _textureId = RxInt(-1); } @override @@ -115,17 +112,13 @@ class _RemotePageState extends State Wakelock.enable(); } // Register texture. - _textureId.value = -1; - if (useTextureRender) { - textureRenderer.createTexture(_textureKey).then((id) async { - debugPrint("id: $id, texture_key: $_textureKey"); - if (id != -1) { - final ptr = await textureRenderer.getTexturePtr(_textureKey); - platformFFI.registerTexture(sessionId, ptr); - _textureId.value = id; - } - }); + if (mainGetBoolOptionSync(kOptionSeparateRemoteWindow)) { + _renderTexture = renderTexture; + } else { + _renderTexture = RenderTexture(); } + _renderTexture.create(sessionId); + _ffi.ffiModel.updateEventListener(sessionId, widget.id); bind.pluginSyncUi(syncTo: kAppTypeDesktopRemote); _ffi.qualityMonitorModel.checkShowQualityMonitor(sessionId); @@ -208,13 +201,8 @@ class _RemotePageState extends State Future dispose() async { // https://github.com/flutter/flutter/issues/64935 super.dispose(); - debugPrint("REMOTE PAGE dispose ${widget.id}"); - if (useTextureRender) { - platformFFI.registerTexture(sessionId, 0); - // sleep for a while to avoid the texture is used after it's unregistered. - await Future.delayed(Duration(milliseconds: 100)); - await textureRenderer.closeTexture(_textureKey); - } + debugPrint("REMOTE PAGE dispose session $sessionId ${widget.id}"); + await _renderTexture.destroy(); // ensure we leave this session, this is a double check bind.sessionEnterOrLeave(sessionId: sessionId, enter: false); DesktopMultiWindow.removeListener(this); @@ -392,8 +380,8 @@ class _RemotePageState extends State cursorOverImage: _cursorOverImage, keyboardEnabled: _keyboardEnabled, remoteCursorMoved: _remoteCursorMoved, - textureId: _textureId, - useTextureRender: useTextureRender, + textureId: _renderTexture.textureId, + useTextureRender: _renderTexture.useTextureRender, listenerBuilder: (child) => _buildRawTouchAndPointerRegion(child, enterView, leaveView), ); diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index adafaf0aa..1880e75fe 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -13,6 +13,7 @@ import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/main.dart'; import 'package:flutter_hbb/models/platform_model.dart'; import 'package:flutter_hbb/models/state_model.dart'; +import 'package:flutter_hbb/models/desktop_render_texture.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:get/get_rx/src/rx_workers/utils/debouncer.dart'; @@ -574,6 +575,8 @@ class WindowActionPanelState extends State } await windowManager.hide(); } else { + renderTexture.destroy(); + // it's safe to hide the subwindow final controller = WindowController.fromWindowId(kWindowId!); if (Platform.isMacOS && await controller.isFullScreen()) { diff --git a/flutter/lib/models/desktop_render_texture.dart b/flutter/lib/models/desktop_render_texture.dart new file mode 100644 index 000000000..37d387eb2 --- /dev/null +++ b/flutter/lib/models/desktop_render_texture.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:texture_rgba_renderer/texture_rgba_renderer.dart'; + +import '../../common.dart'; +import './platform_model.dart'; + +class RenderTexture { + final RxInt textureId = RxInt(-1); + int _textureKey = -1; + SessionID? _sessionId; + final useTextureRender = bind.mainUseTextureRender(); + + final textureRenderer = TextureRgbaRenderer(); + + RenderTexture(); + + create(SessionID sessionId) { + if (useTextureRender) { + _textureKey = bind.getNextTextureKey(); + _sessionId = sessionId; + + textureRenderer.createTexture(_textureKey).then((id) async { + debugPrint("id: $id, texture_key: $_textureKey"); + if (id != -1) { + final ptr = await textureRenderer.getTexturePtr(_textureKey); + platformFFI.registerTexture(sessionId, ptr); + textureId.value = id; + } + }); + } + } + + destroy() async { + if (useTextureRender && _textureKey != -1 && _sessionId != null) { + platformFFI.registerTexture(_sessionId!, 0); + await textureRenderer.closeTexture(_textureKey); + _textureKey = -1; + } + } + + static final RenderTexture instance = RenderTexture(); +} + +// Global instance for separate texture +final renderTexture = RenderTexture.instance; diff --git a/flutter/lib/utils/multi_window_manager.dart b/flutter/lib/utils/multi_window_manager.dart index 1b5eaf2a1..dfc169193 100644 --- a/flutter/lib/utils/multi_window_manager.dart +++ b/flutter/lib/utils/multi_window_manager.dart @@ -5,6 +5,7 @@ import 'package:desktop_multi_window/desktop_multi_window.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/common.dart'; /// must keep the order @@ -35,146 +36,159 @@ class RustDeskMultiWindowManager { static final instance = RustDeskMultiWindowManager._(); - final List _activeWindows = List.empty(growable: true); + final Set _inactiveWindows = {}; + final Set _activeWindows = {}; final List _windowActiveCallbacks = List.empty(growable: true); - int? _remoteDesktopWindowId; - int? _fileTransferWindowId; - int? _portForwardWindowId; + final Map> _remoteDesktopWindows = {}; + final Map> _fileTransferWindows = {}; + final Map> _portForwardWindows = {}; - Future newRemoteDesktop( - String remoteId, { + Future newSession( + WindowType type, + String methodName, + String remoteId, + Map> windows, { String? password, - String? switch_uuid, bool? forceRelay, + String? switchUuid, + bool? isRDP, }) async { var params = { - "type": WindowType.RemoteDesktop.index, + "type": type.index, "id": remoteId, "password": password, "forceRelay": forceRelay }; - if (switch_uuid != null) { - params['switch_uuid'] = switch_uuid; + if (switchUuid != null) { + params['switch_uuid'] = switchUuid; + } + if (isRDP != null) { + params['isRDP'] = isRDP; } final msg = jsonEncode(params); - try { - final ids = await DesktopMultiWindow.getAllSubWindowIds(); - if (!ids.contains(_remoteDesktopWindowId)) { - _remoteDesktopWindowId = null; - } - } on Error { - _remoteDesktopWindowId = null; - } - if (_remoteDesktopWindowId == null) { - final remoteDesktopController = - await DesktopMultiWindow.createWindow(msg); - remoteDesktopController + newSessionWindow() async { + final windowController = await DesktopMultiWindow.createWindow(msg); + windowController ..setFrame(const Offset(0, 0) & const Size(1280, 720)) ..center() - ..setTitle(getWindowNameWithId(remoteId, - overrideType: WindowType.RemoteDesktop)); + ..setTitle(getWindowNameWithId( + remoteId, + overrideType: type, + )); if (Platform.isMacOS) { - Future.microtask(() => remoteDesktopController.show()); + Future.microtask(() => windowController.show()); } - registerActiveWindow(remoteDesktopController.windowId); - _remoteDesktopWindowId = remoteDesktopController.windowId; - } else { - return call(WindowType.RemoteDesktop, "new_remote_desktop", msg); + registerActiveWindow(windowController.windowId); + windows[windowController.windowId] = {remoteId}; } + + // separate window for file transfer is not supported + bool separateWindow = type != WindowType.FileTransfer && + mainGetBoolOptionSync(kOptionSeparateRemoteWindow); + + if (separateWindow) { + for (final item in windows.entries) { + if (_activeWindows.contains(item.key) && + item.value.contains(remoteId)) { + // already has a window for this remote + final windowController = WindowController.fromWindowId(item.key); + windowController.show(); + // to-do: macos? + // if (Platform.isMacOS) { + // Future.microtask(() => windowController.show()); + // } + return; + } + } + if (kCloseMultiWindowByHide && _inactiveWindows.isNotEmpty) { + final windowId = _inactiveWindows.first; + final invokeRes = + await DesktopMultiWindow.invokeMethod(windowId, methodName, msg); + final windowController = WindowController.fromWindowId(windowId); + windowController.show(); + registerActiveWindow(windowController.windowId); + windows[windowController.windowId] = {remoteId}; + return invokeRes; + } else { + await newSessionWindow(); + } + } else { + if (windows.isEmpty) { + await newSessionWindow(); + } else { + return call(type, methodName, msg); + } + } + } + + Future newRemoteDesktop( + String remoteId, { + String? password, + String? switchUuid, + bool? forceRelay, + }) async { + return await newSession( + WindowType.RemoteDesktop, + 'new_remote_desktop', + remoteId, + _remoteDesktopWindows, + password: password, + forceRelay: forceRelay, + switchUuid: switchUuid, + ); } Future newFileTransfer(String remoteId, {String? password, bool? forceRelay}) async { - var msg = jsonEncode({ - "type": WindowType.FileTransfer.index, - "id": remoteId, - "password": password, - "forceRelay": forceRelay, - }); - - try { - final ids = await DesktopMultiWindow.getAllSubWindowIds(); - if (!ids.contains(_fileTransferWindowId)) { - _fileTransferWindowId = null; - } - } on Error { - _fileTransferWindowId = null; - } - if (_fileTransferWindowId == null) { - final fileTransferController = await DesktopMultiWindow.createWindow(msg); - fileTransferController - ..setFrame(const Offset(0, 0) & const Size(1280, 720)) - ..center() - ..setTitle(getWindowNameWithId(remoteId, - overrideType: WindowType.FileTransfer)); - if (Platform.isMacOS) { - Future.microtask(() => fileTransferController.show()); - } - registerActiveWindow(fileTransferController.windowId); - _fileTransferWindowId = fileTransferController.windowId; - } else { - return call(WindowType.FileTransfer, "new_file_transfer", msg); - } + return await newSession( + WindowType.FileTransfer, + 'new_file_transfer', + remoteId, + _fileTransferWindows, + password: password, + forceRelay: forceRelay, + ); } Future newPortForward(String remoteId, bool isRDP, {String? password, bool? forceRelay}) async { - final msg = jsonEncode({ - "type": WindowType.PortForward.index, - "id": remoteId, - "isRDP": isRDP, - "password": password, - "forceRelay": forceRelay, - }); - - try { - final ids = await DesktopMultiWindow.getAllSubWindowIds(); - if (!ids.contains(_portForwardWindowId)) { - _portForwardWindowId = null; - } - } on Error { - _portForwardWindowId = null; - } - if (_portForwardWindowId == null) { - final portForwardController = await DesktopMultiWindow.createWindow(msg); - portForwardController - ..setFrame(const Offset(0, 0) & const Size(1280, 720)) - ..center() - ..setTitle(getWindowNameWithId(remoteId, - overrideType: WindowType.PortForward)); - if (Platform.isMacOS) { - Future.microtask(() => portForwardController.show()); - } - registerActiveWindow(portForwardController.windowId); - _portForwardWindowId = portForwardController.windowId; - } else { - return call(WindowType.PortForward, "new_port_forward", msg); - } + return await newSession( + WindowType.PortForward, + 'new_port_forward', + remoteId, + _portForwardWindows, + password: password, + forceRelay: forceRelay, + isRDP: isRDP, + ); } Future call(WindowType type, String methodName, dynamic args) async { - int? windowId = findWindowByType(type); - if (windowId == null) { + final wnds = _findWindowsByType(type); + if (wnds.isEmpty) { return; } - return await DesktopMultiWindow.invokeMethod(windowId, methodName, args); + return await DesktopMultiWindow.invokeMethod( + wnds.keys.toList()[0], methodName, args); } - int? findWindowByType(WindowType type) { + Map> _findWindowsByType(WindowType type) { switch (type) { case WindowType.Main: - return 0; + return { + 0: {''} + }; case WindowType.RemoteDesktop: - return _remoteDesktopWindowId; + return _remoteDesktopWindows; case WindowType.FileTransfer: - return _fileTransferWindowId; + return _fileTransferWindows; case WindowType.PortForward: - return _portForwardWindowId; + return _portForwardWindows; case WindowType.Unknown: break; } - return null; + return {}; } void clearWindowType(WindowType type) { @@ -182,13 +196,13 @@ class RustDeskMultiWindowManager { case WindowType.Main: return; case WindowType.RemoteDesktop: - _remoteDesktopWindowId = null; + _remoteDesktopWindows.clear(); break; case WindowType.FileTransfer: - _fileTransferWindowId = null; + _fileTransferWindows.clear(); break; case WindowType.PortForward: - _portForwardWindowId = null; + _portForwardWindows.clear(); break; case WindowType.Unknown: break; @@ -209,27 +223,37 @@ class RustDeskMultiWindowManager { // skip main window, use window manager instead return; } - int? wId = findWindowByType(type); - if (wId != null) { + + List windows = []; + try { + windows = await DesktopMultiWindow.getAllSubWindowIds(); + } catch (e) { + debugPrint('Failed to getAllSubWindowIds of $type, $e'); + return; + } + + if (windows.isEmpty) { + return; + } + for (final wId in windows) { debugPrint("closing multi window: ${type.toString()}"); await saveWindowPosition(type, windowId: wId); try { - final ids = await DesktopMultiWindow.getAllSubWindowIds(); - if (!ids.contains(wId)) { - // no such window already - return; - } + // final ids = await DesktopMultiWindow.getAllSubWindowIds(); + // if (!ids.contains(wId)) { + // // no such window already + // return; + // } await WindowController.fromWindowId(wId).setPreventClose(false); await WindowController.fromWindowId(wId).close(); - // unregister the sub window in the main window. - unregisterActiveWindow(wId); + _activeWindows.remove(wId); } catch (e) { debugPrint("$e"); return; - } finally { - clearWindowType(type); } } + await _notifyActiveWindow(); + clearWindowType(type); } Future> getAllSubWindowIds() async { @@ -245,7 +269,7 @@ class RustDeskMultiWindowManager { } } - List getActiveWindows() { + Set getActiveWindows() { return _activeWindows; } @@ -256,14 +280,19 @@ class RustDeskMultiWindowManager { } Future registerActiveWindow(int windowId) async { - if (_activeWindows.contains(windowId)) { - // ignore - } else { - _activeWindows.add(windowId); - } + _activeWindows.add(windowId); + _inactiveWindows.remove(windowId); await _notifyActiveWindow(); } + Future destroyWindow(int windowId) async { + await WindowController.fromWindowId(windowId).setPreventClose(false); + await WindowController.fromWindowId(windowId).close(); + _remoteDesktopWindows.remove(windowId); + _fileTransferWindows.remove(windowId); + _portForwardWindows.remove(windowId); + } + /// Remove active window which has [`windowId`] /// /// [Availability] @@ -271,10 +300,9 @@ class RustDeskMultiWindowManager { /// For other windows, please post a unregister(hide) event to main window handler: /// `rustDeskWinManager.call(WindowType.Main, kWindowEventHide, {"id": windowId!});` Future unregisterActiveWindow(int windowId) async { - if (!_activeWindows.contains(windowId)) { - // ignore - } else { - _activeWindows.remove(windowId); + _activeWindows.remove(windowId); + if (windowId != kMainWindowId) { + _inactiveWindows.add(windowId); } await _notifyActiveWindow(); } diff --git a/src/flutter.rs b/src/flutter.rs index de4791f38..c35f7340d 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -186,7 +186,7 @@ pub type FlutterRgbaRendererPluginOnRgba = unsafe extern "C" fn( #[derive(Clone)] struct VideoRenderer { // TextureRgba pointer in flutter native. - ptr: usize, + ptr: Arc>, width: usize, height: usize, on_rgba_func: Option>, @@ -214,7 +214,7 @@ impl Default for VideoRenderer { } }; Self { - ptr: 0, + ptr: Default::default(), width: 0, height: 0, on_rgba_func, @@ -231,7 +231,8 @@ impl VideoRenderer { } pub fn on_rgba(&self, rgba: &mut scrap::ImageRgb) { - if self.ptr == usize::default() { + let ptr = self.ptr.read().unwrap(); + if *ptr == usize::default() { return; } @@ -243,7 +244,7 @@ impl VideoRenderer { if let Some(func) = &self.on_rgba_func { unsafe { func( - self.ptr as _, + *ptr as _, rgba.raw.as_ptr() as _, rgba.raw.len() as _, rgba.w as _, @@ -328,7 +329,7 @@ impl FlutterHandler { #[inline] #[cfg(feature = "flutter_texture_render")] pub fn register_texture(&mut self, ptr: usize) { - self.renderer.write().unwrap().ptr = ptr; + *self.renderer.read().unwrap().ptr.write().unwrap() = ptr; } #[inline] diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 8925b5804..710a3f3db 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -15,7 +15,7 @@ use flutter_rust_bridge::{StreamSink, SyncReturn}; use hbb_common::allow_err; use hbb_common::{ config::{self, LocalConfig, PeerConfig, PeerInfoSerde}, - fs, log, + fs, lazy_static, log, message_proto::KeyboardMode, ResultType, }; @@ -24,11 +24,19 @@ use std::{ ffi::{CStr, CString}, os::raw::c_char, str::FromStr, + sync::{ + atomic::{AtomicI32, Ordering}, + Arc, + }, time::SystemTime, }; pub type SessionID = uuid::Uuid; +lazy_static::lazy_static! { + static ref TEXTURE_RENDER_KEY: Arc = Arc::new(AtomicI32::new(0)); +} + fn initialize(app_dir: &str) { *config::APP_DIR.write().unwrap() = app_dir.to_owned(); #[cfg(target_os = "android")] @@ -197,6 +205,11 @@ pub fn session_set_flutter_config(session_id: SessionID, k: String, v: String) { } } +pub fn get_next_texture_key() -> SyncReturn { + let k = TEXTURE_RENDER_KEY.fetch_add(1, Ordering::SeqCst) + 1; + SyncReturn(k) +} + pub fn get_local_flutter_config(k: String) -> SyncReturn { SyncReturn(ui_interface::get_local_flutter_config(k)) } diff --git a/src/ui.rs b/src/ui.rs index 8f036509b..309d0aa6b 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -94,8 +94,7 @@ pub fn start(args: &mut [String]) { args[1] = id; } if args.is_empty() { - let children: Children = Default::default(); - std::thread::spawn(move || check_zombie(children)); + std::thread::spawn(move || check_zombie()); crate::common::check_software_update(); frame.event_handler(UI {}); frame.sciter_handler(UIHostHandler {}); @@ -693,28 +692,6 @@ impl sciter::host::HostHandler for UIHostHandler { } } -pub fn check_zombie(children: Children) { - let mut deads = Vec::new(); - loop { - let mut lock = children.lock().unwrap(); - let mut n = 0; - for (id, c) in lock.1.iter_mut() { - if let Ok(Some(_)) = c.try_wait() { - deads.push(id.clone()); - n += 1; - } - } - for ref id in deads.drain(..) { - lock.1.remove(id); - } - if n > 0 { - lock.0 = true; - } - drop(lock); - std::thread::sleep(std::time::Duration::from_millis(100)); - } -} - #[cfg(not(target_os = "linux"))] fn get_sound_inputs() -> Vec { let mut out = Vec::new(); @@ -748,50 +725,6 @@ pub fn value_crash_workaround(values: &[Value]) -> Arc> { persist } -#[inline] -pub fn new_remote(id: String, remote_type: String, force_relay: bool) { - let mut lock = CHILDREN.lock().unwrap(); - let mut args = vec![format!("--{}", remote_type), id.clone()]; - if force_relay { - args.push("".to_string()); // password - args.push("--relay".to_string()); - } - let key = (id.clone(), remote_type.clone()); - if let Some(c) = lock.1.get_mut(&key) { - if let Ok(Some(_)) = c.try_wait() { - lock.1.remove(&key); - } else { - if remote_type == "rdp" { - allow_err!(c.kill()); - std::thread::sleep(std::time::Duration::from_millis(30)); - c.try_wait().ok(); - lock.1.remove(&key); - } else { - return; - } - } - } - match crate::run_me(args) { - Ok(child) => { - lock.1.insert(key, child); - } - Err(err) => { - log::error!("Failed to spawn remote: {}", err); - } - } -} - -#[inline] -pub fn recent_sessions_updated() -> bool { - let mut children = CHILDREN.lock().unwrap(); - if children.0 { - children.0 = false; - true - } else { - false - } -} - pub fn get_icon() -> String { // 128x128 #[cfg(target_os = "macos")] diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 512589b6e..bcc2ab54f 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -65,6 +65,7 @@ lazy_static::lazy_static! { static ref OPTION_SYNCED: Arc> = Default::default(); static ref OPTIONS : Arc>> = Arc::new(Mutex::new(Config::get_options())); pub static ref SENDER : Mutex> = Mutex::new(check_connect_status(true)); + static ref CHILDREN : Children = Default::default(); } const INIT_ASYNC_JOB_STATUS: &str = " "; @@ -827,11 +828,11 @@ pub fn check_super_user_permission() -> bool { return true; } -#[allow(dead_code)] -pub fn check_zombie(children: Children) { +#[cfg(not(any(target_os = "android", target_os = "ios", feature = "flutter")))] +pub fn check_zombie() { let mut deads = Vec::new(); loop { - let mut lock = children.lock().unwrap(); + let mut lock = CHILDREN.lock().unwrap(); let mut n = 0; for (id, c) in lock.1.iter_mut() { if let Ok(Some(_)) = c.try_wait() { @@ -850,6 +851,51 @@ pub fn check_zombie(children: Children) { } } +#[inline] +#[cfg(not(any(target_os = "android", target_os = "ios", feature = "flutter")))] +pub fn recent_sessions_updated() -> bool { + let mut children = CHILDREN.lock().unwrap(); + if children.0 { + children.0 = false; + true + } else { + false + } +} + +#[cfg(not(any(target_os = "android", target_os = "ios", feature = "flutter")))] +pub fn new_remote(id: String, remote_type: String, force_relay: bool) { + let mut lock = CHILDREN.lock().unwrap(); + let mut args = vec![format!("--{}", remote_type), id.clone()]; + if force_relay { + args.push("".to_string()); // password + args.push("--relay".to_string()); + } + let key = (id.clone(), remote_type.clone()); + if let Some(c) = lock.1.get_mut(&key) { + if let Ok(Some(_)) = c.try_wait() { + lock.1.remove(&key); + } else { + if remote_type == "rdp" { + allow_err!(c.kill()); + std::thread::sleep(std::time::Duration::from_millis(30)); + c.try_wait().ok(); + lock.1.remove(&key); + } else { + return; + } + } + } + match crate::run_me(args) { + Ok(child) => { + lock.1.insert(key, child); + } + Err(err) => { + log::error!("Failed to spawn remote: {}", err); + } + } +} + // Make sure `SENDER` is inited here. #[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] From 773a74e2a90fbe3048017c7c5ef0d0a186fbaedd Mon Sep 17 00:00:00 2001 From: dignow Date: Wed, 2 Aug 2023 20:38:09 +0800 Subject: [PATCH 02/16] refact, separate remote window, try active session Signed-off-by: dignow --- flutter/lib/common.dart | 2 +- flutter/lib/consts.dart | 5 ++ .../lib/desktop/pages/desktop_home_page.dart | 2 +- .../desktop/pages/file_manager_tab_page.dart | 4 +- .../desktop/pages/port_forward_tab_page.dart | 4 +- .../lib/desktop/pages/remote_tab_page.dart | 10 +++- .../lib/desktop/widgets/tabbar_widget.dart | 11 ++++- flutter/lib/main.dart | 2 +- flutter/lib/models/chat_model.dart | 4 +- flutter/lib/models/model.dart | 2 +- flutter/lib/models/server_model.dart | 4 +- flutter/lib/utils/multi_window_manager.dart | 46 ++++++++----------- 12 files changed, 54 insertions(+), 42 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 522f93c6f..ee7806a14 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -543,7 +543,7 @@ closeConnection({String? id}) { } } -void window_on_top(int? id) async { +void windowOnTop(int? id) async { if (!isDesktop) { return; } diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index 7acd0f04b..e44da0854 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -32,6 +32,11 @@ const String kWindowEventHide = "hide"; const String kWindowEventShow = "show"; const String kWindowConnect = "connect"; +const String kWindowEventNewRemoteDesktop = "new_remote_desktop"; +const String kWindowEventNewFileTransfer = "new_file_transfer"; +const String kWindowEventNewPortForward = "new_port_forward"; +const String kWindowEventActiveSession = "active_session"; + const String kOptionSeparateRemoteWindow = "enable-separate-remote-window"; const String kUniLinksPrefix = "rustdesk://"; diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart index 2318a5b81..6b7fe7140 100644 --- a/flutter/lib/desktop/pages/desktop_home_page.dart +++ b/flutter/lib/desktop/pages/desktop_home_page.dart @@ -527,7 +527,7 @@ class _DesktopHomePageState extends State debugPrint( "[Main] call ${call.method} with args ${call.arguments} from window $fromWindowId"); if (call.method == kWindowMainWindowOnTop) { - window_on_top(null); + windowOnTop(null); } else if (call.method == kWindowGetWindowInfo) { final screen = (await window_size.getWindowInfo()).screen; if (screen == null) { diff --git a/flutter/lib/desktop/pages/file_manager_tab_page.dart b/flutter/lib/desktop/pages/file_manager_tab_page.dart index c011fe48d..1412ba059 100644 --- a/flutter/lib/desktop/pages/file_manager_tab_page.dart +++ b/flutter/lib/desktop/pages/file_manager_tab_page.dart @@ -60,10 +60,10 @@ class _FileManagerTabPageState extends State { print( "[FileTransfer] call ${call.method} with args ${call.arguments} from window $fromWindowId to ${windowId()}"); // for simplify, just replace connectionId - if (call.method == "new_file_transfer") { + if (call.method == kWindowEventNewFileTransfer) { final args = jsonDecode(call.arguments); final id = args['id']; - window_on_top(windowId()); + windowOnTop(windowId()); tabController.add(TabInfo( key: id, label: id, diff --git a/flutter/lib/desktop/pages/port_forward_tab_page.dart b/flutter/lib/desktop/pages/port_forward_tab_page.dart index df824e431..621f393e0 100644 --- a/flutter/lib/desktop/pages/port_forward_tab_page.dart +++ b/flutter/lib/desktop/pages/port_forward_tab_page.dart @@ -60,11 +60,11 @@ class _PortForwardTabPageState extends State { debugPrint( "[Port Forward] call ${call.method} with args ${call.arguments} from window $fromWindowId"); // for simplify, just replace connectionId - if (call.method == "new_port_forward") { + if (call.method == kWindowEventNewPortForward) { final args = jsonDecode(call.arguments); final id = args['id']; final isRDP = args['isRDP']; - window_on_top(windowId()); + windowOnTop(windowId()); if (tabController.state.value.tabs.indexWhere((e) => e.key == id) >= 0) { debugPrint("port forward $id exists"); diff --git a/flutter/lib/desktop/pages/remote_tab_page.dart b/flutter/lib/desktop/pages/remote_tab_page.dart index 2e553e724..296b7ec08 100644 --- a/flutter/lib/desktop/pages/remote_tab_page.dart +++ b/flutter/lib/desktop/pages/remote_tab_page.dart @@ -95,11 +95,11 @@ class _ConnectionTabPageState extends State { "[Remote Page] call ${call.method} with args ${call.arguments} from window $fromWindowId"); // for simplify, just replace connectionId - if (call.method == "new_remote_desktop") { + if (call.method == kWindowEventNewRemoteDesktop) { final args = jsonDecode(call.arguments); final id = args['id']; final switchUuid = args['switch_uuid']; - window_on_top(windowId()); + windowOnTop(windowId()); ConnectionTypeState.init(id); _toolbarState.setShow( bind.mainGetUserDefaultOption(key: 'collapse_toolbar') != 'Y'); @@ -125,6 +125,12 @@ class _ConnectionTabPageState extends State { tabController.clear(); } else if (call.method == kWindowActionRebuild) { reloadCurrentWindow(); + } else if (call.method == kWindowEventActiveSession) { + final jumpOk = tabController.jumpToByKey(call.arguments); + if (jumpOk) { + windowOnTop(windowId()); + } + return jumpOk; } _update_remote_count(); }); diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index 1880e75fe..2a7d1452e 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -147,8 +147,10 @@ class DesktopTabController { /// For addTab, tabPage has not been initialized, set [callOnSelected] to false, /// and call [onSelected] at the end of initState - void jumpTo(int index, {bool callOnSelected = true}) { - if (!isDesktop || index < 0) return; + bool jumpTo(int index, {bool callOnSelected = true}) { + if (!isDesktop || index < 0) { + return false; + } state.update((val) { val!.selected = index; Future.delayed(Duration(milliseconds: 100), (() { @@ -169,8 +171,13 @@ class DesktopTabController { onSelected?.call(key); } } + return true; } + bool jumpToByKey(String key, {bool callOnSelected = true}) => + jumpTo(state.value.tabs.indexWhere((tab) => tab.key == key), + callOnSelected: callOnSelected); + void closeBy(String? key) { if (!isDesktop) return; assert(onRemoved != null); diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index 4578c2f21..8fcbd4ad0 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -250,7 +250,7 @@ showCmWindow({bool isStartup = false}) async { await windowManager.minimize(); //needed await windowManager.setSizeAlignment( kConnectionManagerWindowSizeClosedChat, Alignment.topRight); - window_on_top(null); + windowOnTop(null); } } } diff --git a/flutter/lib/models/chat_model.dart b/flutter/lib/models/chat_model.dart index fe1de2512..898427351 100644 --- a/flutter/lib/models/chat_model.dart +++ b/flutter/lib/models/chat_model.dart @@ -367,7 +367,7 @@ class ChatModel with ChangeNotifier { // not minisized: add count if (await WindowController.fromWindowId(stateGlobal.windowId) .isMinimized()) { - window_on_top(stateGlobal.windowId); + windowOnTop(stateGlobal.windowId); if (notSelected) { tabController.jumpTo(index); } @@ -386,7 +386,7 @@ class ChatModel with ChangeNotifier { return; } if (isDesktop) { - window_on_top(null); + windowOnTop(null); // disable auto jumpTo other tab when hasFocus, and mark unread message final currentSelectedTab = session.serverModel.tabController.state.value.selectedTabInfo; diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 54965680a..9c20a6a95 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -260,7 +260,7 @@ class FfiModel with ChangeNotifier { }); break; default: - window_on_top(null); + windowOnTop(null); break; } } diff --git a/flutter/lib/models/server_model.dart b/flutter/lib/models/server_model.dart index 6867aa6fe..6e6dd2b82 100644 --- a/flutter/lib/models/server_model.dart +++ b/flutter/lib/models/server_model.dart @@ -473,7 +473,7 @@ class ServerModel with ChangeNotifier { onTap: () {}, page: desktop.buildConnectionCard(client))); Future.delayed(Duration.zero, () async { - if (!hideCm) window_on_top(null); + if (!hideCm) windowOnTop(null); }); // Only do the hidden task when on Desktop. if (client.authorized && isDesktop) { @@ -612,7 +612,7 @@ class ServerModel with ChangeNotifier { if (client.incomingVoiceCall) { // Has incoming phone call, let's set the window on top. Future.delayed(Duration.zero, () { - window_on_top(null); + windowOnTop(null); }); } notifyListeners(); diff --git a/flutter/lib/utils/multi_window_manager.dart b/flutter/lib/utils/multi_window_manager.dart index dfc169193..8b18eb750 100644 --- a/flutter/lib/utils/multi_window_manager.dart +++ b/flutter/lib/utils/multi_window_manager.dart @@ -39,15 +39,15 @@ class RustDeskMultiWindowManager { final Set _inactiveWindows = {}; final Set _activeWindows = {}; final List _windowActiveCallbacks = List.empty(growable: true); - final Map> _remoteDesktopWindows = {}; - final Map> _fileTransferWindows = {}; - final Map> _portForwardWindows = {}; + final List _remoteDesktopWindows = List.empty(growable: true); + final List _fileTransferWindows = List.empty(growable: true); + final List _portForwardWindows = List.empty(growable: true); Future newSession( WindowType type, String methodName, String remoteId, - Map> windows, { + List windows, { String? password, bool? forceRelay, String? switchUuid, @@ -80,27 +80,23 @@ class RustDeskMultiWindowManager { Future.microtask(() => windowController.show()); } registerActiveWindow(windowController.windowId); - windows[windowController.windowId] = {remoteId}; + windows.add(windowController.windowId); } // separate window for file transfer is not supported bool separateWindow = type != WindowType.FileTransfer && mainGetBoolOptionSync(kOptionSeparateRemoteWindow); - if (separateWindow) { - for (final item in windows.entries) { - if (_activeWindows.contains(item.key) && - item.value.contains(remoteId)) { - // already has a window for this remote - final windowController = WindowController.fromWindowId(item.key); - windowController.show(); - // to-do: macos? - // if (Platform.isMacOS) { - // Future.microtask(() => windowController.show()); - // } + if (windows.length > 1 || separateWindow) { + for (final windowId in windows) { + if (await DesktopMultiWindow.invokeMethod( + windowId, kWindowEventActiveSession, remoteId)) { return; } } + } + + if (separateWindow) { if (kCloseMultiWindowByHide && _inactiveWindows.isNotEmpty) { final windowId = _inactiveWindows.first; final invokeRes = @@ -108,7 +104,7 @@ class RustDeskMultiWindowManager { final windowController = WindowController.fromWindowId(windowId); windowController.show(); registerActiveWindow(windowController.windowId); - windows[windowController.windowId] = {remoteId}; + windows.add(windowController.windowId); return invokeRes; } else { await newSessionWindow(); @@ -130,7 +126,7 @@ class RustDeskMultiWindowManager { }) async { return await newSession( WindowType.RemoteDesktop, - 'new_remote_desktop', + kWindowEventNewRemoteDesktop, remoteId, _remoteDesktopWindows, password: password, @@ -143,7 +139,7 @@ class RustDeskMultiWindowManager { {String? password, bool? forceRelay}) async { return await newSession( WindowType.FileTransfer, - 'new_file_transfer', + kWindowEventNewFileTransfer, remoteId, _fileTransferWindows, password: password, @@ -155,7 +151,7 @@ class RustDeskMultiWindowManager { {String? password, bool? forceRelay}) async { return await newSession( WindowType.PortForward, - 'new_port_forward', + kWindowEventNewPortForward, remoteId, _portForwardWindows, password: password, @@ -170,15 +166,13 @@ class RustDeskMultiWindowManager { return; } return await DesktopMultiWindow.invokeMethod( - wnds.keys.toList()[0], methodName, args); + wnds[0], methodName, args); } - Map> _findWindowsByType(WindowType type) { + List _findWindowsByType(WindowType type) { switch (type) { case WindowType.Main: - return { - 0: {''} - }; + return [0]; case WindowType.RemoteDesktop: return _remoteDesktopWindows; case WindowType.FileTransfer: @@ -188,7 +182,7 @@ class RustDeskMultiWindowManager { case WindowType.Unknown: break; } - return {}; + return []; } void clearWindowType(WindowType type) { From 688ecef4cc082b82db1625be1116c2bce9eb224a Mon Sep 17 00:00:00 2001 From: dignow Date: Wed, 2 Aug 2023 22:02:18 +0800 Subject: [PATCH 03/16] refact, separate remote window, save session position Signed-off-by: dignow --- flutter/lib/common.dart | 28 +++++++++++++++++-- flutter/lib/consts.dart | 1 + .../lib/desktop/pages/remote_tab_page.dart | 13 ++++++++- flutter/lib/main.dart | 2 +- flutter/lib/utils/multi_window_manager.dart | 2 +- src/flutter_ffi.rs | 19 +++++++++++++ 6 files changed, 60 insertions(+), 5 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index ee7806a14..ff88c5647 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -1408,8 +1408,24 @@ Future saveWindowPosition(WindowType type, {int? windowId}) async { sz.width, sz.height, position.dx, position.dy, isMaximized); debugPrint( "Saving frame: $windowId: ${pos.width}/${pos.height}, offset:${pos.offsetWidth}/${pos.offsetHeight}"); + await bind.setLocalFlutterConfig( k: kWindowPrefix + type.name, v: pos.toString()); + + if (type == WindowType.RemoteDesktop && windowId != null) { + await _saveSessionWindowPosition(windowId, pos); + } +} + +Future _saveSessionWindowPosition(int windowId, LastWindowPosition pos) async { + final remoteList = await DesktopMultiWindow.invokeMethod( + windowId, kWindowEventGetRemoteList, null); + if (remoteList != null) { + for (final peerId in remoteList.split(',')) { + bind.sessionSetFlutterConfigByPeerId( + id: peerId, k: kWindowPrefix, v: pos.toString()); + } + } } Future _adjustRestoreMainWindowSize(double? width, double? height) async { @@ -1499,7 +1515,7 @@ Future _adjustRestoreMainWindowOffset( /// Restore window position and size on start /// Note that windowId must be provided if it's subwindow -Future restoreWindowPosition(WindowType type, {int? windowId}) async { +Future restoreWindowPosition(WindowType type, {int? windowId, String? peerId}) async { if (bind .mainGetEnv(key: "DISABLE_RUSTDESK_RESTORE_WINDOW_POSITION") .isNotEmpty) { @@ -1508,8 +1524,16 @@ Future restoreWindowPosition(WindowType type, {int? windowId}) async { if (type != WindowType.Main && windowId == null) { debugPrint( "Error: windowId cannot be null when saving positions for sub window"); + return false; } - final pos = bind.getLocalFlutterConfig(k: kWindowPrefix + type.name); + + String? pos; + if (type == WindowType.RemoteDesktop && windowId != null && peerId != null) { + pos = await bind.sessionGetFlutterConfigByPeerId(id: peerId, k: kWindowPrefix); + } + pos ??= bind.getLocalFlutterConfig(k: kWindowPrefix + type.name); + + var lpos = LastWindowPosition.loadFromString(pos); if (lpos == null) { debugPrint("no window position saved, ignoring position restoration"); diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index e44da0854..f945a6292 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -36,6 +36,7 @@ const String kWindowEventNewRemoteDesktop = "new_remote_desktop"; const String kWindowEventNewFileTransfer = "new_file_transfer"; const String kWindowEventNewPortForward = "new_port_forward"; const String kWindowEventActiveSession = "active_session"; +const String kWindowEventGetRemoteList = "get_remote_list"; const String kOptionSeparateRemoteWindow = "enable-separate-remote-window"; diff --git a/flutter/lib/desktop/pages/remote_tab_page.dart b/flutter/lib/desktop/pages/remote_tab_page.dart index 296b7ec08..4ab731649 100644 --- a/flutter/lib/desktop/pages/remote_tab_page.dart +++ b/flutter/lib/desktop/pages/remote_tab_page.dart @@ -131,11 +131,22 @@ class _ConnectionTabPageState extends State { windowOnTop(windowId()); } return jumpOk; + } else if (call.method == kWindowEventGetRemoteList) { + return tabController.state.value.tabs + .map((e) => e.key) + .toList() + .join(','); } _update_remote_count(); }); Future.delayed(Duration.zero, () { - restoreWindowPosition(WindowType.RemoteDesktop, windowId: windowId()); + restoreWindowPosition( + WindowType.RemoteDesktop, + windowId: windowId(), + peerId: tabController.state.value.tabs.isEmpty + ? null + : tabController.state.value.tabs[0].key, + ); }); } diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index 8fcbd4ad0..14db061bd 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -197,7 +197,7 @@ void runMultiWindow( switch (appType) { case kAppTypeDesktopRemote: await restoreWindowPosition(WindowType.RemoteDesktop, - windowId: kWindowId!); + windowId: kWindowId!, peerId: argument['id'] as String?); break; case kAppTypeDesktopFileTransfer: await restoreWindowPosition(WindowType.FileTransfer, diff --git a/flutter/lib/utils/multi_window_manager.dart b/flutter/lib/utils/multi_window_manager.dart index 8b18eb750..3fcb17c2b 100644 --- a/flutter/lib/utils/multi_window_manager.dart +++ b/flutter/lib/utils/multi_window_manager.dart @@ -70,7 +70,7 @@ class RustDeskMultiWindowManager { newSessionWindow() async { final windowController = await DesktopMultiWindow.createWindow(msg); windowController - ..setFrame(const Offset(0, 0) & const Size(1280, 720)) + ..setFrame(const Offset(0, 0) & Size(1280 + windowController.windowId * 20, 720 + windowController.windowId * 20)) ..center() ..setTitle(getWindowNameWithId( remoteId, diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 710a3f3db..5df00c344 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -205,6 +205,25 @@ pub fn session_set_flutter_config(session_id: SessionID, k: String, v: String) { } } +pub fn session_get_flutter_config_by_peer_id(id: String, k: String) -> Option { + if let Some((_, session)) = SESSIONS.read().unwrap().iter().find(|(_, s)| s.id == id) { + Some(session.get_flutter_config(k)) + } else { + None + } +} + +pub fn session_set_flutter_config_by_peer_id(id: String, k: String, v: String) { + if let Some((_, session)) = SESSIONS + .write() + .unwrap() + .iter_mut() + .find(|(_, s)| s.id == id) + { + session.save_flutter_config(k, v); + } +} + pub fn get_next_texture_key() -> SyncReturn { let k = TEXTURE_RENDER_KEY.fetch_add(1, Ordering::SeqCst) + 1; SyncReturn(k) From 1a8463015f22cb3513d20c491c7562b580b00334 Mon Sep 17 00:00:00 2001 From: dignow Date: Wed, 2 Aug 2023 23:10:31 +0800 Subject: [PATCH 04/16] refact, separate remote window, connect(separate window) Signed-off-by: dignow --- flutter/lib/common.dart | 52 ++++++++++++------- flutter/lib/common/widgets/peer_card.dart | 48 +++++++++++------ .../lib/desktop/pages/desktop_home_page.dart | 3 +- flutter/lib/desktop/pages/remote_page.dart | 2 +- flutter/lib/utils/multi_window_manager.dart | 15 ++++-- src/lang/ca.rs | 2 + src/lang/cn.rs | 2 + src/lang/cs.rs | 2 + src/lang/da.rs | 2 + src/lang/de.rs | 2 + src/lang/el.rs | 2 + src/lang/eo.rs | 2 + src/lang/es.rs | 2 + src/lang/fa.rs | 2 + src/lang/fr.rs | 2 + src/lang/hu.rs | 2 + src/lang/id.rs | 2 + src/lang/it.rs | 2 + src/lang/ja.rs | 2 + src/lang/ko.rs | 2 + src/lang/kz.rs | 2 + src/lang/lt.rs | 2 + src/lang/nl.rs | 2 + src/lang/pl.rs | 2 + src/lang/pt_PT.rs | 2 + src/lang/ptbr.rs | 2 + src/lang/ro.rs | 2 + src/lang/ru.rs | 2 + src/lang/sk.rs | 2 + src/lang/sl.rs | 2 + src/lang/sq.rs | 2 + src/lang/sr.rs | 2 + src/lang/sv.rs | 2 + src/lang/template.rs | 2 + src/lang/th.rs | 2 + src/lang/tr.rs | 2 + src/lang/tw.rs | 2 + src/lang/ua.rs | 2 + src/lang/vn.rs | 2 + 39 files changed, 146 insertions(+), 42 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index ff88c5647..a9117554d 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -1515,7 +1515,8 @@ Future _adjustRestoreMainWindowOffset( /// Restore window position and size on start /// Note that windowId must be provided if it's subwindow -Future restoreWindowPosition(WindowType type, {int? windowId, String? peerId}) async { +Future restoreWindowPosition(WindowType type, + {int? windowId, String? peerId}) async { if (bind .mainGetEnv(key: "DISABLE_RUSTDESK_RESTORE_WINDOW_POSITION") .isNotEmpty) { @@ -1529,11 +1530,11 @@ Future restoreWindowPosition(WindowType type, {int? windowId, String? peer String? pos; if (type == WindowType.RemoteDesktop && windowId != null && peerId != null) { - pos = await bind.sessionGetFlutterConfigByPeerId(id: peerId, k: kWindowPrefix); + pos = await bind.sessionGetFlutterConfigByPeerId( + id: peerId, k: kWindowPrefix); } pos ??= bind.getLocalFlutterConfig(k: kWindowPrefix + type.name); - - + var lpos = LastWindowPosition.loadFromString(pos); if (lpos == null) { debugPrint("no window position saved, ignoring position restoration"); @@ -1781,17 +1782,20 @@ List? urlLinkToCmdArgs(Uri uri) { return null; } -connectMainDesktop(String id, - {required bool isFileTransfer, - required bool isTcpTunneling, - required bool isRDP, - bool? forceRelay}) async { +connectMainDesktop( + String id, { + required bool isFileTransfer, + required bool isTcpTunneling, + required bool isRDP, + bool? forceRelay, + bool forceSeparateWindow = false, +}) async { if (isFileTransfer) { await rustDeskWinManager.newFileTransfer(id, forceRelay: forceRelay); } else if (isTcpTunneling || isRDP) { await rustDeskWinManager.newPortForward(id, isRDP, forceRelay: forceRelay); } else { - await rustDeskWinManager.newRemoteDesktop(id, forceRelay: forceRelay); + await rustDeskWinManager.newRemoteDesktop(id, forceRelay: forceRelay, forceSeparateWindow: forceSeparateWindow); } } @@ -1799,10 +1803,14 @@ connectMainDesktop(String id, /// If [isFileTransfer], starts a session only for file transfer. /// If [isTcpTunneling], starts a session only for tcp tunneling. /// If [isRDP], starts a session only for rdp. -connect(BuildContext context, String id, - {bool isFileTransfer = false, - bool isTcpTunneling = false, - bool isRDP = false}) async { +connect( + BuildContext context, + String id, { + bool isFileTransfer = false, + bool isTcpTunneling = false, + bool isRDP = false, + bool forceSeparateWindow = false, +}) async { if (id == '') return; id = id.replaceAll(' ', ''); final oldId = id; @@ -1813,18 +1821,22 @@ connect(BuildContext context, String id, if (isDesktop) { if (desktopType == DesktopType.main) { - await connectMainDesktop(id, - isFileTransfer: isFileTransfer, - isTcpTunneling: isTcpTunneling, - isRDP: isRDP, - forceRelay: forceRelay); + await connectMainDesktop( + id, + isFileTransfer: isFileTransfer, + isTcpTunneling: isTcpTunneling, + isRDP: isRDP, + forceRelay: forceRelay, + forceSeparateWindow: forceSeparateWindow, + ); } else { await rustDeskWinManager.call(WindowType.Main, kWindowConnect, { 'id': id, 'isFileTransfer': isFileTransfer, 'isTcpTunneling': isTcpTunneling, 'isRDP': isRDP, - "forceRelay": forceRelay, + 'forceRelay': forceRelay, + 'forceSeparateWindow': forceSeparateWindow, }); } } else { diff --git a/flutter/lib/common/widgets/peer_card.dart b/flutter/lib/common/widgets/peer_card.dart index 44178bd5d..4e03edfbd 100644 --- a/flutter/lib/common/widgets/peer_card.dart +++ b/flutter/lib/common/widgets/peer_card.dart @@ -399,10 +399,14 @@ abstract class BasePeerCard extends StatelessWidget { Future>> _buildMenuItems(BuildContext context); MenuEntryBase _connectCommonAction( - BuildContext context, String id, String title, - {bool isFileTransfer = false, - bool isTcpTunneling = false, - bool isRDP = false}) { + BuildContext context, + String id, + String title, { + bool isFileTransfer = false, + bool isTcpTunneling = false, + bool isRDP = false, + bool forceSeparateWindow = false, + }) { return MenuEntryButton( childBuilder: (TextStyle? style) => Text( title, @@ -415,6 +419,7 @@ abstract class BasePeerCard extends StatelessWidget { isFileTransfer: isFileTransfer, isTcpTunneling: isTcpTunneling, isRDP: isRDP, + forceSeparateWindow: forceSeparateWindow, ); }, padding: menuPadding, @@ -423,13 +428,26 @@ abstract class BasePeerCard extends StatelessWidget { } @protected - MenuEntryBase _connectAction(BuildContext context, Peer peer) { + List> _connectActions(BuildContext context, Peer peer) { + final actions = [_connectAction(context, peer, false)]; + if (!mainGetLocalBoolOptionSync(kOptionSeparateRemoteWindow)) { + actions.add(_connectAction(context, peer, true)); + } + return actions; + } + + @protected + MenuEntryBase _connectAction( + BuildContext context, Peer peer, bool forceSeparateWindow) { return _connectCommonAction( - context, - peer.id, - peer.alias.isEmpty - ? translate('Connect') - : "${translate('Connect')} ${peer.id}"); + context, + peer.id, + (peer.alias.isEmpty + ? translate('Connect') + : '${translate('Connect')} ${peer.id}') + + (forceSeparateWindow ? ' (${translate('separate window')})' : ''), + forceSeparateWindow: forceSeparateWindow, + ); } @protected @@ -796,7 +814,7 @@ class RecentPeerCard extends BasePeerCard { Future>> _buildMenuItems( BuildContext context) async { final List> menuItems = [ - _connectAction(context, peer), + ..._connectActions(context, peer), _transferFileAction(context, peer.id), ]; @@ -852,7 +870,7 @@ class FavoritePeerCard extends BasePeerCard { Future>> _buildMenuItems( BuildContext context) async { final List> menuItems = [ - _connectAction(context, peer), + ..._connectActions(context, peer), _transferFileAction(context, peer.id), ]; if (isDesktop && peer.platform != 'Android') { @@ -902,7 +920,7 @@ class DiscoveredPeerCard extends BasePeerCard { Future>> _buildMenuItems( BuildContext context) async { final List> menuItems = [ - _connectAction(context, peer), + ..._connectActions(context, peer), _transferFileAction(context, peer.id), ]; @@ -954,7 +972,7 @@ class AddressBookPeerCard extends BasePeerCard { Future>> _buildMenuItems( BuildContext context) async { final List> menuItems = [ - _connectAction(context, peer), + ..._connectActions(context, peer), _transferFileAction(context, peer.id), ]; if (isDesktop && peer.platform != 'Android') { @@ -1016,7 +1034,7 @@ class MyGroupPeerCard extends BasePeerCard { Future>> _buildMenuItems( BuildContext context) async { final List> menuItems = [ - _connectAction(context, peer), + ..._connectActions(context, peer), _transferFileAction(context, peer.id), ]; if (isDesktop && peer.platform != 'Android') { diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart index 6b7fe7140..43c73b0b5 100644 --- a/flutter/lib/desktop/pages/desktop_home_page.dart +++ b/flutter/lib/desktop/pages/desktop_home_page.dart @@ -556,7 +556,7 @@ class _DesktopHomePageState extends State } else if (call.method == kWindowEventHide) { final wId = call.arguments['id']; final isSeparateWindowEnabled = - mainGetBoolOptionSync(kOptionSeparateRemoteWindow); + mainGetLocalBoolOptionSync(kOptionSeparateRemoteWindow); if (isSeparateWindowEnabled && !kCloseMultiWindowByHide) { await rustDeskWinManager.destroyWindow(wId); } @@ -568,6 +568,7 @@ class _DesktopHomePageState extends State isTcpTunneling: call.arguments['isTcpTunneling'], isRDP: call.arguments['isRDP'], forceRelay: call.arguments['forceRelay'], + forceSeparateWindow: call.arguments['forceSeparateWindow'], ); } }); diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index d9f4d4ec2..7ffa527fe 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -112,7 +112,7 @@ class _RemotePageState extends State Wakelock.enable(); } // Register texture. - if (mainGetBoolOptionSync(kOptionSeparateRemoteWindow)) { + if (mainGetLocalBoolOptionSync(kOptionSeparateRemoteWindow)) { _renderTexture = renderTexture; } else { _renderTexture = RenderTexture(); diff --git a/flutter/lib/utils/multi_window_manager.dart b/flutter/lib/utils/multi_window_manager.dart index 3fcb17c2b..720b7dc95 100644 --- a/flutter/lib/utils/multi_window_manager.dart +++ b/flutter/lib/utils/multi_window_manager.dart @@ -52,6 +52,7 @@ class RustDeskMultiWindowManager { bool? forceRelay, String? switchUuid, bool? isRDP, + bool forceSeparateWindow = false, }) async { var params = { "type": type.index, @@ -70,7 +71,9 @@ class RustDeskMultiWindowManager { newSessionWindow() async { final windowController = await DesktopMultiWindow.createWindow(msg); windowController - ..setFrame(const Offset(0, 0) & Size(1280 + windowController.windowId * 20, 720 + windowController.windowId * 20)) + ..setFrame(const Offset(0, 0) & + Size(1280 + windowController.windowId * 20, + 720 + windowController.windowId * 20)) ..center() ..setTitle(getWindowNameWithId( remoteId, @@ -84,8 +87,9 @@ class RustDeskMultiWindowManager { } // separate window for file transfer is not supported - bool separateWindow = type != WindowType.FileTransfer && - mainGetBoolOptionSync(kOptionSeparateRemoteWindow); + bool separateWindow = forceSeparateWindow || + (type != WindowType.FileTransfer && + mainGetLocalBoolOptionSync(kOptionSeparateRemoteWindow)); if (windows.length > 1 || separateWindow) { for (final windowId in windows) { @@ -123,6 +127,7 @@ class RustDeskMultiWindowManager { String? password, String? switchUuid, bool? forceRelay, + bool forceSeparateWindow = false, }) async { return await newSession( WindowType.RemoteDesktop, @@ -132,6 +137,7 @@ class RustDeskMultiWindowManager { password: password, forceRelay: forceRelay, switchUuid: switchUuid, + forceSeparateWindow: forceSeparateWindow, ); } @@ -165,8 +171,7 @@ class RustDeskMultiWindowManager { if (wnds.isEmpty) { return; } - return await DesktopMultiWindow.invokeMethod( - wnds[0], methodName, args); + return await DesktopMultiWindow.invokeMethod(wnds[0], methodName, args); } List _findWindowsByType(WindowType type) { diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 51a731c35..326ef5dd0 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index b4a76c62e..f52cb9b61 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", "管理的设备数已达到最大值"), ("Sync with recent sessions", "同步最近会话"), ("Sort tags", "对标签进行排序"), + ("Separate remote window", "使用独立远程窗口"), + ("separate window", "独立窗口"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 29898eaf7..a61c46001 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 0a4adaf36..c3f43b7f1 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index c42ac5719..e59eb63d6 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", "Sie haben die maximale Anzahl der verwalteten Geräte erreicht."), ("Sync with recent sessions", "Synchronisierung mit den letzten Sitzungen"), ("Sort tags", "Tags sortieren"), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index eb91e9a83..83160cae7 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index a576538ef..47792961a 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index c39bcb7f5..b57474793 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", "Has alcanzado el máximo número de dispositivos administrados."), ("Sync with recent sessions", "Sincronizar con sesiones recientes"), ("Sort tags", "Ordenar etiquetas"), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 112edf32a..6f2531b46 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 57c548e08..679620a99 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 638ed3adf..8eacb75d3 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index ac9df01b3..2d12c31e5 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index c89e6cf72..5118adc16 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", "Hai raggiunto il numero massimo di dispositivi gestibili."), ("Sync with recent sessions", "Sincronizza con le sessioni recenti"), ("Sort tags", "Ordina etichette"), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 67234039d..ed09d6132 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index c0b71c205..d48347023 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 829c59c73..1bcadec64 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index 69936b2b4..accdf9a39 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index c470f2270..b1bcd1d2a 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", "Het maximum aantal gecontroleerde apparaten is bereikt."), ("Sync with recent sessions", "Recente sessies synchroniseren"), ("Sort tags", "Labels sorteren"), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index af6d11659..1d813ef41 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 52675daf7..6b32a1e48 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 195b79804..c732dd2af 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index 378d7c035..4db68ee50 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 6b48ef20b..9dcef1a48 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", "Достигнуто максимальне количество управляемых устройств."), ("Sync with recent sessions", "Синхронизация последних сессий"), ("Sort tags", "Сортировка меток"), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index f24f55bef..3f8d57169 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index ff5c254e0..e3c17a659 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 797328139..46db68578 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 3bb40f4ce..370865e0a 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index ac898e38f..3ec8535ef 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 5c710624f..7eb168fdf 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 2b2d0f4b2..f125384de 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index d7bd99f25..b4959b158 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index bef733f00..cda76a154 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 3b115a5f3..8d46b61ae 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 59490da1d..c188e2d14 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -524,5 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("exceed_max_devices", ""), ("Sync with recent sessions", ""), ("Sort tags", ""), + ("Separate remote window", ""), + ("separate window", ""), ].iter().cloned().collect(); } From 902f56c499c54c221f077ac8acc9140617513f7e Mon Sep 17 00:00:00 2001 From: dignow Date: Thu, 3 Aug 2023 18:03:56 +0800 Subject: [PATCH 05/16] refact, separate remote window, add offset Signed-off-by: dignow --- flutter/lib/common.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index a9117554d..7324a8c18 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -1528,10 +1528,12 @@ Future restoreWindowPosition(WindowType type, return false; } + bool isRemotePeerPos = false; String? pos; if (type == WindowType.RemoteDesktop && windowId != null && peerId != null) { pos = await bind.sessionGetFlutterConfigByPeerId( id: peerId, k: kWindowPrefix); + isRemotePeerPos = pos != null; } pos ??= bind.getLocalFlutterConfig(k: kWindowPrefix + type.name); @@ -1540,6 +1542,14 @@ Future restoreWindowPosition(WindowType type, debugPrint("no window position saved, ignoring position restoration"); return false; } + if (type == WindowType.RemoteDesktop && !isRemotePeerPos && windowId != null) { + if (lpos.offsetWidth != null) { + lpos.offsetWidth = lpos.offsetWidth! + windowId * 20; + } + if (lpos.offsetHeight != null) { + lpos.offsetHeight = lpos.offsetHeight! + windowId * 20; + } + } switch (type) { case WindowType.Main: From 19707950939e5c057c0e97efa31789aef0c5d74e Mon Sep 17 00:00:00 2001 From: dignow Date: Thu, 3 Aug 2023 23:14:40 +0800 Subject: [PATCH 06/16] refact, separate remote window, tmp commit Signed-off-by: dignow --- flutter/lib/common.dart | 2 +- flutter/lib/consts.dart | 3 + .../desktop/pages/desktop_setting_page.dart | 19 ++++-- .../lib/desktop/pages/file_manager_page.dart | 2 +- .../lib/desktop/pages/port_forward_page.dart | 2 +- flutter/lib/desktop/pages/remote_page.dart | 16 +++-- .../lib/desktop/pages/remote_tab_page.dart | 13 ++++ flutter/lib/models/model.dart | 36 ++++++----- flutter/lib/utils/multi_window_manager.dart | 62 +++++++++++++------ src/flutter.rs | 12 ++-- 10 files changed, 115 insertions(+), 52 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 7324a8c18..f108fb5fd 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -1216,7 +1216,7 @@ FFI get gFFI => _globalFFI; Future initGlobalFFI() async { debugPrint("_globalFFI init"); - _globalFFI = FFI(); + _globalFFI = FFI(null); debugPrint("_globalFFI init end"); // after `put`, can also be globally found by Get.find(); Get.put(_globalFFI, permanent: true); diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index f945a6292..59d0576a1 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -37,6 +37,9 @@ const String kWindowEventNewFileTransfer = "new_file_transfer"; const String kWindowEventNewPortForward = "new_port_forward"; const String kWindowEventActiveSession = "active_session"; const String kWindowEventGetRemoteList = "get_remote_list"; +const String kWindowEventGetSessionIdList = "get_session_id_list"; + +const String kWindowEventCloseForSeparateWindow = "close_for_separate_window"; const String kOptionSeparateRemoteWindow = "enable-separate-remote-window"; diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 61b5ad072..75ea16335 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hbb/common.dart'; import 'package:flutter_hbb/consts.dart'; +import 'package:flutter_hbb/utils/multi_window_manager.dart'; import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart'; import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart'; import 'package:flutter_hbb/models/platform_model.dart'; @@ -248,7 +249,7 @@ class _General extends StatefulWidget { class _GeneralState extends State<_General> { final RxBool serviceStop = Get.find(tag: 'stop-service'); - RxBool serviceBtnEabled = true.obs; + RxBool serviceBtnEnabled = true.obs; @override Widget build(BuildContext context) { @@ -300,14 +301,14 @@ class _GeneralState extends State<_General> { return _Card(title: 'Service', children: [ Obx(() => _Button(serviceStop.value ? 'Start' : 'Stop', () { () async { - serviceBtnEabled.value = false; + serviceBtnEnabled.value = false; await start_service(serviceStop.value); // enable the button after 1 second Future.delayed(const Duration(seconds: 1), () { - serviceBtnEabled.value = true; + serviceBtnEnabled.value = true; }); }(); - }, enabled: serviceBtnEabled.value)) + }, enabled: serviceBtnEnabled.value)) ]); } @@ -318,7 +319,14 @@ class _GeneralState extends State<_General> { isServer: false), _OptionCheckBox(context, 'Adaptive Bitrate', 'enable-abr'), _OptionCheckBox( - context, 'Separate remote window', kOptionSeparateRemoteWindow, isServer: false), + context, + 'Separate remote window', + kOptionSeparateRemoteWindow, + isServer: false, + update: () { + rustDeskWinManager.separateWindows(); + }, + ), ]; // though this is related to GUI, but opengl problem affects all users, so put in config rather than local children.add(Tooltip( @@ -1678,7 +1686,6 @@ Widget _OptionCheckBox(BuildContext context, String label, String key, isServer ? await mainSetBoolOption(key, option) : await mainSetLocalBoolOption(key, option); - ; update?.call(); } } diff --git a/flutter/lib/desktop/pages/file_manager_page.dart b/flutter/lib/desktop/pages/file_manager_page.dart index 73d10a957..d684d1535 100644 --- a/flutter/lib/desktop/pages/file_manager_page.dart +++ b/flutter/lib/desktop/pages/file_manager_page.dart @@ -80,7 +80,7 @@ class _FileManagerPageState extends State @override void initState() { super.initState(); - _ffi = FFI(); + _ffi = FFI(null); _ffi.start(widget.id, isFileTransfer: true, password: widget.password, diff --git a/flutter/lib/desktop/pages/port_forward_page.dart b/flutter/lib/desktop/pages/port_forward_page.dart index f4b4a6c3f..2a173c53b 100644 --- a/flutter/lib/desktop/pages/port_forward_page.dart +++ b/flutter/lib/desktop/pages/port_forward_page.dart @@ -54,7 +54,7 @@ class _PortForwardPageState extends State @override void initState() { super.initState(); - _ffi = FFI(); + _ffi = FFI(null); _ffi.start(widget.id, isPortForward: true, password: widget.password, diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 7ffa527fe..ecaa1b29a 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -28,10 +28,13 @@ import '../widgets/tabbar_widget.dart'; final SimpleWrapper _firstEnterImage = SimpleWrapper(false); +final Map noCloseSessionOnDispose = {}; + class RemotePage extends StatefulWidget { RemotePage({ Key? key, required this.id, + required this.sessionId, required this.password, required this.toolbarState, required this.tabController, @@ -40,6 +43,7 @@ class RemotePage extends StatefulWidget { }) : super(key: key); final String id; + final SessionID? sessionId; final String? password; final ToolbarState toolbarState; final String? switchUuid; @@ -91,7 +95,7 @@ class _RemotePageState extends State void initState() { super.initState(); _initStates(widget.id); - _ffi = FFI(); + _ffi = FFI(widget.sessionId); Get.put(_ffi, tag: widget.id); _ffi.imageModel.addCallbackOnFirstImage((String peerId) { showKBLayoutTypeChooserIfNeeded( @@ -199,6 +203,8 @@ class _RemotePageState extends State @override Future dispose() async { + final closeSession = noCloseSessionOnDispose.remove(widget.id) ?? false; + // https://github.com/flutter/flutter/issues/64935 super.dispose(); debugPrint("REMOTE PAGE dispose session $sessionId ${widget.id}"); @@ -209,11 +215,13 @@ class _RemotePageState extends State _ffi.dialogManager.hideMobileActionsOverlay(); _ffi.recordingModel.onClose(); _rawKeyFocusNode.dispose(); - await _ffi.close(); + await _ffi.close(closeSession: closeSession); _timer?.cancel(); _ffi.dialogManager.dismissAll(); - await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, - overlays: SystemUiOverlay.values); + if (closeSession) { + await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, + overlays: SystemUiOverlay.values); + } if (!Platform.isLinux) { await Wakelock.disable(); } diff --git a/flutter/lib/desktop/pages/remote_tab_page.dart b/flutter/lib/desktop/pages/remote_tab_page.dart index 4ab731649..3e7f7f464 100644 --- a/flutter/lib/desktop/pages/remote_tab_page.dart +++ b/flutter/lib/desktop/pages/remote_tab_page.dart @@ -52,6 +52,7 @@ class _ConnectionTabPageState extends State { _toolbarState = ToolbarState(); RemoteCountState.init(); final peerId = params['id']; + final sessionId = params['session_id']; if (peerId != null) { ConnectionTypeState.init(peerId); tabController.onSelected = (id) { @@ -73,6 +74,7 @@ class _ConnectionTabPageState extends State { page: RemotePage( key: ValueKey(peerId), id: peerId, + sessionId: sessionId == null ? null : SessionID(sessionId), password: params['password'], toolbarState: _toolbarState, tabController: tabController, @@ -99,6 +101,7 @@ class _ConnectionTabPageState extends State { final args = jsonDecode(call.arguments); final id = args['id']; final switchUuid = args['switch_uuid']; + final sessionId = args['session_id']; windowOnTop(windowId()); ConnectionTypeState.init(id); _toolbarState.setShow( @@ -112,6 +115,7 @@ class _ConnectionTabPageState extends State { page: RemotePage( key: ValueKey(id), id: id, + sessionId: sessionId == null ? null : SessionID(sessionId), password: args['password'], toolbarState: _toolbarState, tabController: tabController, @@ -136,6 +140,15 @@ class _ConnectionTabPageState extends State { .map((e) => e.key) .toList() .join(','); + } else if (call.method == kWindowEventGetSessionIdList) { + return tabController.state.value.tabs + .map((e) => '${e.key},${(e.page as RemotePage).ffi.sessionId}') + .toList() + .join(';'); + } else if (call.method == kWindowEventCloseForSeparateWindow) { + final peerId = call.arguments; + noCloseSessionOnDispose[peerId] = true; + tabController.closeBy(peerId); } _update_remote_count(); }); diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 9c20a6a95..02c0b8281 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -1579,6 +1579,7 @@ class FFI { /// dialogManager use late to ensure init after main page binding [globalKey] late final dialogManager = OverlayDialogManager(); + late final bool isSessionAdded; late final SessionID sessionId; late final ImageModel imageModel; // session late final FfiModel ffiModel; // session @@ -1596,8 +1597,9 @@ class FFI { late final InputModel inputModel; // session late final ElevationModel elevationModel; // session - FFI() { - sessionId = isDesktop ? Uuid().v4obj() : _constSessionId; + FFI(SessionID? sId) { + isSessionAdded = sId != null; + sessionId = sId ?? (isDesktop ? Uuid().v4obj() : _constSessionId); imageModel = ImageModel(WeakReference(this)); ffiModel = FfiModel(WeakReference(this)); cursorModel = CursorModel(WeakReference(this)); @@ -1637,17 +1639,19 @@ class FFI { imageModel.id = id; cursorModel.id = id; } - // ignore: unused_local_variable - final addRes = bind.sessionAddSync( - sessionId: sessionId, - id: id, - isFileTransfer: isFileTransfer, - isPortForward: isPortForward, - isRdp: isRdp, - switchUuid: switchUuid ?? "", - forceRelay: forceRelay ?? false, - password: password ?? "", - ); + if (isSessionAdded) { + // ignore: unused_local_variable + final addRes = bind.sessionAddSync( + sessionId: sessionId, + id: id, + isFileTransfer: isFileTransfer, + isPortForward: isPortForward, + isRdp: isRdp, + switchUuid: switchUuid ?? "", + forceRelay: forceRelay ?? false, + password: password ?? "", + ); + } final stream = bind.sessionStart(sessionId: sessionId, id: id); final cb = ffiModel.startEventListener(sessionId, id); final useTextureRender = bind.mainUseTextureRender(); @@ -1712,7 +1716,7 @@ class FFI { } /// Close the remote session. - Future close() async { + Future close({bool closeSession = true}) async { closed = true; chatModel.close(); if (imageModel.image != null && !isWebDesktop) { @@ -1730,7 +1734,9 @@ class FFI { ffiModel.clear(); canvasModel.clear(); inputModel.resetModifiers(); - await bind.sessionClose(sessionId: sessionId); + if (closeSession) { + await bind.sessionClose(sessionId: sessionId); + } debugPrint('model $id closed'); id = ''; } diff --git a/flutter/lib/utils/multi_window_manager.dart b/flutter/lib/utils/multi_window_manager.dart index 720b7dc95..d0c89cf3b 100644 --- a/flutter/lib/utils/multi_window_manager.dart +++ b/flutter/lib/utils/multi_window_manager.dart @@ -43,6 +43,46 @@ class RustDeskMultiWindowManager { final List _fileTransferWindows = List.empty(growable: true); final List _portForwardWindows = List.empty(growable: true); + separateWindows() async { + for (final windowId in _remoteDesktopWindows) { + final sessionIdList = await DesktopMultiWindow.invokeMethod( + windowId, kWindowEventGetSessionIdList, null); + if (sessionIdList != null) { + for (final idPair in sessionIdList.split(';')) { + final peerSession = idPair.split(','); + var params = { + 'type': WindowType.RemoteDesktop.index, + 'id': peerSession[0], + 'sessionId': peerSession[1], + }; + await newSessionWindow(WindowType.RemoteDesktop, peerSession[0], + jsonEncode(params), _remoteDesktopWindows); + await DesktopMultiWindow.invokeMethod( + windowId, kWindowEventCloseForSeparateWindow, peerSession[0]); + } + } + } + } + + newSessionWindow( + WindowType type, String remoteId, String msg, List windows) async { + final windowController = await DesktopMultiWindow.createWindow(msg); + windowController + ..setFrame(const Offset(0, 0) & + Size(1280 + windowController.windowId * 20, + 720 + windowController.windowId * 20)) + ..center() + ..setTitle(getWindowNameWithId( + remoteId, + overrideType: type, + )); + if (Platform.isMacOS) { + Future.microtask(() => windowController.show()); + } + registerActiveWindow(windowController.windowId); + windows.add(windowController.windowId); + } + Future newSession( WindowType type, String methodName, @@ -68,24 +108,6 @@ class RustDeskMultiWindowManager { } final msg = jsonEncode(params); - newSessionWindow() async { - final windowController = await DesktopMultiWindow.createWindow(msg); - windowController - ..setFrame(const Offset(0, 0) & - Size(1280 + windowController.windowId * 20, - 720 + windowController.windowId * 20)) - ..center() - ..setTitle(getWindowNameWithId( - remoteId, - overrideType: type, - )); - if (Platform.isMacOS) { - Future.microtask(() => windowController.show()); - } - registerActiveWindow(windowController.windowId); - windows.add(windowController.windowId); - } - // separate window for file transfer is not supported bool separateWindow = forceSeparateWindow || (type != WindowType.FileTransfer && @@ -111,11 +133,11 @@ class RustDeskMultiWindowManager { windows.add(windowController.windowId); return invokeRes; } else { - await newSessionWindow(); + await newSessionWindow(type, remoteId, msg, windows); } } else { if (windows.isEmpty) { - await newSessionWindow(); + await newSessionWindow(type, remoteId, msg, windows); } else { return call(type, methodName, msg); } diff --git a/src/flutter.rs b/src/flutter.rs index c35f7340d..f5711b823 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -782,11 +782,15 @@ pub fn session_start_( ); #[cfg(not(feature = "flutter_texture_render"))] log::info!("Session {} start, render by flutter paint widget", id); + let is_pre_added = session.event_stream.read().unwrap().is_some(); + session.close_event_stream(); *session.event_stream.write().unwrap() = Some(event_stream); - let session = session.clone(); - std::thread::spawn(move || { - io_loop(session); - }); + if !is_pre_added { + let session = session.clone(); + std::thread::spawn(move || { + io_loop(session); + }); + } Ok(()) } else { bail!("No session with peer id {}", id) From e121ed8ecb5508dd2752783cc19722f7578077cf Mon Sep 17 00:00:00 2001 From: dignow Date: Thu, 3 Aug 2023 23:40:54 +0800 Subject: [PATCH 07/16] refact, separate remote window, debug Signed-off-by: dignow --- flutter/lib/models/model.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 02c0b8281..50457b195 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -1639,7 +1639,7 @@ class FFI { imageModel.id = id; cursorModel.id = id; } - if (isSessionAdded) { + if (!isSessionAdded) { // ignore: unused_local_variable final addRes = bind.sessionAddSync( sessionId: sessionId, @@ -1647,9 +1647,9 @@ class FFI { isFileTransfer: isFileTransfer, isPortForward: isPortForward, isRdp: isRdp, - switchUuid: switchUuid ?? "", + switchUuid: switchUuid ?? '', forceRelay: forceRelay ?? false, - password: password ?? "", + password: password ?? '', ); } final stream = bind.sessionStart(sessionId: sessionId, id: id); From e4ac34da0d87bc9210bf1361b45a73894c6b5bee Mon Sep 17 00:00:00 2001 From: dignow Date: Fri, 4 Aug 2023 00:09:22 +0800 Subject: [PATCH 08/16] refact, separate remote window, debug Signed-off-by: dignow --- flutter/lib/desktop/pages/desktop_setting_page.dart | 10 ++++++++-- flutter/lib/utils/multi_window_manager.dart | 8 ++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 75ea16335..9072643ea 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -324,7 +324,11 @@ class _GeneralState extends State<_General> { kOptionSeparateRemoteWindow, isServer: false, update: () { - rustDeskWinManager.separateWindows(); + final useSeparateWindow = + mainGetLocalBoolOptionSync(kOptionSeparateRemoteWindow); + if (useSeparateWindow) { + rustDeskWinManager.separateWindows(); + } }, ), ]; @@ -1681,11 +1685,13 @@ Widget _OptionCheckBox(BuildContext context, String label, String key, var ref = value.obs; onChanged(option) async { if (option != null) { - ref.value = option; if (reverse) option = !option; isServer ? await mainSetBoolOption(key, option) : await mainSetLocalBoolOption(key, option); + ref.value = isServer + ? mainGetBoolOptionSync(key) + : mainGetLocalBoolOptionSync(key); update?.call(); } } diff --git a/flutter/lib/utils/multi_window_manager.dart b/flutter/lib/utils/multi_window_manager.dart index d0c89cf3b..d759477d2 100644 --- a/flutter/lib/utils/multi_window_manager.dart +++ b/flutter/lib/utils/multi_window_manager.dart @@ -48,7 +48,11 @@ class RustDeskMultiWindowManager { final sessionIdList = await DesktopMultiWindow.invokeMethod( windowId, kWindowEventGetSessionIdList, null); if (sessionIdList != null) { - for (final idPair in sessionIdList.split(';')) { + final idList = sessionIdList.split(';') as List; + if (idList.length <= 1) { + continue; + } + for (final idPair in idList.sublist(1)) { final peerSession = idPair.split(','); var params = { 'type': WindowType.RemoteDesktop.index, @@ -58,7 +62,7 @@ class RustDeskMultiWindowManager { await newSessionWindow(WindowType.RemoteDesktop, peerSession[0], jsonEncode(params), _remoteDesktopWindows); await DesktopMultiWindow.invokeMethod( - windowId, kWindowEventCloseForSeparateWindow, peerSession[0]); + windowId, kWindowEventCloseForSeparateWindow, peerSession[0]); } } } From e12d0ef4aa017d7b12af5d4aa51dbc791c299ec0 Mon Sep 17 00:00:00 2001 From: dignow Date: Fri, 4 Aug 2023 00:17:11 +0800 Subject: [PATCH 09/16] tmp commit Signed-off-by: dignow --- flutter/lib/utils/multi_window_manager.dart | 91 ++++++++++++--------- 1 file changed, 53 insertions(+), 38 deletions(-) diff --git a/flutter/lib/utils/multi_window_manager.dart b/flutter/lib/utils/multi_window_manager.dart index d759477d2..974dfd9c4 100644 --- a/flutter/lib/utils/multi_window_manager.dart +++ b/flutter/lib/utils/multi_window_manager.dart @@ -45,25 +45,29 @@ class RustDeskMultiWindowManager { separateWindows() async { for (final windowId in _remoteDesktopWindows) { - final sessionIdList = await DesktopMultiWindow.invokeMethod( + final String sessionIdList = await DesktopMultiWindow.invokeMethod( windowId, kWindowEventGetSessionIdList, null); - if (sessionIdList != null) { - final idList = sessionIdList.split(';') as List; - if (idList.length <= 1) { - continue; - } - for (final idPair in idList.sublist(1)) { - final peerSession = idPair.split(','); - var params = { - 'type': WindowType.RemoteDesktop.index, - 'id': peerSession[0], - 'sessionId': peerSession[1], - }; - await newSessionWindow(WindowType.RemoteDesktop, peerSession[0], - jsonEncode(params), _remoteDesktopWindows); - await DesktopMultiWindow.invokeMethod( - windowId, kWindowEventCloseForSeparateWindow, peerSession[0]); - } + final idList = sessionIdList.split(';'); + if (idList.length <= 1) { + continue; + } + for (final idPair in idList.sublist(1)) { + final peerSession = idPair.split(','); + var params = { + 'type': WindowType.RemoteDesktop.index, + 'id': peerSession[0], + 'sessionId': peerSession[1], + }; + await _newSession( + true, + WindowType.RemoteDesktop, + kWindowEventNewRemoteDesktop, + peerSession[0], + _remoteDesktopWindows, + jsonEncode(params), + ); + await DesktopMultiWindow.invokeMethod( + windowId, kWindowEventCloseForSeparateWindow, peerSession[0]); } } } @@ -87,6 +91,36 @@ class RustDeskMultiWindowManager { windows.add(windowController.windowId); } + _newSession( + bool separateWindow, + WindowType type, + String methodName, + String remoteId, + List windows, + String msg, + ) async { + if (separateWindow) { + if (kCloseMultiWindowByHide && _inactiveWindows.isNotEmpty) { + final windowId = _inactiveWindows.first; + final invokeRes = + await DesktopMultiWindow.invokeMethod(windowId, methodName, msg); + final windowController = WindowController.fromWindowId(windowId); + windowController.show(); + registerActiveWindow(windowController.windowId); + windows.add(windowController.windowId); + return invokeRes; + } else { + await newSessionWindow(type, remoteId, msg, windows); + } + } else { + if (windows.isEmpty) { + await newSessionWindow(type, remoteId, msg, windows); + } else { + return call(type, methodName, msg); + } + } + } + Future newSession( WindowType type, String methodName, @@ -126,26 +160,7 @@ class RustDeskMultiWindowManager { } } - if (separateWindow) { - if (kCloseMultiWindowByHide && _inactiveWindows.isNotEmpty) { - final windowId = _inactiveWindows.first; - final invokeRes = - await DesktopMultiWindow.invokeMethod(windowId, methodName, msg); - final windowController = WindowController.fromWindowId(windowId); - windowController.show(); - registerActiveWindow(windowController.windowId); - windows.add(windowController.windowId); - return invokeRes; - } else { - await newSessionWindow(type, remoteId, msg, windows); - } - } else { - if (windows.isEmpty) { - await newSessionWindow(type, remoteId, msg, windows); - } else { - return call(type, methodName, msg); - } - } + await _newSession(separateWindow, type, methodName, remoteId, windows, msg); } Future newRemoteDesktop( From 53e87352daea95fb6c46eecfe40b17a884f08961 Mon Sep 17 00:00:00 2001 From: dignow Date: Fri, 4 Aug 2023 01:41:36 +0800 Subject: [PATCH 10/16] tmp commit Signed-off-by: dignow --- flutter/lib/desktop/pages/remote_page.dart | 4 ++-- flutter/lib/desktop/pages/remote_tab_page.dart | 2 +- flutter/lib/models/model.dart | 4 ++++ flutter/lib/utils/multi_window_manager.dart | 4 ++-- src/client/io_loop.rs | 8 ++++++++ src/flutter_ffi.rs | 9 ++++++++- src/ui_session_interface.rs | 6 +++++- 7 files changed, 30 insertions(+), 7 deletions(-) diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index ecaa1b29a..32faf08ae 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -28,7 +28,7 @@ import '../widgets/tabbar_widget.dart'; final SimpleWrapper _firstEnterImage = SimpleWrapper(false); -final Map noCloseSessionOnDispose = {}; +final Map closeSessionOnDispose = {}; class RemotePage extends StatefulWidget { RemotePage({ @@ -203,7 +203,7 @@ class _RemotePageState extends State @override Future dispose() async { - final closeSession = noCloseSessionOnDispose.remove(widget.id) ?? false; + final closeSession = closeSessionOnDispose.remove(widget.id) ?? true; // https://github.com/flutter/flutter/issues/64935 super.dispose(); diff --git a/flutter/lib/desktop/pages/remote_tab_page.dart b/flutter/lib/desktop/pages/remote_tab_page.dart index 3e7f7f464..080895729 100644 --- a/flutter/lib/desktop/pages/remote_tab_page.dart +++ b/flutter/lib/desktop/pages/remote_tab_page.dart @@ -147,7 +147,7 @@ class _ConnectionTabPageState extends State { .join(';'); } else if (call.method == kWindowEventCloseForSeparateWindow) { final peerId = call.arguments; - noCloseSessionOnDispose[peerId] = true; + closeSessionOnDispose[peerId] = false; tabController.closeBy(peerId); } _update_remote_count(); diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 50457b195..2bf08c53f 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -1702,6 +1702,10 @@ class FFI { }); // every instance will bind a stream this.id = id; + + if (isSessionAdded) { + bind.sessionHandlePeerInfo(sessionId: sessionId); + } } /// Login with [password], choose if the client should [remember] it. diff --git a/flutter/lib/utils/multi_window_manager.dart b/flutter/lib/utils/multi_window_manager.dart index 974dfd9c4..2a7b89bf0 100644 --- a/flutter/lib/utils/multi_window_manager.dart +++ b/flutter/lib/utils/multi_window_manager.dart @@ -44,7 +44,7 @@ class RustDeskMultiWindowManager { final List _portForwardWindows = List.empty(growable: true); separateWindows() async { - for (final windowId in _remoteDesktopWindows) { + for (final windowId in _remoteDesktopWindows.toList()) { final String sessionIdList = await DesktopMultiWindow.invokeMethod( windowId, kWindowEventGetSessionIdList, null); final idList = sessionIdList.split(';'); @@ -56,7 +56,7 @@ class RustDeskMultiWindowManager { var params = { 'type': WindowType.RemoteDesktop.index, 'id': peerSession[0], - 'sessionId': peerSession[1], + 'session_id': peerSession[1], }; await _newSession( true, diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 82c3794bb..6236aa838 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -1008,6 +1008,10 @@ impl Remote { } } Some(login_response::Union::PeerInfo(pi)) => { + #[cfg(feature = "flutter")] + { + self.handler.pi = pi.clone(); + } self.handler.handle_peer_info(pi); #[cfg(not(feature = "flutter"))] self.check_clipboard_file_context(); @@ -1274,6 +1278,10 @@ impl Remote { } } Some(misc::Union::SwitchDisplay(s)) => { + #[cfg(feature = "flutter")] + { + self.handler.switch_display = s.clone(); + } self.handler.handle_peer_switch_display(&s); self.video_sender.send(MediaData::Reset).ok(); if s.width > 0 && s.height > 0 { diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 5df00c344..c3df147b9 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1,7 +1,7 @@ #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::common::get_default_sound_input; use crate::{ - client::file_trait::FileManager, + client::{file_trait::FileManager, Interface}, common::is_keyboard_mode_supported, common::make_fd_to_json, flutter::{self, SESSIONS}, @@ -601,6 +601,13 @@ pub fn session_change_resolution(session_id: SessionID, display: i32, width: i32 } } +pub fn session_handle_peer_info(session_id: SessionID) { + if let Some(session) = SESSIONS.write().unwrap().get_mut(&session_id) { + session.handle_peer_info(session.pi.clone()); + session.handle_peer_switch_display(&session.switch_display); + } +} + pub fn session_set_size(_session_id: SessionID, _width: usize, _height: usize) { #[cfg(feature = "flutter_texture_render")] if let Some(session) = SESSIONS.write().unwrap().get_mut(&_session_id) { diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 33cfa2ced..740929c39 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -62,6 +62,10 @@ pub struct Session { pub server_file_transfer_enabled: Arc>, pub server_clipboard_enabled: Arc>, pub last_change_display: Arc>, + #[cfg(feature = "flutter")] + pub pi: PeerInfo, + #[cfg(feature = "flutter")] + pub switch_display: SwitchDisplay, } #[derive(Clone)] @@ -1186,7 +1190,7 @@ impl Session { #[tokio::main(flavor = "current_thread")] pub async fn io_loop(handler: Session) { // It is ok to call this function multiple times. - #[cfg(target_os ="windows")] + #[cfg(target_os = "windows")] if !handler.is_file_transfer() && !handler.is_port_forward() { clipboard::ContextSend::enable(true); } From fd12f69afac6ea34a7117f94b2ec73f6a1ad7826 Mon Sep 17 00:00:00 2001 From: dignow Date: Fri, 4 Aug 2023 20:22:54 +0800 Subject: [PATCH 11/16] tmp commit Signed-off-by: dignow --- flutter/lib/desktop/pages/desktop_setting_page.dart | 4 ++-- flutter/lib/models/model.dart | 10 ++++++---- flutter/lib/utils/multi_window_manager.dart | 9 +++++---- src/client/io_loop.rs | 4 ++-- src/flutter_ffi.rs | 5 +++-- src/ui_session_interface.rs | 4 ++-- 6 files changed, 20 insertions(+), 16 deletions(-) diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 9072643ea..c84f126bd 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -326,9 +326,9 @@ class _GeneralState extends State<_General> { update: () { final useSeparateWindow = mainGetLocalBoolOptionSync(kOptionSeparateRemoteWindow); - if (useSeparateWindow) { + // if (useSeparateWindow) { rustDeskWinManager.separateWindows(); - } + // } }, ), ]; diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 2bf08c53f..1a24067a0 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -1655,9 +1655,15 @@ class FFI { final stream = bind.sessionStart(sessionId: sessionId, id: id); final cb = ffiModel.startEventListener(sessionId, id); final useTextureRender = bind.mainUseTextureRender(); + + final SimpleWrapper isPeerInfoSent = SimpleWrapper(false); // Preserved for the rgba data. stream.listen((message) { if (closed) return; + if (isSessionAdded && !isPeerInfoSent.value) { + bind.sessionHandlePeerInfo(sessionId: sessionId); + isPeerInfoSent.value = true; + } () async { if (message is EventToUI_Event) { if (message.field0 == "close") { @@ -1702,10 +1708,6 @@ class FFI { }); // every instance will bind a stream this.id = id; - - if (isSessionAdded) { - bind.sessionHandlePeerInfo(sessionId: sessionId); - } } /// Login with [password], choose if the client should [remember] it. diff --git a/flutter/lib/utils/multi_window_manager.dart b/flutter/lib/utils/multi_window_manager.dart index 2a7b89bf0..939bc7934 100644 --- a/flutter/lib/utils/multi_window_manager.dart +++ b/flutter/lib/utils/multi_window_manager.dart @@ -7,6 +7,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/common.dart'; +import 'package:flutter_hbb/models/platform_model.dart'; /// must keep the order enum WindowType { Main, RemoteDesktop, FileTransfer, PortForward, Unknown } @@ -48,10 +49,10 @@ class RustDeskMultiWindowManager { final String sessionIdList = await DesktopMultiWindow.invokeMethod( windowId, kWindowEventGetSessionIdList, null); final idList = sessionIdList.split(';'); - if (idList.length <= 1) { - continue; - } - for (final idPair in idList.sublist(1)) { + // if (idList.length <= 1) { + // continue; + // } + for (final idPair in idList) { final peerSession = idPair.split(','); var params = { 'type': WindowType.RemoteDesktop.index, diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 6236aa838..f50dc4737 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -1010,7 +1010,7 @@ impl Remote { Some(login_response::Union::PeerInfo(pi)) => { #[cfg(feature = "flutter")] { - self.handler.pi = pi.clone(); + *self.handler.pi.write().unwrap() = pi.clone(); } self.handler.handle_peer_info(pi); #[cfg(not(feature = "flutter"))] @@ -1280,7 +1280,7 @@ impl Remote { Some(misc::Union::SwitchDisplay(s)) => { #[cfg(feature = "flutter")] { - self.handler.switch_display = s.clone(); + *self.handler.switch_display.write().unwrap() = s.clone(); } self.handler.handle_peer_switch_display(&s); self.video_sender.send(MediaData::Reset).ok(); diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index c3df147b9..c0529171a 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -603,8 +603,9 @@ pub fn session_change_resolution(session_id: SessionID, display: i32, width: i32 pub fn session_handle_peer_info(session_id: SessionID) { if let Some(session) = SESSIONS.write().unwrap().get_mut(&session_id) { - session.handle_peer_info(session.pi.clone()); - session.handle_peer_switch_display(&session.switch_display); + let pi = session.pi.read().unwrap().clone(); + session.handle_peer_info(pi); + session.handle_peer_switch_display(&session.switch_display.read().unwrap()); } } diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 740929c39..32f8fe5bf 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -63,9 +63,9 @@ pub struct Session { pub server_clipboard_enabled: Arc>, pub last_change_display: Arc>, #[cfg(feature = "flutter")] - pub pi: PeerInfo, + pub pi: Arc>, #[cfg(feature = "flutter")] - pub switch_display: SwitchDisplay, + pub switch_display: Arc>, } #[derive(Clone)] From 01ab0cf1963ac842cb5b7ee07867a2ad16d1c7b5 Mon Sep 17 00:00:00 2001 From: dignow Date: Fri, 4 Aug 2023 20:58:14 +0800 Subject: [PATCH 12/16] tmp commit Signed-off-by: dignow --- .../desktop/pages/desktop_setting_page.dart | 4 ++-- flutter/lib/models/model.dart | 8 +++---- flutter/lib/utils/multi_window_manager.dart | 8 +++---- src/client/io_loop.rs | 15 ++++++++++-- src/flutter_ffi.rs | 8 +++---- src/ui_session_interface.rs | 24 ++++++++++++++++--- 6 files changed, 47 insertions(+), 20 deletions(-) diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index c84f126bd..9072643ea 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -326,9 +326,9 @@ class _GeneralState extends State<_General> { update: () { final useSeparateWindow = mainGetLocalBoolOptionSync(kOptionSeparateRemoteWindow); - // if (useSeparateWindow) { + if (useSeparateWindow) { rustDeskWinManager.separateWindows(); - // } + } }, ), ]; diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 1a24067a0..a77b3b238 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -1656,13 +1656,13 @@ class FFI { final cb = ffiModel.startEventListener(sessionId, id); final useTextureRender = bind.mainUseTextureRender(); - final SimpleWrapper isPeerInfoSent = SimpleWrapper(false); + final SimpleWrapper isCacheRestored = SimpleWrapper(false); // Preserved for the rgba data. stream.listen((message) { if (closed) return; - if (isSessionAdded && !isPeerInfoSent.value) { - bind.sessionHandlePeerInfo(sessionId: sessionId); - isPeerInfoSent.value = true; + if (isSessionAdded && !isCacheRestored.value) { + bind.sessionRestoreCache(sessionId: sessionId); + isCacheRestored.value = true; } () async { if (message is EventToUI_Event) { diff --git a/flutter/lib/utils/multi_window_manager.dart b/flutter/lib/utils/multi_window_manager.dart index 939bc7934..8c4a5303c 100644 --- a/flutter/lib/utils/multi_window_manager.dart +++ b/flutter/lib/utils/multi_window_manager.dart @@ -49,10 +49,10 @@ class RustDeskMultiWindowManager { final String sessionIdList = await DesktopMultiWindow.invokeMethod( windowId, kWindowEventGetSessionIdList, null); final idList = sessionIdList.split(';'); - // if (idList.length <= 1) { - // continue; - // } - for (final idPair in idList) { + if (idList.length <= 1) { + continue; + } + for (final idPair in idList.sublist(1)) { final peerSession = idPair.split(','); var params = { 'type': WindowType.RemoteDesktop.index, diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index f50dc4737..7c0b3a193 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -1010,7 +1010,7 @@ impl Remote { Some(login_response::Union::PeerInfo(pi)) => { #[cfg(feature = "flutter")] { - *self.handler.pi.write().unwrap() = pi.clone(); + self.handler.cache_flutter.write().unwrap().pi = pi.clone(); } self.handler.handle_peer_info(pi); #[cfg(not(feature = "flutter"))] @@ -1059,9 +1059,20 @@ impl Remote { _ => {} }, Some(message::Union::CursorData(cd)) => { + #[cfg(feature = "flutter")] + { + let mut lock = self.handler.cache_flutter.write().unwrap(); + if !lock.cursor_data.contains_key(&cd.id) { + lock.cursor_data.insert(cd.id, cd.clone()); + } + } self.handler.set_cursor_data(cd); } Some(message::Union::CursorId(id)) => { + #[cfg(feature = "flutter")] + { + self.handler.cache_flutter.write().unwrap().cursor_id = id; + } self.handler.set_cursor_id(id.to_string()); } Some(message::Union::CursorPosition(cp)) => { @@ -1280,7 +1291,7 @@ impl Remote { Some(misc::Union::SwitchDisplay(s)) => { #[cfg(feature = "flutter")] { - *self.handler.switch_display.write().unwrap() = s.clone(); + self.handler.cache_flutter.write().unwrap().sp = s.clone(); } self.handler.handle_peer_switch_display(&s); self.video_sender.send(MediaData::Reset).ok(); diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index c0529171a..c0e96a487 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1,7 +1,7 @@ #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::common::get_default_sound_input; use crate::{ - client::{file_trait::FileManager, Interface}, + client::file_trait::FileManager, common::is_keyboard_mode_supported, common::make_fd_to_json, flutter::{self, SESSIONS}, @@ -601,11 +601,9 @@ pub fn session_change_resolution(session_id: SessionID, display: i32, width: i32 } } -pub fn session_handle_peer_info(session_id: SessionID) { +pub fn session_restore_cache(session_id: SessionID) { if let Some(session) = SESSIONS.write().unwrap().get_mut(&session_id) { - let pi = session.pi.read().unwrap().clone(); - session.handle_peer_info(pi); - session.handle_peer_switch_display(&session.switch_display.read().unwrap()); + session.restore_flutter_cache(); } } diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 32f8fe5bf..50bc23339 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -48,6 +48,15 @@ pub static IS_IN: AtomicBool = AtomicBool::new(false); const CHANGE_RESOLUTION_VALID_TIMEOUT_SECS: u64 = 15; +#[cfg(feature = "flutter")] +#[derive(Default)] +pub struct CacheFlutter { + pub pi: PeerInfo, + pub sp: SwitchDisplay, + pub cursor_data: HashMap, + pub cursor_id: u64, +} + #[derive(Clone, Default)] pub struct Session { pub session_id: SessionID, // different from the one in LoginConfigHandler, used for flutter UI message pass @@ -63,9 +72,7 @@ pub struct Session { pub server_clipboard_enabled: Arc>, pub last_change_display: Arc>, #[cfg(feature = "flutter")] - pub pi: Arc>, - #[cfg(feature = "flutter")] - pub switch_display: Arc>, + pub cache_flutter: Arc>, } #[derive(Clone)] @@ -1185,6 +1192,17 @@ impl Session { pub fn ctrl_alt_del(&self) { self.send_key_event(&crate::keyboard::client::event_ctrl_alt_del()); } + + #[cfg(feature = "flutter")] + pub fn restore_flutter_cache(&mut self) { + let pi = self.cache_flutter.read().unwrap().pi.clone(); + self.handle_peer_info(pi); + self.handle_peer_switch_display(&self.cache_flutter.read().unwrap().sp); + for (_, cd) in self.cache_flutter.read().unwrap().cursor_data.iter() { + self.set_cursor_data(cd.clone()); + } + self.set_cursor_id(self.cache_flutter.read().unwrap().cursor_id.to_string()); + } } #[tokio::main(flavor = "current_thread")] From 30c2c92970053ad52a026f328d18710e029a8bdb Mon Sep 17 00:00:00 2001 From: dignow Date: Sat, 5 Aug 2023 15:02:35 +0800 Subject: [PATCH 13/16] refact, separate window, debug Signed-off-by: dignow --- src/client/io_loop.rs | 7 ++++++- src/ui_session_interface.rs | 6 ++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 7c0b3a193..400aff2d1 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -1291,7 +1291,12 @@ impl Remote { Some(misc::Union::SwitchDisplay(s)) => { #[cfg(feature = "flutter")] { - self.handler.cache_flutter.write().unwrap().sp = s.clone(); + self.handler + .cache_flutter + .write() + .unwrap() + .sp + .replace(s.clone()); } self.handler.handle_peer_switch_display(&s); self.video_sender.send(MediaData::Reset).ok(); diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 50bc23339..1e0f6b1b9 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -52,7 +52,7 @@ const CHANGE_RESOLUTION_VALID_TIMEOUT_SECS: u64 = 15; #[derive(Default)] pub struct CacheFlutter { pub pi: PeerInfo, - pub sp: SwitchDisplay, + pub sp: Option, pub cursor_data: HashMap, pub cursor_id: u64, } @@ -1197,7 +1197,9 @@ impl Session { pub fn restore_flutter_cache(&mut self) { let pi = self.cache_flutter.read().unwrap().pi.clone(); self.handle_peer_info(pi); - self.handle_peer_switch_display(&self.cache_flutter.read().unwrap().sp); + if let Some(sp) = self.cache_flutter.read().unwrap().sp.as_ref() { + self.handle_peer_switch_display(sp); + } for (_, cd) in self.cache_flutter.read().unwrap().cursor_data.iter() { self.set_cursor_data(cd.clone()); } From bf42d66171eaa0e7c0e7a9ca44b774f6a5aa9e9c Mon Sep 17 00:00:00 2001 From: dignow Date: Sat, 5 Aug 2023 17:57:12 +0800 Subject: [PATCH 14/16] refact, separate remote window Signed-off-by: dignow --- flutter/lib/utils/multi_window_manager.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/flutter/lib/utils/multi_window_manager.dart b/flutter/lib/utils/multi_window_manager.dart index 8c4a5303c..b0959246b 100644 --- a/flutter/lib/utils/multi_window_manager.dart +++ b/flutter/lib/utils/multi_window_manager.dart @@ -7,7 +7,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/common.dart'; -import 'package:flutter_hbb/models/platform_model.dart'; /// must keep the order enum WindowType { Main, RemoteDesktop, FileTransfer, PortForward, Unknown } @@ -213,6 +212,11 @@ class RustDeskMultiWindowManager { if (wnds.isEmpty) { return; } + for (final windowId in wnds) { + if (_activeWindows.contains(windowId)) { + return await DesktopMultiWindow.invokeMethod(windowId, methodName, args); + } + } return await DesktopMultiWindow.invokeMethod(wnds[0], methodName, args); } From 5817e48e1785967c6e646a516b1e08e50ab7dc39 Mon Sep 17 00:00:00 2001 From: dignow Date: Sat, 5 Aug 2023 19:20:20 +0800 Subject: [PATCH 15/16] fix build Signed-off-by: dignow --- src/client/io_loop.rs | 4 ++++ src/flutter_ffi.rs | 1 + src/ui_session_interface.rs | 3 +++ 3 files changed, 8 insertions(+) diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 400aff2d1..eee857f60 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -1009,6 +1009,7 @@ impl Remote { } Some(login_response::Union::PeerInfo(pi)) => { #[cfg(feature = "flutter")] + #[cfg(not(any(target_os = "android", target_os = "ios")))] { self.handler.cache_flutter.write().unwrap().pi = pi.clone(); } @@ -1060,6 +1061,7 @@ impl Remote { }, Some(message::Union::CursorData(cd)) => { #[cfg(feature = "flutter")] + #[cfg(not(any(target_os = "android", target_os = "ios")))] { let mut lock = self.handler.cache_flutter.write().unwrap(); if !lock.cursor_data.contains_key(&cd.id) { @@ -1070,6 +1072,7 @@ impl Remote { } Some(message::Union::CursorId(id)) => { #[cfg(feature = "flutter")] + #[cfg(not(any(target_os = "android", target_os = "ios")))] { self.handler.cache_flutter.write().unwrap().cursor_id = id; } @@ -1290,6 +1293,7 @@ impl Remote { } Some(misc::Union::SwitchDisplay(s)) => { #[cfg(feature = "flutter")] + #[cfg(not(any(target_os = "android", target_os = "ios")))] { self.handler .cache_flutter diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index c0e96a487..05819dfd8 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -602,6 +602,7 @@ pub fn session_change_resolution(session_id: SessionID, display: i32, width: i32 } pub fn session_restore_cache(session_id: SessionID) { + #[cfg(not(any(target_os = "android", target_os = "ios")))] if let Some(session) = SESSIONS.write().unwrap().get_mut(&session_id) { session.restore_flutter_cache(); } diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 1e0f6b1b9..21b9c9d9e 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -49,6 +49,7 @@ pub static IS_IN: AtomicBool = AtomicBool::new(false); const CHANGE_RESOLUTION_VALID_TIMEOUT_SECS: u64 = 15; #[cfg(feature = "flutter")] +#[cfg(not(any(target_os = "android", target_os = "ios")))] #[derive(Default)] pub struct CacheFlutter { pub pi: PeerInfo, @@ -72,6 +73,7 @@ pub struct Session { pub server_clipboard_enabled: Arc>, pub last_change_display: Arc>, #[cfg(feature = "flutter")] + #[cfg(not(any(target_os = "android", target_os = "ios")))] pub cache_flutter: Arc>, } @@ -1194,6 +1196,7 @@ impl Session { } #[cfg(feature = "flutter")] + #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn restore_flutter_cache(&mut self) { let pi = self.cache_flutter.read().unwrap().pi.clone(); self.handle_peer_info(pi); From 2db8f1f9d1c52166f9fd859f9ca0250a3711324a Mon Sep 17 00:00:00 2001 From: dignow Date: Sat, 5 Aug 2023 19:48:32 +0800 Subject: [PATCH 16/16] refact, separate remote window Signed-off-by: dignow --- flutter/lib/models/model.dart | 8 ++++---- src/flutter_ffi.rs | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index a77b3b238..ba6c50ac0 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -1656,13 +1656,13 @@ class FFI { final cb = ffiModel.startEventListener(sessionId, id); final useTextureRender = bind.mainUseTextureRender(); - final SimpleWrapper isCacheRestored = SimpleWrapper(false); + final SimpleWrapper isToNewWindowNotified = SimpleWrapper(false); // Preserved for the rgba data. stream.listen((message) { if (closed) return; - if (isSessionAdded && !isCacheRestored.value) { - bind.sessionRestoreCache(sessionId: sessionId); - isCacheRestored.value = true; + if (isSessionAdded && !isToNewWindowNotified.value) { + bind.sessionReadyToNewWindow(sessionId: sessionId); + isToNewWindowNotified.value = true; } () async { if (message is EventToUI_Event) { diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 05819dfd8..0a93ac58c 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -601,10 +601,11 @@ pub fn session_change_resolution(session_id: SessionID, display: i32, width: i32 } } -pub fn session_restore_cache(session_id: SessionID) { +pub fn session_ready_to_new_window(session_id: SessionID) { #[cfg(not(any(target_os = "android", target_os = "ios")))] if let Some(session) = SESSIONS.write().unwrap().get_mut(&session_id) { session.restore_flutter_cache(); + session.refresh_video(); } }