Merge pull request #5386 from dignow/refact/tab_2_window_flutter_data

Refact/tab 2 window flutter data
This commit is contained in:
RustDesk
2023-08-23 12:52:19 +08:00
committed by GitHub
15 changed files with 179 additions and 138 deletions

View File

@@ -5,6 +5,7 @@ import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/models/state_model.dart';
const double kDesktopRemoteTabBarHeight = 28.0;
const int kInvalidWindowId = -1;
const int kMainWindowId = 0;
const String kPeerPlatformWindows = "Windows";
@@ -38,7 +39,7 @@ const String kWindowEventGetRemoteList = "get_remote_list";
const String kWindowEventGetSessionIdList = "get_session_id_list";
const String kWindowEventMoveTabToNewWindow = "move_tab_to_new_window";
const String kWindowEventCloseForSeparateWindow = "close_for_separate_window";
const String kWindowEventGetCachedSessionData = "get_cached_session_data";
const String kOptionOpenNewConnInTabs = "enable-open-new-connections-in-tabs";
const String kOptionOpenInTabs = "allow-open-in-tabs";

View File

@@ -17,7 +17,6 @@ import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart';
import 'package:window_manager/window_manager.dart';
import '../../common/widgets/dialog.dart';
import '../../common/widgets/login.dart';

View File

@@ -35,6 +35,7 @@ class RemotePage extends StatefulWidget {
Key? key,
required this.id,
required this.sessionId,
required this.tabWindowId,
required this.password,
required this.toolbarState,
required this.tabController,
@@ -44,6 +45,7 @@ class RemotePage extends StatefulWidget {
final String id;
final SessionID? sessionId;
final int? tabWindowId;
final String? password;
final ToolbarState toolbarState;
final String? switchUuid;
@@ -106,6 +108,7 @@ class _RemotePageState extends State<RemotePage>
password: widget.password,
switchUuid: widget.switchUuid,
forceRelay: widget.forceRelay,
tabWindowId: widget.tabWindowId,
);
WidgetsBinding.instance.addPostFrameCallback((_) {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);

View File

@@ -55,6 +55,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
RemoteCountState.init();
peerId = params['id'];
final sessionId = params['session_id'];
final tabWindowId = params['tab_window_id'];
if (peerId != null) {
ConnectionTypeState.init(peerId!);
tabController.onSelected = (id) {
@@ -77,6 +78,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
key: ValueKey(peerId),
id: peerId!,
sessionId: sessionId == null ? null : SessionID(sessionId),
tabWindowId: tabWindowId,
password: params['password'],
toolbarState: _toolbarState,
tabController: tabController,
@@ -98,12 +100,14 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
print(
"[Remote Page] call ${call.method} with args ${call.arguments} from window $fromWindowId");
dynamic returnValue;
// for simplify, just replace connectionId
if (call.method == kWindowEventNewRemoteDesktop) {
final args = jsonDecode(call.arguments);
final id = args['id'];
final switchUuid = args['switch_uuid'];
final sessionId = args['session_id'];
final tabWindowId = args['tab_window_id'];
windowOnTop(windowId());
ConnectionTypeState.init(id);
_toolbarState.setShow(
@@ -118,6 +122,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
key: ValueKey(id),
id: id,
sessionId: sessionId == null ? null : SessionID(sessionId),
tabWindowId: tabWindowId,
password: args['password'],
toolbarState: _toolbarState,
tabController: tabController,
@@ -147,12 +152,24 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
.map((e) => '${e.key},${(e.page as RemotePage).ffi.sessionId}')
.toList()
.join(';');
} else if (call.method == kWindowEventCloseForSeparateWindow) {
} else if (call.method == kWindowEventGetCachedSessionData) {
// Ready to show new window and close old tab.
final peerId = call.arguments;
closeSessionOnDispose[peerId] = false;
tabController.closeBy(peerId);
try {
final remotePage = tabController.state.value.tabs
.firstWhere((tab) => tab.key == peerId)
.page as RemotePage;
returnValue = remotePage.ffi.ffiModel.cachedPeerData.toString();
} catch (e) {
debugPrint('Failed to get cached session data: $e');
}
if (returnValue != null) {
closeSessionOnDispose[peerId] = false;
tabController.closeBy(peerId);
}
}
_update_remote_count();
return returnValue;
});
Future.delayed(Duration.zero, () {
restoreWindowPosition(

View File

@@ -771,7 +771,7 @@ class ScreenAdjustor {
updateScreen() async {
final v = await rustDeskWinManager.call(
WindowType.Main, kWindowGetWindowInfo, '');
final String valueStr = v;
final String valueStr = v.result;
if (valueStr.isEmpty) {
_screen = null;
} else {

View File

@@ -1,4 +1,3 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:texture_rgba_renderer/texture_rgba_renderer.dart';
@@ -21,7 +20,6 @@ class RenderTexture {
_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);

View File

@@ -4,6 +4,7 @@ import 'dart:io';
import 'dart:math';
import 'dart:ui' as ui;
import 'package:desktop_multi_window/desktop_multi_window.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hbb/consts.dart';
@@ -41,7 +42,50 @@ final _waitForImageDialogShow = <UuidValue, bool>{};
final _waitForFirstImage = <UuidValue, bool>{};
final _constSessionId = Uuid().v4obj();
class CachedPeerData {
Map<String, dynamic> updatePrivacyMode = {};
Map<String, dynamic> peerInfo = {};
List<Map<String, dynamic>> cursorDataList = [];
Map<String, dynamic> lastCursorId = {};
bool secure = false;
bool direct = false;
CachedPeerData();
@override
String toString() {
return jsonEncode({
'updatePrivacyMode': updatePrivacyMode,
'peerInfo': peerInfo,
'cursorDataList': cursorDataList,
'lastCursorId': lastCursorId,
'secure': secure,
'direct': direct,
});
}
static CachedPeerData? fromString(String s) {
try {
final map = jsonDecode(s);
final data = CachedPeerData();
data.updatePrivacyMode = map['updatePrivacyMode'];
data.peerInfo = map['peerInfo'];
for (final cursorData in map['cursorDataList']) {
data.cursorDataList.add(cursorData);
}
data.lastCursorId = map['lastCursorId'];
data.secure = map['secure'];
data.direct = map['direct'];
return data;
} catch (e) {
debugPrint('Failed to parse CachedPeerData: $e');
return null;
}
}
}
class FfiModel with ChangeNotifier {
CachedPeerData cachedPeerData = CachedPeerData();
PeerInfo _pi = PeerInfo();
Display _display = Display();
@@ -117,6 +161,8 @@ class FfiModel with ChangeNotifier {
}
setConnectionType(String peerId, bool secure, bool direct) {
cachedPeerData.secure = secure;
cachedPeerData.direct = direct;
_secure = secure;
_direct = direct;
try {
@@ -143,6 +189,22 @@ class FfiModel with ChangeNotifier {
_permissions.clear();
}
handleCachedPeerData(CachedPeerData data, String peerId) async {
handleMsgBox({
'type': 'success',
'title': 'Successful',
'text': 'Connected, waiting for image...',
'link': '',
}, sessionId, peerId);
updatePrivacyMode(data.updatePrivacyMode, sessionId, peerId);
setConnectionType(peerId, data.secure, data.direct);
handlePeerInfo(data.peerInfo, peerId);
for (var element in data.cursorDataList) {
handleCursorData(element);
}
handleCursorId(data.lastCursorId);
}
// todo: why called by two position
StreamEventHandler startEventListener(SessionID sessionId, String peerId) {
return (evt) async {
@@ -159,9 +221,9 @@ class FfiModel with ChangeNotifier {
} else if (name == 'switch_display') {
handleSwitchDisplay(evt, sessionId, peerId);
} else if (name == 'cursor_data') {
await parent.target?.cursorModel.updateCursorData(evt);
await handleCursorData(evt);
} else if (name == 'cursor_id') {
await parent.target?.cursorModel.updateCursorId(evt);
await handleCursorId(evt);
} else if (name == 'cursor_position') {
await parent.target?.cursorModel.updateCursorPosition(evt, peerId);
} else if (name == 'clipboard') {
@@ -464,6 +526,8 @@ class FfiModel with ChangeNotifier {
/// Handle the peer info event based on [evt].
handlePeerInfo(Map<String, dynamic> evt, String peerId) async {
cachedPeerData.peerInfo = evt;
// recent peer updated by handle_peer_info(ui_session_interface.rs) --> handle_peer_info(client.rs) --> save_config(client.rs)
bind.mainLoadRecentPeers();
@@ -579,9 +643,20 @@ class FfiModel with ChangeNotifier {
return d;
}
handleCursorId(Map<String, dynamic> evt) async {
cachedPeerData.lastCursorId = evt;
await parent.target?.cursorModel.updateCursorId(evt);
}
handleCursorData(Map<String, dynamic> evt) async {
cachedPeerData.cursorDataList.add(evt);
await parent.target?.cursorModel.updateCursorData(evt);
}
/// Handle the peer info synchronization event based on [evt].
handleSyncPeerInfo(Map<String, dynamic> evt, SessionID sessionId) async {
if (evt['displays'] != null) {
cachedPeerData.peerInfo['displays'] = evt['displays'];
List<dynamic> displays = json.decode(evt['displays']);
List<Display> newDisplays = [];
for (int i = 0; i < displays.length; ++i) {
@@ -1596,7 +1671,6 @@ 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
@@ -1615,7 +1689,6 @@ class FFI {
late final ElevationModel elevationModel; // session
FFI(SessionID? sId) {
isSessionAdded = sId != null;
sessionId = sId ?? (isDesktop ? Uuid().v4obj() : _constSessionId);
imageModel = ImageModel(WeakReference(this));
ffiModel = FfiModel(WeakReference(this));
@@ -1641,7 +1714,8 @@ class FFI {
bool isRdp = false,
String? switchUuid,
String? password,
bool? forceRelay}) {
bool? forceRelay,
int? tabWindowId}) {
closed = false;
auditNote = '';
assert(!(isFileTransfer && isPortForward), 'more than one connect type');
@@ -1656,7 +1730,9 @@ class FFI {
imageModel.id = id;
cursorModel.id = id;
}
if (!isSessionAdded) {
// If tabWindowId != null, this session is a "tab -> window" one.
// Else this session is a new one.
if (tabWindowId == null) {
// ignore: unused_local_variable
final addRes = bind.sessionAddSync(
sessionId: sessionId,
@@ -1677,8 +1753,25 @@ class FFI {
// Preserved for the rgba data.
stream.listen((message) {
if (closed) return;
if (isSessionAdded && !isToNewWindowNotified.value) {
bind.sessionReadyToNewWindow(sessionId: sessionId);
if (tabWindowId != null && !isToNewWindowNotified.value) {
// Session is read to be moved to a new window.
// Get the cached data and handle the cached data.
Future.delayed(Duration.zero, () async {
final cachedData = await DesktopMultiWindow.invokeMethod(
tabWindowId, kWindowEventGetCachedSessionData, id);
if (cachedData == null) {
// unreachable
debugPrint('Unreachable, the cached data is empty.');
return;
}
final data = CachedPeerData.fromString(cachedData);
if (data == null) {
debugPrint('Unreachable, the cached data cannot be decoded.');
return;
}
ffiModel.handleCachedPeerData(data, id);
await bind.sessionRefresh(sessionId: sessionId);
});
isToNewWindowNotified.value = true;
}
() async {

View File

@@ -28,6 +28,13 @@ extension Index on int {
}
}
class MultiWindowCallResult {
int windowId;
dynamic result;
MultiWindowCallResult(this.windowId, this.result);
}
/// Window Manager
/// mainly use it in `Main Window`
/// use it in sub window is not recommended
@@ -47,6 +54,7 @@ class RustDeskMultiWindowManager {
var params = {
'type': WindowType.RemoteDesktop.index,
'id': peerId,
'tab_window_id': windowId,
'session_id': sessionId,
};
await _newSession(
@@ -57,17 +65,15 @@ class RustDeskMultiWindowManager {
_remoteDesktopWindows,
jsonEncode(params),
);
await DesktopMultiWindow.invokeMethod(
windowId, kWindowEventCloseForSeparateWindow, peerId);
}
newSessionWindow(
Future<int> newSessionWindow(
WindowType type, String remoteId, String msg, List<int> windows) async {
final windowController = await DesktopMultiWindow.createWindow(msg);
final windowId = windowController.windowId;
windowController
..setFrame(const Offset(0, 0) &
Size(1280 + windowController.windowId * 20,
720 + windowController.windowId * 20))
..setFrame(
const Offset(0, 0) & Size(1280 + windowId * 20, 720 + windowId * 20))
..center()
..setTitle(getWindowNameWithId(
remoteId,
@@ -76,11 +82,12 @@ class RustDeskMultiWindowManager {
if (Platform.isMacOS) {
Future.microtask(() => windowController.show());
}
registerActiveWindow(windowController.windowId);
windows.add(windowController.windowId);
registerActiveWindow(windowId);
windows.add(windowId);
return windowId;
}
_newSession(
Future<MultiWindowCallResult> _newSession(
bool openInTabs,
WindowType type,
String methodName,
@@ -90,9 +97,10 @@ class RustDeskMultiWindowManager {
) async {
if (openInTabs) {
if (windows.isEmpty) {
await newSessionWindow(type, remoteId, msg, windows);
final windowId = await newSessionWindow(type, remoteId, msg, windows);
return MultiWindowCallResult(windowId, null);
} else {
call(type, methodName, msg);
return call(type, methodName, msg);
}
} else {
if (_inactiveWindows.isNotEmpty) {
@@ -103,15 +111,16 @@ class RustDeskMultiWindowManager {
await DesktopMultiWindow.invokeMethod(windowId, methodName, msg);
WindowController.fromWindowId(windowId).show();
registerActiveWindow(windowId);
return;
return MultiWindowCallResult(windowId, null);
}
}
}
await newSessionWindow(type, remoteId, msg, windows);
final windowId = await newSessionWindow(type, remoteId, msg, windows);
return MultiWindowCallResult(windowId, null);
}
}
Future<dynamic> newSession(
Future<MultiWindowCallResult> newSession(
WindowType type,
String methodName,
String remoteId,
@@ -143,15 +152,15 @@ class RustDeskMultiWindowManager {
for (final windowId in windows) {
if (await DesktopMultiWindow.invokeMethod(
windowId, kWindowEventActiveSession, remoteId)) {
return;
return MultiWindowCallResult(windowId, null);
}
}
}
await _newSession(openInTabs, type, methodName, remoteId, windows, msg);
return _newSession(openInTabs, type, methodName, remoteId, windows, msg);
}
Future<dynamic> newRemoteDesktop(
Future<MultiWindowCallResult> newRemoteDesktop(
String remoteId, {
String? password,
String? switchUuid,
@@ -168,7 +177,7 @@ class RustDeskMultiWindowManager {
);
}
Future<dynamic> newFileTransfer(String remoteId,
Future<MultiWindowCallResult> newFileTransfer(String remoteId,
{String? password, bool? forceRelay}) async {
return await newSession(
WindowType.FileTransfer,
@@ -180,7 +189,7 @@ class RustDeskMultiWindowManager {
);
}
Future<dynamic> newPortForward(String remoteId, bool isRDP,
Future<MultiWindowCallResult> newPortForward(String remoteId, bool isRDP,
{String? password, bool? forceRelay}) async {
return await newSession(
WindowType.PortForward,
@@ -193,18 +202,22 @@ class RustDeskMultiWindowManager {
);
}
Future<dynamic> call(WindowType type, String methodName, dynamic args) async {
Future<MultiWindowCallResult> call(
WindowType type, String methodName, dynamic args) async {
final wnds = _findWindowsByType(type);
if (wnds.isEmpty) {
return;
return MultiWindowCallResult(kInvalidWindowId, null);
}
for (final windowId in wnds) {
if (_activeWindows.contains(windowId)) {
return await DesktopMultiWindow.invokeMethod(
windowId, methodName, args);
final res =
await DesktopMultiWindow.invokeMethod(windowId, methodName, args);
return MultiWindowCallResult(windowId, res);
}
}
return await DesktopMultiWindow.invokeMethod(wnds[0], methodName, args);
final res =
await DesktopMultiWindow.invokeMethod(wnds[0], methodName, args);
return MultiWindowCallResult(wnds[0], res);
}
List<int> _findWindowsByType(WindowType type) {