refact, separate remote window, tmp commit

Signed-off-by: dignow <linlong1265@gmail.com>
This commit is contained in:
dignow 2023-08-03 23:14:40 +08:00
parent 902f56c499
commit 1970795093
10 changed files with 115 additions and 52 deletions

View File

@ -1216,7 +1216,7 @@ FFI get gFFI => _globalFFI;
Future<void> initGlobalFFI() async { Future<void> initGlobalFFI() async {
debugPrint("_globalFFI init"); debugPrint("_globalFFI init");
_globalFFI = FFI(); _globalFFI = FFI(null);
debugPrint("_globalFFI init end"); debugPrint("_globalFFI init end");
// after `put`, can also be globally found by Get.find<FFI>(); // after `put`, can also be globally found by Get.find<FFI>();
Get.put(_globalFFI, permanent: true); Get.put(_globalFFI, permanent: true);

View File

@ -37,6 +37,9 @@ const String kWindowEventNewFileTransfer = "new_file_transfer";
const String kWindowEventNewPortForward = "new_port_forward"; const String kWindowEventNewPortForward = "new_port_forward";
const String kWindowEventActiveSession = "active_session"; const String kWindowEventActiveSession = "active_session";
const String kWindowEventGetRemoteList = "get_remote_list"; 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"; const String kOptionSeparateRemoteWindow = "enable-separate-remote-window";

View File

@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_hbb/common.dart'; import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/consts.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_home_page.dart';
import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart'; import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart';
import 'package:flutter_hbb/models/platform_model.dart'; import 'package:flutter_hbb/models/platform_model.dart';
@ -248,7 +249,7 @@ class _General extends StatefulWidget {
class _GeneralState extends State<_General> { class _GeneralState extends State<_General> {
final RxBool serviceStop = Get.find<RxBool>(tag: 'stop-service'); final RxBool serviceStop = Get.find<RxBool>(tag: 'stop-service');
RxBool serviceBtnEabled = true.obs; RxBool serviceBtnEnabled = true.obs;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -300,14 +301,14 @@ class _GeneralState extends State<_General> {
return _Card(title: 'Service', children: [ return _Card(title: 'Service', children: [
Obx(() => _Button(serviceStop.value ? 'Start' : 'Stop', () { Obx(() => _Button(serviceStop.value ? 'Start' : 'Stop', () {
() async { () async {
serviceBtnEabled.value = false; serviceBtnEnabled.value = false;
await start_service(serviceStop.value); await start_service(serviceStop.value);
// enable the button after 1 second // enable the button after 1 second
Future.delayed(const Duration(seconds: 1), () { 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), isServer: false),
_OptionCheckBox(context, 'Adaptive Bitrate', 'enable-abr'), _OptionCheckBox(context, 'Adaptive Bitrate', 'enable-abr'),
_OptionCheckBox( _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 // 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(
@ -1678,7 +1686,6 @@ Widget _OptionCheckBox(BuildContext context, String label, String key,
isServer isServer
? await mainSetBoolOption(key, option) ? await mainSetBoolOption(key, option)
: await mainSetLocalBoolOption(key, option); : await mainSetLocalBoolOption(key, option);
;
update?.call(); update?.call();
} }
} }

View File

@ -80,7 +80,7 @@ class _FileManagerPageState extends State<FileManagerPage>
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_ffi = FFI(); _ffi = FFI(null);
_ffi.start(widget.id, _ffi.start(widget.id,
isFileTransfer: true, isFileTransfer: true,
password: widget.password, password: widget.password,

View File

@ -54,7 +54,7 @@ class _PortForwardPageState extends State<PortForwardPage>
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_ffi = FFI(); _ffi = FFI(null);
_ffi.start(widget.id, _ffi.start(widget.id,
isPortForward: true, isPortForward: true,
password: widget.password, password: widget.password,

View File

@ -28,10 +28,13 @@ import '../widgets/tabbar_widget.dart';
final SimpleWrapper<bool> _firstEnterImage = SimpleWrapper(false); final SimpleWrapper<bool> _firstEnterImage = SimpleWrapper(false);
final Map<String, bool> noCloseSessionOnDispose = {};
class RemotePage extends StatefulWidget { class RemotePage extends StatefulWidget {
RemotePage({ RemotePage({
Key? key, Key? key,
required this.id, required this.id,
required this.sessionId,
required this.password, required this.password,
required this.toolbarState, required this.toolbarState,
required this.tabController, required this.tabController,
@ -40,6 +43,7 @@ class RemotePage extends StatefulWidget {
}) : super(key: key); }) : super(key: key);
final String id; final String id;
final SessionID? sessionId;
final String? password; final String? password;
final ToolbarState toolbarState; final ToolbarState toolbarState;
final String? switchUuid; final String? switchUuid;
@ -91,7 +95,7 @@ class _RemotePageState extends State<RemotePage>
void initState() { void initState() {
super.initState(); super.initState();
_initStates(widget.id); _initStates(widget.id);
_ffi = FFI(); _ffi = FFI(widget.sessionId);
Get.put(_ffi, tag: widget.id); Get.put(_ffi, tag: widget.id);
_ffi.imageModel.addCallbackOnFirstImage((String peerId) { _ffi.imageModel.addCallbackOnFirstImage((String peerId) {
showKBLayoutTypeChooserIfNeeded( showKBLayoutTypeChooserIfNeeded(
@ -199,6 +203,8 @@ class _RemotePageState extends State<RemotePage>
@override @override
Future<void> dispose() async { Future<void> dispose() async {
final closeSession = noCloseSessionOnDispose.remove(widget.id) ?? false;
// https://github.com/flutter/flutter/issues/64935 // https://github.com/flutter/flutter/issues/64935
super.dispose(); super.dispose();
debugPrint("REMOTE PAGE dispose session $sessionId ${widget.id}"); debugPrint("REMOTE PAGE dispose session $sessionId ${widget.id}");
@ -209,11 +215,13 @@ class _RemotePageState extends State<RemotePage>
_ffi.dialogManager.hideMobileActionsOverlay(); _ffi.dialogManager.hideMobileActionsOverlay();
_ffi.recordingModel.onClose(); _ffi.recordingModel.onClose();
_rawKeyFocusNode.dispose(); _rawKeyFocusNode.dispose();
await _ffi.close(); await _ffi.close(closeSession: closeSession);
_timer?.cancel(); _timer?.cancel();
_ffi.dialogManager.dismissAll(); _ffi.dialogManager.dismissAll();
if (closeSession) {
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
overlays: SystemUiOverlay.values); overlays: SystemUiOverlay.values);
}
if (!Platform.isLinux) { if (!Platform.isLinux) {
await Wakelock.disable(); await Wakelock.disable();
} }

View File

@ -52,6 +52,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
_toolbarState = ToolbarState(); _toolbarState = ToolbarState();
RemoteCountState.init(); RemoteCountState.init();
final peerId = params['id']; final peerId = params['id'];
final sessionId = params['session_id'];
if (peerId != null) { if (peerId != null) {
ConnectionTypeState.init(peerId); ConnectionTypeState.init(peerId);
tabController.onSelected = (id) { tabController.onSelected = (id) {
@ -73,6 +74,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
page: RemotePage( page: RemotePage(
key: ValueKey(peerId), key: ValueKey(peerId),
id: peerId, id: peerId,
sessionId: sessionId == null ? null : SessionID(sessionId),
password: params['password'], password: params['password'],
toolbarState: _toolbarState, toolbarState: _toolbarState,
tabController: tabController, tabController: tabController,
@ -99,6 +101,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
final args = jsonDecode(call.arguments); final args = jsonDecode(call.arguments);
final id = args['id']; final id = args['id'];
final switchUuid = args['switch_uuid']; final switchUuid = args['switch_uuid'];
final sessionId = args['session_id'];
windowOnTop(windowId()); windowOnTop(windowId());
ConnectionTypeState.init(id); ConnectionTypeState.init(id);
_toolbarState.setShow( _toolbarState.setShow(
@ -112,6 +115,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
page: RemotePage( page: RemotePage(
key: ValueKey(id), key: ValueKey(id),
id: id, id: id,
sessionId: sessionId == null ? null : SessionID(sessionId),
password: args['password'], password: args['password'],
toolbarState: _toolbarState, toolbarState: _toolbarState,
tabController: tabController, tabController: tabController,
@ -136,6 +140,15 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
.map((e) => e.key) .map((e) => e.key)
.toList() .toList()
.join(','); .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(); _update_remote_count();
}); });

View File

@ -1579,6 +1579,7 @@ class FFI {
/// dialogManager use late to ensure init after main page binding [globalKey] /// dialogManager use late to ensure init after main page binding [globalKey]
late final dialogManager = OverlayDialogManager(); late final dialogManager = OverlayDialogManager();
late final bool isSessionAdded;
late final SessionID sessionId; late final SessionID sessionId;
late final ImageModel imageModel; // session late final ImageModel imageModel; // session
late final FfiModel ffiModel; // session late final FfiModel ffiModel; // session
@ -1596,8 +1597,9 @@ class FFI {
late final InputModel inputModel; // session late final InputModel inputModel; // session
late final ElevationModel elevationModel; // session late final ElevationModel elevationModel; // session
FFI() { FFI(SessionID? sId) {
sessionId = isDesktop ? Uuid().v4obj() : _constSessionId; isSessionAdded = sId != null;
sessionId = sId ?? (isDesktop ? Uuid().v4obj() : _constSessionId);
imageModel = ImageModel(WeakReference(this)); imageModel = ImageModel(WeakReference(this));
ffiModel = FfiModel(WeakReference(this)); ffiModel = FfiModel(WeakReference(this));
cursorModel = CursorModel(WeakReference(this)); cursorModel = CursorModel(WeakReference(this));
@ -1637,6 +1639,7 @@ class FFI {
imageModel.id = id; imageModel.id = id;
cursorModel.id = id; cursorModel.id = id;
} }
if (isSessionAdded) {
// ignore: unused_local_variable // ignore: unused_local_variable
final addRes = bind.sessionAddSync( final addRes = bind.sessionAddSync(
sessionId: sessionId, sessionId: sessionId,
@ -1648,6 +1651,7 @@ class FFI {
forceRelay: forceRelay ?? false, forceRelay: forceRelay ?? false,
password: password ?? "", password: password ?? "",
); );
}
final stream = bind.sessionStart(sessionId: sessionId, id: id); final stream = bind.sessionStart(sessionId: sessionId, id: id);
final cb = ffiModel.startEventListener(sessionId, id); final cb = ffiModel.startEventListener(sessionId, id);
final useTextureRender = bind.mainUseTextureRender(); final useTextureRender = bind.mainUseTextureRender();
@ -1712,7 +1716,7 @@ class FFI {
} }
/// Close the remote session. /// Close the remote session.
Future<void> close() async { Future<void> close({bool closeSession = true}) async {
closed = true; closed = true;
chatModel.close(); chatModel.close();
if (imageModel.image != null && !isWebDesktop) { if (imageModel.image != null && !isWebDesktop) {
@ -1730,7 +1734,9 @@ class FFI {
ffiModel.clear(); ffiModel.clear();
canvasModel.clear(); canvasModel.clear();
inputModel.resetModifiers(); inputModel.resetModifiers();
if (closeSession) {
await bind.sessionClose(sessionId: sessionId); await bind.sessionClose(sessionId: sessionId);
}
debugPrint('model $id closed'); debugPrint('model $id closed');
id = ''; id = '';
} }

View File

@ -43,6 +43,46 @@ class RustDeskMultiWindowManager {
final List<int> _fileTransferWindows = List.empty(growable: true); final List<int> _fileTransferWindows = List.empty(growable: true);
final List<int> _portForwardWindows = List.empty(growable: true); final List<int> _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<int> 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<dynamic> newSession( Future<dynamic> newSession(
WindowType type, WindowType type,
String methodName, String methodName,
@ -68,24 +108,6 @@ class RustDeskMultiWindowManager {
} }
final msg = jsonEncode(params); 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 // separate window for file transfer is not supported
bool separateWindow = forceSeparateWindow || bool separateWindow = forceSeparateWindow ||
(type != WindowType.FileTransfer && (type != WindowType.FileTransfer &&
@ -111,11 +133,11 @@ class RustDeskMultiWindowManager {
windows.add(windowController.windowId); windows.add(windowController.windowId);
return invokeRes; return invokeRes;
} else { } else {
await newSessionWindow(); await newSessionWindow(type, remoteId, msg, windows);
} }
} else { } else {
if (windows.isEmpty) { if (windows.isEmpty) {
await newSessionWindow(); await newSessionWindow(type, remoteId, msg, windows);
} else { } else {
return call(type, methodName, msg); return call(type, methodName, msg);
} }

View File

@ -782,11 +782,15 @@ pub fn session_start_(
); );
#[cfg(not(feature = "flutter_texture_render"))] #[cfg(not(feature = "flutter_texture_render"))]
log::info!("Session {} start, render by flutter paint widget", id); 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); *session.event_stream.write().unwrap() = Some(event_stream);
if !is_pre_added {
let session = session.clone(); let session = session.clone();
std::thread::spawn(move || { std::thread::spawn(move || {
io_loop(session); io_loop(session);
}); });
}
Ok(()) Ok(())
} else { } else {
bail!("No session with peer id {}", id) bail!("No session with peer id {}", id)