refact, separate remote window

Signed-off-by: dignow <linlong1265@gmail.com>
This commit is contained in:
dignow 2023-08-01 22:19:38 +08:00
parent 72c198a1e9
commit f495bf105f
12 changed files with 297 additions and 233 deletions

View file

@ -19,7 +19,6 @@ import 'package:flutter_hbb/utils/multi_window_manager.dart';
import 'package:flutter_hbb/utils/platform_channel.dart'; import 'package:flutter_hbb/utils/platform_channel.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:texture_rgba_renderer/texture_rgba_renderer.dart';
import 'package:uni_links/uni_links.dart'; import 'package:uni_links/uni_links.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
@ -47,11 +46,6 @@ var isMobile = isAndroid || isIOS;
var version = ""; var version = "";
int androidVersion = 0; int androidVersion = 0;
/// Incriment count for textureId.
int _textureId = 0;
int get newTextureId => _textureId++;
final textureRenderer = TextureRgbaRenderer();
/// only available for Windows target /// only available for Windows target
int windowsBuildNumber = 0; int windowsBuildNumber = 0;
DesktopType? desktopType; DesktopType? desktopType;
@ -1698,7 +1692,7 @@ bool handleUriLink({List<String>? cmdArgs, Uri? uri, String? uriString}) {
Future.delayed(Duration.zero, () { Future.delayed(Duration.zero, () {
rustDeskWinManager.newRemoteDesktop(id!, rustDeskWinManager.newRemoteDesktop(id!,
password: password, password: password,
switch_uuid: switchUuid, switchUuid: switchUuid,
forceRelay: forceRelay); forceRelay: forceRelay);
}); });
break; break;

View file

@ -22,6 +22,8 @@ const String kAppTypeDesktopRemote = "remote";
const String kAppTypeDesktopFileTransfer = "file transfer"; const String kAppTypeDesktopFileTransfer = "file transfer";
const String kAppTypeDesktopPortForward = "port forward"; const String kAppTypeDesktopPortForward = "port forward";
const bool kCloseMultiWindowByHide = true;
const String kWindowMainWindowOnTop = "main_window_on_top"; const String kWindowMainWindowOnTop = "main_window_on_top";
const String kWindowGetWindowInfo = "get_window_info"; const String kWindowGetWindowInfo = "get_window_info";
const String kWindowDisableGrabKeyboard = "disable_grab_keyboard"; const String kWindowDisableGrabKeyboard = "disable_grab_keyboard";
@ -30,6 +32,8 @@ const String kWindowEventHide = "hide";
const String kWindowEventShow = "show"; const String kWindowEventShow = "show";
const String kWindowConnect = "connect"; const String kWindowConnect = "connect";
const String kOptionSeparateRemoteWindow = "enable-separate-remote-window";
const String kUniLinksPrefix = "rustdesk://"; const String kUniLinksPrefix = "rustdesk://";
const String kUrlActionClose = "close"; const String kUrlActionClose = "close";

View file

@ -554,7 +554,13 @@ class _DesktopHomePageState extends State<DesktopHomePage>
} else if (call.method == kWindowEventShow) { } else if (call.method == kWindowEventShow) {
await rustDeskWinManager.registerActiveWindow(call.arguments["id"]); await rustDeskWinManager.registerActiveWindow(call.arguments["id"]);
} else if (call.method == kWindowEventHide) { } 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) { } else if (call.method == kWindowConnect) {
await connectMainDesktop( await connectMainDesktop(
call.arguments['id'], call.arguments['id'],

View file

@ -316,7 +316,9 @@ class _GeneralState extends State<_General> {
_OptionCheckBox(context, 'Confirm before closing multiple tabs', _OptionCheckBox(context, 'Confirm before closing multiple tabs',
'enable-confirm-closing-tabs', 'enable-confirm-closing-tabs',
isServer: false), 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 // though this is related to GUI, but opengl problem affects all users, so put in config rather than local
children.add(Tooltip( children.add(Tooltip(

View file

@ -18,6 +18,7 @@ import '../../common/widgets/remote_input.dart';
import '../../common.dart'; import '../../common.dart';
import '../../common/widgets/dialog.dart'; import '../../common/widgets/dialog.dart';
import '../../models/model.dart'; import '../../models/model.dart';
import '../../models/desktop_render_texture.dart';
import '../../models/platform_model.dart'; import '../../models/platform_model.dart';
import '../../common/shared_state.dart'; import '../../common/shared_state.dart';
import '../../utils/image.dart'; import '../../utils/image.dart';
@ -66,9 +67,7 @@ class _RemotePageState extends State<RemotePage>
late RxBool _zoomCursor; late RxBool _zoomCursor;
late RxBool _remoteCursorMoved; late RxBool _remoteCursorMoved;
late RxBool _keyboardEnabled; late RxBool _keyboardEnabled;
late RxInt _textureId; late RenderTexture _renderTexture;
late int _textureKey;
final useTextureRender = bind.mainUseTextureRender();
final _blockableOverlayState = BlockableOverlayState(); final _blockableOverlayState = BlockableOverlayState();
@ -86,8 +85,6 @@ class _RemotePageState extends State<RemotePage>
_showRemoteCursor = ShowRemoteCursorState.find(id); _showRemoteCursor = ShowRemoteCursorState.find(id);
_keyboardEnabled = KeyboardEnabledState.find(id); _keyboardEnabled = KeyboardEnabledState.find(id);
_remoteCursorMoved = RemoteCursorMovedState.find(id); _remoteCursorMoved = RemoteCursorMovedState.find(id);
_textureKey = newTextureId;
_textureId = RxInt(-1);
} }
@override @override
@ -115,17 +112,13 @@ class _RemotePageState extends State<RemotePage>
Wakelock.enable(); Wakelock.enable();
} }
// Register texture. // Register texture.
_textureId.value = -1; if (mainGetBoolOptionSync(kOptionSeparateRemoteWindow)) {
if (useTextureRender) { _renderTexture = renderTexture;
textureRenderer.createTexture(_textureKey).then((id) async { } else {
debugPrint("id: $id, texture_key: $_textureKey"); _renderTexture = RenderTexture();
if (id != -1) {
final ptr = await textureRenderer.getTexturePtr(_textureKey);
platformFFI.registerTexture(sessionId, ptr);
_textureId.value = id;
}
});
} }
_renderTexture.create(sessionId);
_ffi.ffiModel.updateEventListener(sessionId, widget.id); _ffi.ffiModel.updateEventListener(sessionId, widget.id);
bind.pluginSyncUi(syncTo: kAppTypeDesktopRemote); bind.pluginSyncUi(syncTo: kAppTypeDesktopRemote);
_ffi.qualityMonitorModel.checkShowQualityMonitor(sessionId); _ffi.qualityMonitorModel.checkShowQualityMonitor(sessionId);
@ -208,13 +201,8 @@ class _RemotePageState extends State<RemotePage>
Future<void> dispose() async { Future<void> dispose() async {
// https://github.com/flutter/flutter/issues/64935 // https://github.com/flutter/flutter/issues/64935
super.dispose(); super.dispose();
debugPrint("REMOTE PAGE dispose ${widget.id}"); debugPrint("REMOTE PAGE dispose session $sessionId ${widget.id}");
if (useTextureRender) { await _renderTexture.destroy();
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);
}
// ensure we leave this session, this is a double check // ensure we leave this session, this is a double check
bind.sessionEnterOrLeave(sessionId: sessionId, enter: false); bind.sessionEnterOrLeave(sessionId: sessionId, enter: false);
DesktopMultiWindow.removeListener(this); DesktopMultiWindow.removeListener(this);
@ -392,8 +380,8 @@ class _RemotePageState extends State<RemotePage>
cursorOverImage: _cursorOverImage, cursorOverImage: _cursorOverImage,
keyboardEnabled: _keyboardEnabled, keyboardEnabled: _keyboardEnabled,
remoteCursorMoved: _remoteCursorMoved, remoteCursorMoved: _remoteCursorMoved,
textureId: _textureId, textureId: _renderTexture.textureId,
useTextureRender: useTextureRender, useTextureRender: _renderTexture.useTextureRender,
listenerBuilder: (child) => listenerBuilder: (child) =>
_buildRawTouchAndPointerRegion(child, enterView, leaveView), _buildRawTouchAndPointerRegion(child, enterView, leaveView),
); );

View file

@ -13,6 +13,7 @@ import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/main.dart'; import 'package:flutter_hbb/main.dart';
import 'package:flutter_hbb/models/platform_model.dart'; import 'package:flutter_hbb/models/platform_model.dart';
import 'package:flutter_hbb/models/state_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:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:get/get_rx/src/rx_workers/utils/debouncer.dart'; import 'package:get/get_rx/src/rx_workers/utils/debouncer.dart';
@ -574,6 +575,8 @@ class WindowActionPanelState extends State<WindowActionPanel>
} }
await windowManager.hide(); await windowManager.hide();
} else { } else {
renderTexture.destroy();
// it's safe to hide the subwindow // it's safe to hide the subwindow
final controller = WindowController.fromWindowId(kWindowId!); final controller = WindowController.fromWindowId(kWindowId!);
if (Platform.isMacOS && await controller.isFullScreen()) { if (Platform.isMacOS && await controller.isFullScreen()) {

View file

@ -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;

View file

@ -5,6 +5,7 @@ import 'package:desktop_multi_window/desktop_multi_window.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/common.dart'; import 'package:flutter_hbb/common.dart';
/// must keep the order /// must keep the order
@ -35,146 +36,159 @@ class RustDeskMultiWindowManager {
static final instance = RustDeskMultiWindowManager._(); static final instance = RustDeskMultiWindowManager._();
final List<int> _activeWindows = List.empty(growable: true); final Set<int> _inactiveWindows = {};
final Set<int> _activeWindows = {};
final List<AsyncCallback> _windowActiveCallbacks = List.empty(growable: true); final List<AsyncCallback> _windowActiveCallbacks = List.empty(growable: true);
int? _remoteDesktopWindowId; final Map<int, Set<String>> _remoteDesktopWindows = {};
int? _fileTransferWindowId; final Map<int, Set<String>> _fileTransferWindows = {};
int? _portForwardWindowId; final Map<int, Set<String>> _portForwardWindows = {};
Future<dynamic> newRemoteDesktop( Future<dynamic> newSession(
String remoteId, { WindowType type,
String methodName,
String remoteId,
Map<int, Set<String>> windows, {
String? password, String? password,
String? switch_uuid,
bool? forceRelay, bool? forceRelay,
String? switchUuid,
bool? isRDP,
}) async { }) async {
var params = { var params = {
"type": WindowType.RemoteDesktop.index, "type": type.index,
"id": remoteId, "id": remoteId,
"password": password, "password": password,
"forceRelay": forceRelay "forceRelay": forceRelay
}; };
if (switch_uuid != null) { if (switchUuid != null) {
params['switch_uuid'] = switch_uuid; params['switch_uuid'] = switchUuid;
}
if (isRDP != null) {
params['isRDP'] = isRDP;
} }
final msg = jsonEncode(params); final msg = jsonEncode(params);
try { newSessionWindow() async {
final ids = await DesktopMultiWindow.getAllSubWindowIds(); final windowController = await DesktopMultiWindow.createWindow(msg);
if (!ids.contains(_remoteDesktopWindowId)) { windowController
_remoteDesktopWindowId = null;
}
} on Error {
_remoteDesktopWindowId = null;
}
if (_remoteDesktopWindowId == null) {
final remoteDesktopController =
await DesktopMultiWindow.createWindow(msg);
remoteDesktopController
..setFrame(const Offset(0, 0) & const Size(1280, 720)) ..setFrame(const Offset(0, 0) & const Size(1280, 720))
..center() ..center()
..setTitle(getWindowNameWithId(remoteId, ..setTitle(getWindowNameWithId(
overrideType: WindowType.RemoteDesktop)); remoteId,
overrideType: type,
));
if (Platform.isMacOS) { if (Platform.isMacOS) {
Future.microtask(() => remoteDesktopController.show()); Future.microtask(() => windowController.show());
} }
registerActiveWindow(remoteDesktopController.windowId); registerActiveWindow(windowController.windowId);
_remoteDesktopWindowId = remoteDesktopController.windowId; windows[windowController.windowId] = {remoteId};
} else {
return call(WindowType.RemoteDesktop, "new_remote_desktop", msg);
} }
// 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<dynamic> 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<dynamic> newFileTransfer(String remoteId, Future<dynamic> newFileTransfer(String remoteId,
{String? password, bool? forceRelay}) async { {String? password, bool? forceRelay}) async {
var msg = jsonEncode({ return await newSession(
"type": WindowType.FileTransfer.index, WindowType.FileTransfer,
"id": remoteId, 'new_file_transfer',
"password": password, remoteId,
"forceRelay": forceRelay, _fileTransferWindows,
}); 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);
}
} }
Future<dynamic> newPortForward(String remoteId, bool isRDP, Future<dynamic> newPortForward(String remoteId, bool isRDP,
{String? password, bool? forceRelay}) async { {String? password, bool? forceRelay}) async {
final msg = jsonEncode({ return await newSession(
"type": WindowType.PortForward.index, WindowType.PortForward,
"id": remoteId, 'new_port_forward',
"isRDP": isRDP, remoteId,
"password": password, _portForwardWindows,
"forceRelay": forceRelay, password: password,
}); forceRelay: forceRelay,
isRDP: isRDP,
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);
}
} }
Future<dynamic> call(WindowType type, String methodName, dynamic args) async { Future<dynamic> call(WindowType type, String methodName, dynamic args) async {
int? windowId = findWindowByType(type); final wnds = _findWindowsByType(type);
if (windowId == null) { if (wnds.isEmpty) {
return; return;
} }
return await DesktopMultiWindow.invokeMethod(windowId, methodName, args); return await DesktopMultiWindow.invokeMethod(
wnds.keys.toList()[0], methodName, args);
} }
int? findWindowByType(WindowType type) { Map<int, Set<String>> _findWindowsByType(WindowType type) {
switch (type) { switch (type) {
case WindowType.Main: case WindowType.Main:
return 0; return {
0: {''}
};
case WindowType.RemoteDesktop: case WindowType.RemoteDesktop:
return _remoteDesktopWindowId; return _remoteDesktopWindows;
case WindowType.FileTransfer: case WindowType.FileTransfer:
return _fileTransferWindowId; return _fileTransferWindows;
case WindowType.PortForward: case WindowType.PortForward:
return _portForwardWindowId; return _portForwardWindows;
case WindowType.Unknown: case WindowType.Unknown:
break; break;
} }
return null; return {};
} }
void clearWindowType(WindowType type) { void clearWindowType(WindowType type) {
@ -182,13 +196,13 @@ class RustDeskMultiWindowManager {
case WindowType.Main: case WindowType.Main:
return; return;
case WindowType.RemoteDesktop: case WindowType.RemoteDesktop:
_remoteDesktopWindowId = null; _remoteDesktopWindows.clear();
break; break;
case WindowType.FileTransfer: case WindowType.FileTransfer:
_fileTransferWindowId = null; _fileTransferWindows.clear();
break; break;
case WindowType.PortForward: case WindowType.PortForward:
_portForwardWindowId = null; _portForwardWindows.clear();
break; break;
case WindowType.Unknown: case WindowType.Unknown:
break; break;
@ -209,27 +223,37 @@ class RustDeskMultiWindowManager {
// skip main window, use window manager instead // skip main window, use window manager instead
return; return;
} }
int? wId = findWindowByType(type);
if (wId != null) { List<int> 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()}"); debugPrint("closing multi window: ${type.toString()}");
await saveWindowPosition(type, windowId: wId); await saveWindowPosition(type, windowId: wId);
try { try {
final ids = await DesktopMultiWindow.getAllSubWindowIds(); // final ids = await DesktopMultiWindow.getAllSubWindowIds();
if (!ids.contains(wId)) { // if (!ids.contains(wId)) {
// no such window already // // no such window already
return; // return;
} // }
await WindowController.fromWindowId(wId).setPreventClose(false); await WindowController.fromWindowId(wId).setPreventClose(false);
await WindowController.fromWindowId(wId).close(); await WindowController.fromWindowId(wId).close();
// unregister the sub window in the main window. _activeWindows.remove(wId);
unregisterActiveWindow(wId);
} catch (e) { } catch (e) {
debugPrint("$e"); debugPrint("$e");
return; return;
} finally {
clearWindowType(type);
} }
} }
await _notifyActiveWindow();
clearWindowType(type);
} }
Future<List<int>> getAllSubWindowIds() async { Future<List<int>> getAllSubWindowIds() async {
@ -245,7 +269,7 @@ class RustDeskMultiWindowManager {
} }
} }
List<int> getActiveWindows() { Set<int> getActiveWindows() {
return _activeWindows; return _activeWindows;
} }
@ -256,14 +280,19 @@ class RustDeskMultiWindowManager {
} }
Future<void> registerActiveWindow(int windowId) async { Future<void> registerActiveWindow(int windowId) async {
if (_activeWindows.contains(windowId)) { _activeWindows.add(windowId);
// ignore _inactiveWindows.remove(windowId);
} else {
_activeWindows.add(windowId);
}
await _notifyActiveWindow(); await _notifyActiveWindow();
} }
Future<void> 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`] /// Remove active window which has [`windowId`]
/// ///
/// [Availability] /// [Availability]
@ -271,10 +300,9 @@ class RustDeskMultiWindowManager {
/// For other windows, please post a unregister(hide) event to main window handler: /// For other windows, please post a unregister(hide) event to main window handler:
/// `rustDeskWinManager.call(WindowType.Main, kWindowEventHide, {"id": windowId!});` /// `rustDeskWinManager.call(WindowType.Main, kWindowEventHide, {"id": windowId!});`
Future<void> unregisterActiveWindow(int windowId) async { Future<void> unregisterActiveWindow(int windowId) async {
if (!_activeWindows.contains(windowId)) { _activeWindows.remove(windowId);
// ignore if (windowId != kMainWindowId) {
} else { _inactiveWindows.add(windowId);
_activeWindows.remove(windowId);
} }
await _notifyActiveWindow(); await _notifyActiveWindow();
} }

View file

@ -186,7 +186,7 @@ pub type FlutterRgbaRendererPluginOnRgba = unsafe extern "C" fn(
#[derive(Clone)] #[derive(Clone)]
struct VideoRenderer { struct VideoRenderer {
// TextureRgba pointer in flutter native. // TextureRgba pointer in flutter native.
ptr: usize, ptr: Arc<RwLock<usize>>,
width: usize, width: usize,
height: usize, height: usize,
on_rgba_func: Option<Symbol<'static, FlutterRgbaRendererPluginOnRgba>>, on_rgba_func: Option<Symbol<'static, FlutterRgbaRendererPluginOnRgba>>,
@ -214,7 +214,7 @@ impl Default for VideoRenderer {
} }
}; };
Self { Self {
ptr: 0, ptr: Default::default(),
width: 0, width: 0,
height: 0, height: 0,
on_rgba_func, on_rgba_func,
@ -231,7 +231,8 @@ impl VideoRenderer {
} }
pub fn on_rgba(&self, rgba: &mut scrap::ImageRgb) { 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; return;
} }
@ -243,7 +244,7 @@ impl VideoRenderer {
if let Some(func) = &self.on_rgba_func { if let Some(func) = &self.on_rgba_func {
unsafe { unsafe {
func( func(
self.ptr as _, *ptr as _,
rgba.raw.as_ptr() as _, rgba.raw.as_ptr() as _,
rgba.raw.len() as _, rgba.raw.len() as _,
rgba.w as _, rgba.w as _,
@ -328,7 +329,7 @@ impl FlutterHandler {
#[inline] #[inline]
#[cfg(feature = "flutter_texture_render")] #[cfg(feature = "flutter_texture_render")]
pub fn register_texture(&mut self, ptr: usize) { pub fn register_texture(&mut self, ptr: usize) {
self.renderer.write().unwrap().ptr = ptr; *self.renderer.read().unwrap().ptr.write().unwrap() = ptr;
} }
#[inline] #[inline]

View file

@ -15,7 +15,7 @@ use flutter_rust_bridge::{StreamSink, SyncReturn};
use hbb_common::allow_err; use hbb_common::allow_err;
use hbb_common::{ use hbb_common::{
config::{self, LocalConfig, PeerConfig, PeerInfoSerde}, config::{self, LocalConfig, PeerConfig, PeerInfoSerde},
fs, log, fs, lazy_static, log,
message_proto::KeyboardMode, message_proto::KeyboardMode,
ResultType, ResultType,
}; };
@ -24,11 +24,19 @@ use std::{
ffi::{CStr, CString}, ffi::{CStr, CString},
os::raw::c_char, os::raw::c_char,
str::FromStr, str::FromStr,
sync::{
atomic::{AtomicI32, Ordering},
Arc,
},
time::SystemTime, time::SystemTime,
}; };
pub type SessionID = uuid::Uuid; pub type SessionID = uuid::Uuid;
lazy_static::lazy_static! {
static ref TEXTURE_RENDER_KEY: Arc<AtomicI32> = Arc::new(AtomicI32::new(0));
}
fn initialize(app_dir: &str) { fn initialize(app_dir: &str) {
*config::APP_DIR.write().unwrap() = app_dir.to_owned(); *config::APP_DIR.write().unwrap() = app_dir.to_owned();
#[cfg(target_os = "android")] #[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<i32> {
let k = TEXTURE_RENDER_KEY.fetch_add(1, Ordering::SeqCst) + 1;
SyncReturn(k)
}
pub fn get_local_flutter_config(k: String) -> SyncReturn<String> { pub fn get_local_flutter_config(k: String) -> SyncReturn<String> {
SyncReturn(ui_interface::get_local_flutter_config(k)) SyncReturn(ui_interface::get_local_flutter_config(k))
} }

View file

@ -94,8 +94,7 @@ pub fn start(args: &mut [String]) {
args[1] = id; args[1] = id;
} }
if args.is_empty() { if args.is_empty() {
let children: Children = Default::default(); std::thread::spawn(move || check_zombie());
std::thread::spawn(move || check_zombie(children));
crate::common::check_software_update(); crate::common::check_software_update();
frame.event_handler(UI {}); frame.event_handler(UI {});
frame.sciter_handler(UIHostHandler {}); 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"))] #[cfg(not(target_os = "linux"))]
fn get_sound_inputs() -> Vec<String> { fn get_sound_inputs() -> Vec<String> {
let mut out = Vec::new(); let mut out = Vec::new();
@ -748,50 +725,6 @@ pub fn value_crash_workaround(values: &[Value]) -> Arc<Vec<Value>> {
persist 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 { pub fn get_icon() -> String {
// 128x128 // 128x128
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]

View file

@ -65,6 +65,7 @@ lazy_static::lazy_static! {
static ref OPTION_SYNCED: Arc<Mutex<bool>> = Default::default(); static ref OPTION_SYNCED: Arc<Mutex<bool>> = Default::default();
static ref OPTIONS : Arc<Mutex<HashMap<String, String>>> = Arc::new(Mutex::new(Config::get_options())); static ref OPTIONS : Arc<Mutex<HashMap<String, String>>> = Arc::new(Mutex::new(Config::get_options()));
pub static ref SENDER : Mutex<mpsc::UnboundedSender<ipc::Data>> = Mutex::new(check_connect_status(true)); pub static ref SENDER : Mutex<mpsc::UnboundedSender<ipc::Data>> = Mutex::new(check_connect_status(true));
static ref CHILDREN : Children = Default::default();
} }
const INIT_ASYNC_JOB_STATUS: &str = " "; const INIT_ASYNC_JOB_STATUS: &str = " ";
@ -827,11 +828,11 @@ pub fn check_super_user_permission() -> bool {
return true; return true;
} }
#[allow(dead_code)] #[cfg(not(any(target_os = "android", target_os = "ios", feature = "flutter")))]
pub fn check_zombie(children: Children) { pub fn check_zombie() {
let mut deads = Vec::new(); let mut deads = Vec::new();
loop { loop {
let mut lock = children.lock().unwrap(); let mut lock = CHILDREN.lock().unwrap();
let mut n = 0; let mut n = 0;
for (id, c) in lock.1.iter_mut() { for (id, c) in lock.1.iter_mut() {
if let Ok(Some(_)) = c.try_wait() { 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. // Make sure `SENDER` is inited here.
#[inline] #[inline]
#[cfg(not(any(target_os = "android", target_os = "ios")))] #[cfg(not(any(target_os = "android", target_os = "ios")))]