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")))]