mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
feat, open multi windows
Signed-off-by: dignow <linlong1265@gmail.com>
This commit is contained in:
parent
f5d8e99fc7
commit
bf83d552f8
@ -14,8 +14,8 @@ import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hbb/common/formatter/id_formatter.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/refresh_wrapper.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
||||
import 'package:flutter_hbb/models/desktop_render_texture.dart';
|
||||
import 'package:flutter_hbb/main.dart';
|
||||
import 'package:flutter_hbb/models/desktop_render_texture.dart';
|
||||
import 'package:flutter_hbb/models/peer_model.dart';
|
||||
import 'package:flutter_hbb/models/state_model.dart';
|
||||
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
||||
@ -53,6 +53,9 @@ int androidVersion = 0;
|
||||
int windowsBuildNumber = 0;
|
||||
DesktopType? desktopType;
|
||||
|
||||
bool get isMainDesktopWindow =>
|
||||
desktopType == DesktopType.main || desktopType == DesktopType.cm;
|
||||
|
||||
/// Check if the app is running with single view mode.
|
||||
bool isSingleViewApp() {
|
||||
return desktopType == DesktopType.cm;
|
||||
@ -1672,6 +1675,7 @@ Future<Offset?> _adjustRestoreMainWindowOffset(
|
||||
/// Note that windowId must be provided if it's subwindow
|
||||
Future<bool> restoreWindowPosition(WindowType type,
|
||||
{int? windowId, String? peerId}) async {
|
||||
debugPrintStack(label: "restoreWindowPosition");
|
||||
if (bind
|
||||
.mainGetEnv(key: "DISABLE_RUSTDESK_RESTORE_WINDOW_POSITION")
|
||||
.isNotEmpty) {
|
||||
@ -2605,3 +2609,109 @@ bool isChooseDisplayToOpenInNewWindow(PeerInfo pi, SessionID sessionId) =>
|
||||
pi.isSupportMultiDisplay &&
|
||||
useTextureRender &&
|
||||
bind.sessionGetDisplaysAsIndividualWindows(sessionId: sessionId) == 'Y';
|
||||
|
||||
Future<List<Rect>> getScreenListWayland() async {
|
||||
final screenRectList = <Rect>[];
|
||||
if (isMainDesktopWindow) {
|
||||
for (var screen in await window_size.getScreenList()) {
|
||||
final scale = kIgnoreDpi ? 1.0 : screen.scaleFactor;
|
||||
double l = screen.frame.left;
|
||||
double t = screen.frame.top;
|
||||
double r = screen.frame.right;
|
||||
double b = screen.frame.bottom;
|
||||
final rect = Rect.fromLTRB(l / scale, t / scale, r / scale, b / scale);
|
||||
screenRectList.add(rect);
|
||||
}
|
||||
} else {
|
||||
final screenList = await rustDeskWinManager.call(
|
||||
WindowType.Main, kWindowGetScreenList, '');
|
||||
try {
|
||||
for (var screen in jsonDecode(screenList.result) as List<dynamic>) {
|
||||
final scale = kIgnoreDpi ? 1.0 : screen['scaleFactor'];
|
||||
double l = screen['frame']['l'];
|
||||
double t = screen['frame']['t'];
|
||||
double r = screen['frame']['r'];
|
||||
double b = screen['frame']['b'];
|
||||
final rect = Rect.fromLTRB(l / scale, t / scale, r / scale, b / scale);
|
||||
screenRectList.add(rect);
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Failed to parse screenList: $e');
|
||||
}
|
||||
}
|
||||
return screenRectList;
|
||||
}
|
||||
|
||||
Future<List<Rect>> getScreenListNotWayland() async {
|
||||
final screenRectList = <Rect>[];
|
||||
final displays = bind.mainGetDisplays();
|
||||
if (displays.isEmpty) {
|
||||
return screenRectList;
|
||||
}
|
||||
try {
|
||||
for (var display in jsonDecode(displays) as List<dynamic>) {
|
||||
// to-do: scale factor ?
|
||||
// final scale = kIgnoreDpi ? 1.0 : screen.scaleFactor;
|
||||
double l = display['x'].toDouble();
|
||||
double t = display['y'].toDouble();
|
||||
double r = (display['x'] + display['w']).toDouble();
|
||||
double b = (display['y'] + display['h']).toDouble();
|
||||
screenRectList.add(Rect.fromLTRB(l, t, r, b));
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Failed to parse displays: $e');
|
||||
}
|
||||
return screenRectList;
|
||||
}
|
||||
|
||||
Future<List<Rect>> getScreenRectList() async {
|
||||
return bind.mainCurrentIsWayland()
|
||||
? await getScreenListWayland()
|
||||
: await getScreenListNotWayland();
|
||||
}
|
||||
|
||||
openMonitorInTheSameTab(int i, FFI ffi, PeerInfo pi) {
|
||||
final displays = i == kAllDisplayValue
|
||||
? List.generate(pi.displays.length, (index) => index)
|
||||
: [i];
|
||||
bind.sessionSwitchDisplay(
|
||||
sessionId: ffi.sessionId, value: Int32List.fromList(displays));
|
||||
ffi.ffiModel.switchToNewDisplay(i, ffi.sessionId, ffi.id);
|
||||
}
|
||||
|
||||
// Open new tab or window to show this monitor.
|
||||
// For now just open new window.
|
||||
//
|
||||
// screenRect is used to move the new window to the specified screen and set fullscreen.
|
||||
openMonitorInNewTabOrWindow(int i, String peerId, PeerInfo pi,
|
||||
{Rect? screenRect}) {
|
||||
final args = {
|
||||
'window_id': stateGlobal.windowId,
|
||||
'peer_id': peerId,
|
||||
'display': i,
|
||||
'display_count': pi.displays.length,
|
||||
};
|
||||
if (screenRect != null) {
|
||||
args['screen_rect'] = {
|
||||
'l': screenRect.left,
|
||||
't': screenRect.top,
|
||||
'r': screenRect.right,
|
||||
'b': screenRect.bottom,
|
||||
};
|
||||
}
|
||||
DesktopMultiWindow.invokeMethod(
|
||||
kMainWindowId, kWindowEventOpenMonitorSession, jsonEncode(args));
|
||||
}
|
||||
|
||||
tryMoveToScreenAndSetFullscreen(Rect? screenRect) async {
|
||||
if (screenRect == null) {
|
||||
return;
|
||||
}
|
||||
final frame =
|
||||
Rect.fromLTWH(screenRect.left + 30, screenRect.top + 30, 600, 400);
|
||||
await WindowController.fromWindowId(stateGlobal.windowId).setFrame(frame);
|
||||
// An duration is needed to avoid the window being restored after fullscreen.
|
||||
Future.delayed(Duration(milliseconds: 300), () async {
|
||||
stateGlobal.setFullscreen(true);
|
||||
});
|
||||
}
|
||||
|
||||
@ -29,6 +29,7 @@ const String kAppTypeDesktopPortForward = "port forward";
|
||||
|
||||
const String kWindowMainWindowOnTop = "main_window_on_top";
|
||||
const String kWindowGetWindowInfo = "get_window_info";
|
||||
const String kWindowGetScreenList = "get_screen_list";
|
||||
const String kWindowDisableGrabKeyboard = "disable_grab_keyboard";
|
||||
const String kWindowActionRebuild = "rebuild";
|
||||
const String kWindowEventHide = "hide";
|
||||
@ -65,6 +66,7 @@ const String kPointerEventKindTouch = "touch";
|
||||
const String kPointerEventKindMouse = "mouse";
|
||||
|
||||
const String kKeyShowDisplaysAsIndividualWindows = 'displays_as_individual_windows';
|
||||
const String kKeyUseAllMyMonitorsWhenConnecting = 'use_all_my_monitors_when_connecting';
|
||||
const String kKeyShowMonitorsToolbar = 'show_monitors_toolbar';
|
||||
|
||||
// the executable name of the portable version
|
||||
|
||||
@ -329,8 +329,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
"Click to download", () async {
|
||||
final Uri url = Uri.parse('https://rustdesk.com/download');
|
||||
await launchUrl(url);
|
||||
},
|
||||
closeButton: true);
|
||||
}, closeButton: true);
|
||||
}
|
||||
if (systemError.isNotEmpty) {
|
||||
return buildInstallCard("", systemError, "", () {});
|
||||
@ -397,7 +396,6 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
Widget buildInstallCard(String title, String content, String btnText,
|
||||
GestureTapCallback onPressed,
|
||||
{String? help, String? link, bool? closeButton}) {
|
||||
|
||||
void closeCard() {
|
||||
setState(() {
|
||||
isCardClosed = true;
|
||||
@ -555,6 +553,22 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
Get.put<RxBool>(svcStopped, tag: 'stop-service');
|
||||
rustDeskWinManager.registerActiveWindowListener(onActiveWindowChanged);
|
||||
|
||||
screenToMap(window_size.Screen screen) => {
|
||||
'frame': {
|
||||
'l': screen.frame.left,
|
||||
't': screen.frame.top,
|
||||
'r': screen.frame.right,
|
||||
'b': screen.frame.bottom,
|
||||
},
|
||||
'visibleFrame': {
|
||||
'l': screen.visibleFrame.left,
|
||||
't': screen.visibleFrame.top,
|
||||
'r': screen.visibleFrame.right,
|
||||
'b': screen.visibleFrame.bottom,
|
||||
},
|
||||
'scaleFactor': screen.scaleFactor,
|
||||
};
|
||||
|
||||
rustDeskWinManager.setMethodHandler((call, fromWindowId) async {
|
||||
debugPrint(
|
||||
"[Main] call ${call.method} with args ${call.arguments} from window $fromWindowId");
|
||||
@ -563,24 +577,13 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
} else if (call.method == kWindowGetWindowInfo) {
|
||||
final screen = (await window_size.getWindowInfo()).screen;
|
||||
if (screen == null) {
|
||||
return "";
|
||||
return '';
|
||||
} else {
|
||||
return jsonEncode({
|
||||
'frame': {
|
||||
'l': screen.frame.left,
|
||||
't': screen.frame.top,
|
||||
'r': screen.frame.right,
|
||||
'b': screen.frame.bottom,
|
||||
},
|
||||
'visibleFrame': {
|
||||
'l': screen.visibleFrame.left,
|
||||
't': screen.visibleFrame.top,
|
||||
'r': screen.visibleFrame.right,
|
||||
'b': screen.visibleFrame.bottom,
|
||||
},
|
||||
'scaleFactor': screen.scaleFactor,
|
||||
});
|
||||
return jsonEncode(screenToMap(screen));
|
||||
}
|
||||
} else if (call.method == kWindowGetScreenList) {
|
||||
return jsonEncode(
|
||||
(await window_size.getScreenList()).map(screenToMap).toList());
|
||||
} else if (call.method == kWindowActionRebuild) {
|
||||
reloadCurrentWindow();
|
||||
} else if (call.method == kWindowEventShow) {
|
||||
@ -613,8 +616,9 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
final peerId = args['peer_id'] as String;
|
||||
final display = args['display'] as int;
|
||||
final displayCount = args['display_count'] as int;
|
||||
final screenRect = args['screen_rect'];
|
||||
await rustDeskWinManager.openMonitorSession(
|
||||
windowId, peerId, display, displayCount);
|
||||
windowId, peerId, display, displayCount, screenRect);
|
||||
}
|
||||
});
|
||||
_uniLinksSubscription = listenUniLinks();
|
||||
|
||||
@ -1324,6 +1324,8 @@ class _DisplayState extends State<_Display> {
|
||||
if (useTextureRender) {
|
||||
children.add(otherRow('Show displays as individual windows',
|
||||
kKeyShowDisplaysAsIndividualWindows));
|
||||
children.add(otherRow('Use all my displays when connecting',
|
||||
kKeyUseAllMyMonitorsWhenConnecting));
|
||||
}
|
||||
return _Card(title: 'Other Default Options', children: children);
|
||||
}
|
||||
|
||||
@ -48,6 +48,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
|
||||
late ToolbarState _toolbarState;
|
||||
String? peerId;
|
||||
bool isScreenRectSet = false;
|
||||
|
||||
var connectionMap = RxList<Widget>.empty(growable: true);
|
||||
|
||||
@ -59,6 +60,9 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
final tabWindowId = params['tab_window_id'];
|
||||
final display = params['display'];
|
||||
final displays = params['displays'];
|
||||
final screenRect = parseScreenRect(params);
|
||||
isScreenRectSet = screenRect != null;
|
||||
tryMoveToScreenAndSetFullscreen(screenRect);
|
||||
if (peerId != null) {
|
||||
ConnectionTypeState.init(peerId!);
|
||||
tabController.onSelected = (id) {
|
||||
@ -95,6 +99,18 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
}
|
||||
}
|
||||
|
||||
parseScreenRect(Map<String, dynamic> params) {
|
||||
Rect? screenRect;
|
||||
if (params['screen_rect'] != null) {
|
||||
double l = params['screen_rect']['l'];
|
||||
double t = params['screen_rect']['t'];
|
||||
double r = params['screen_rect']['r'];
|
||||
double b = params['screen_rect']['b'];
|
||||
screenRect = Rect.fromLTRB(l, t, r, b);
|
||||
}
|
||||
return screenRect;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@ -115,7 +131,9 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
final tabWindowId = args['tab_window_id'];
|
||||
final display = args['display'];
|
||||
final displays = args['displays'];
|
||||
final screenRect = parseScreenRect(args);
|
||||
windowOnTop(windowId());
|
||||
tryMoveToScreenAndSetFullscreen(screenRect);
|
||||
if (tabController.length == 0) {
|
||||
if (Platform.isMacOS && stateGlobal.closeOnFullscreen) {
|
||||
stateGlobal.setFullscreen(true);
|
||||
@ -196,15 +214,17 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
_update_remote_count();
|
||||
return returnValue;
|
||||
});
|
||||
Future.delayed(Duration.zero, () {
|
||||
restoreWindowPosition(
|
||||
WindowType.RemoteDesktop,
|
||||
windowId: windowId(),
|
||||
peerId: tabController.state.value.tabs.isEmpty
|
||||
? null
|
||||
: tabController.state.value.tabs[0].key,
|
||||
);
|
||||
});
|
||||
if (!isScreenRectSet) {
|
||||
Future.delayed(Duration.zero, () {
|
||||
restoreWindowPosition(
|
||||
WindowType.RemoteDesktop,
|
||||
windowId: windowId(),
|
||||
peerId: tabController.state.value.tabs.isEmpty
|
||||
? null
|
||||
: tabController.state.value.tabs[0].key,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@ -451,6 +471,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
c++;
|
||||
}
|
||||
}
|
||||
|
||||
loopCloseWindow();
|
||||
}
|
||||
ConnectionTypeState.delete(id);
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hbb/common/widgets/toolbar.dart';
|
||||
import 'package:flutter_hbb/main.dart';
|
||||
import 'package:flutter_hbb/models/chat_model.dart';
|
||||
import 'package:flutter_hbb/models/state_model.dart';
|
||||
import 'package:flutter_hbb/models/desktop_render_texture.dart';
|
||||
@ -744,42 +742,14 @@ class _MonitorMenu extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
// Open new tab or window to show this monitor.
|
||||
// For now just open new window.
|
||||
openMonitorInNewTabOrWindow(int i, PeerInfo pi) {
|
||||
if (kWindowId == null) {
|
||||
// unreachable
|
||||
debugPrint('openMonitorInNewTabOrWindow, unreachable! kWindowId is null');
|
||||
return;
|
||||
}
|
||||
DesktopMultiWindow.invokeMethod(
|
||||
kMainWindowId,
|
||||
kWindowEventOpenMonitorSession,
|
||||
jsonEncode({
|
||||
'window_id': kWindowId!,
|
||||
'peer_id': ffi.id,
|
||||
'display': i,
|
||||
'display_count': pi.displays.length,
|
||||
}));
|
||||
}
|
||||
|
||||
openMonitorInTheSameTab(int i, PeerInfo pi) {
|
||||
final displays = i == kAllDisplayValue
|
||||
? List.generate(pi.displays.length, (index) => index)
|
||||
: [i];
|
||||
bind.sessionSwitchDisplay(
|
||||
sessionId: ffi.sessionId, value: Int32List.fromList(displays));
|
||||
ffi.ffiModel.switchToNewDisplay(i, ffi.sessionId, id);
|
||||
}
|
||||
|
||||
onPressed(int i, PeerInfo pi) {
|
||||
_menuDismissCallback(ffi);
|
||||
RxInt display = CurrentDisplayState.find(id);
|
||||
if (display.value != i) {
|
||||
if (isChooseDisplayToOpenInNewWindow(pi, ffi.sessionId)) {
|
||||
openMonitorInNewTabOrWindow(i, pi);
|
||||
openMonitorInNewTabOrWindow(i, ffi.id, pi);
|
||||
} else {
|
||||
openMonitorInTheSameTab(i, pi);
|
||||
openMonitorInTheSameTab(i, ffi, pi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,8 +198,11 @@ void runMultiWindow(
|
||||
}
|
||||
switch (appType) {
|
||||
case kAppTypeDesktopRemote:
|
||||
await restoreWindowPosition(WindowType.RemoteDesktop,
|
||||
windowId: kWindowId!, peerId: argument['id'] as String?);
|
||||
// If screen rect is set, the window will be moved to the target screen and then set fullscreen.
|
||||
if (argument['screen_rect'] == null) {
|
||||
await restoreWindowPosition(WindowType.RemoteDesktop,
|
||||
windowId: kWindowId!, peerId: argument['id'] as String?);
|
||||
}
|
||||
break;
|
||||
case kAppTypeDesktopFileTransfer:
|
||||
await restoreWindowPosition(WindowType.FileTransfer,
|
||||
|
||||
@ -227,7 +227,7 @@ class FfiModel with ChangeNotifier {
|
||||
}, sessionId, peerId);
|
||||
updatePrivacyMode(data.updatePrivacyMode, sessionId, peerId);
|
||||
setConnectionType(peerId, data.secure, data.direct);
|
||||
await handlePeerInfo(data.peerInfo, peerId);
|
||||
await handlePeerInfo(data.peerInfo, peerId, true);
|
||||
for (final element in data.cursorDataList) {
|
||||
updateLastCursorId(element);
|
||||
await handleCursorData(element);
|
||||
@ -245,7 +245,7 @@ class FfiModel with ChangeNotifier {
|
||||
if (name == 'msgbox') {
|
||||
handleMsgBox(evt, sessionId, peerId);
|
||||
} else if (name == 'peer_info') {
|
||||
handlePeerInfo(evt, peerId);
|
||||
handlePeerInfo(evt, peerId, false);
|
||||
} else if (name == 'sync_peer_info') {
|
||||
handleSyncPeerInfo(evt, sessionId, peerId);
|
||||
} else if (name == 'connection_ready') {
|
||||
@ -623,7 +623,7 @@ class FfiModel with ChangeNotifier {
|
||||
}
|
||||
|
||||
/// Handle the peer info event based on [evt].
|
||||
handlePeerInfo(Map<String, dynamic> evt, String peerId) async {
|
||||
handlePeerInfo(Map<String, dynamic> evt, String peerId, bool isCache) async {
|
||||
cachedPeerData.peerInfo = evt;
|
||||
|
||||
// recent peer updated by handle_peer_info(ui_session_interface.rs) --> handle_peer_info(client.rs) --> save_config(client.rs)
|
||||
@ -703,6 +703,51 @@ class FfiModel with ChangeNotifier {
|
||||
stateGlobal.resetLastResolutionGroupValues(peerId);
|
||||
|
||||
notifyListeners();
|
||||
|
||||
if (!isCache) {
|
||||
tryUseAllMyDisplaysWhenConnecting(peerId);
|
||||
}
|
||||
}
|
||||
|
||||
tryUseAllMyDisplaysWhenConnecting(String peerId) async {
|
||||
if (bind.mainGetUserDefaultOption(
|
||||
key: kKeyUseAllMyMonitorsWhenConnecting) !=
|
||||
'Y') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_pi.isSupportMultiDisplay || _pi.displays.length <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
final screenRectList = await getScreenRectList();
|
||||
if (screenRectList.length <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// to-do: peer currentDisplay is the primary display, but the primary display may not be the first display.
|
||||
// local primary display also may not be the first display.
|
||||
//
|
||||
// 0 is assumed to be the primary display here, for now.
|
||||
|
||||
// move to the first display and set fullscreen
|
||||
bind.sessionSwitchDisplay(
|
||||
sessionId: sessionId, value: Int32List.fromList([0]));
|
||||
_pi.currentDisplay = 0;
|
||||
try {
|
||||
CurrentDisplayState.find(peerId).value = _pi.currentDisplay;
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
await tryMoveToScreenAndSetFullscreen(screenRectList[0]);
|
||||
|
||||
final length = _pi.displays.length < screenRectList.length
|
||||
? _pi.displays.length
|
||||
: screenRectList.length;
|
||||
for (var i = 1; i < length; i++) {
|
||||
openMonitorInNewTabOrWindow(i, peerId, _pi,
|
||||
screenRect: screenRectList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
tryShowAndroidActionsOverlay({int delayMSecs = 10}) {
|
||||
|
||||
@ -64,13 +64,14 @@ class RustDeskMultiWindowManager {
|
||||
peerId,
|
||||
_remoteDesktopWindows,
|
||||
jsonEncode(params),
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
// This function must be called in the main window thread.
|
||||
// Because the _remoteDesktopWindows is managed in that thread.
|
||||
openMonitorSession(
|
||||
int windowId, String peerId, int display, int displayCount) async {
|
||||
openMonitorSession(int windowId, String peerId, int display, int displayCount,
|
||||
dynamic screenRect) async {
|
||||
if (_remoteDesktopWindows.length > 1) {
|
||||
for (final windowId in _remoteDesktopWindows) {
|
||||
if (await DesktopMultiWindow.invokeMethod(
|
||||
@ -94,6 +95,7 @@ class RustDeskMultiWindowManager {
|
||||
'tab_window_id': windowId,
|
||||
'display': display,
|
||||
'displays': displays,
|
||||
'screen_rect': screenRect,
|
||||
};
|
||||
await _newSession(
|
||||
false,
|
||||
@ -102,21 +104,34 @@ class RustDeskMultiWindowManager {
|
||||
peerId,
|
||||
_remoteDesktopWindows,
|
||||
jsonEncode(params),
|
||||
screenRect != null,
|
||||
);
|
||||
}
|
||||
|
||||
Future<int> newSessionWindow(
|
||||
WindowType type, String remoteId, String msg, List<int> windows) async {
|
||||
WindowType type,
|
||||
String remoteId,
|
||||
String msg,
|
||||
List<int> windows,
|
||||
bool withScreenRect,
|
||||
) async {
|
||||
final windowController = await DesktopMultiWindow.createWindow(msg);
|
||||
final windowId = windowController.windowId;
|
||||
windowController
|
||||
..setFrame(
|
||||
const Offset(0, 0) & Size(1280 + windowId * 20, 720 + windowId * 20))
|
||||
..center()
|
||||
..setTitle(getWindowNameWithId(
|
||||
if (!withScreenRect) {
|
||||
windowController
|
||||
..setFrame(const Offset(0, 0) &
|
||||
Size(1280 + windowId * 20, 720 + windowId * 20))
|
||||
..center()
|
||||
..setTitle(getWindowNameWithId(
|
||||
remoteId,
|
||||
overrideType: type,
|
||||
));
|
||||
} else {
|
||||
windowController.setTitle(getWindowNameWithId(
|
||||
remoteId,
|
||||
overrideType: type,
|
||||
));
|
||||
}
|
||||
if (Platform.isMacOS) {
|
||||
Future.microtask(() => windowController.show());
|
||||
}
|
||||
@ -132,10 +147,12 @@ class RustDeskMultiWindowManager {
|
||||
String remoteId,
|
||||
List<int> windows,
|
||||
String msg,
|
||||
bool withScreenRect,
|
||||
) async {
|
||||
if (openInTabs) {
|
||||
if (windows.isEmpty) {
|
||||
final windowId = await newSessionWindow(type, remoteId, msg, windows);
|
||||
final windowId = await newSessionWindow(
|
||||
type, remoteId, msg, windows, withScreenRect);
|
||||
return MultiWindowCallResult(windowId, null);
|
||||
} else {
|
||||
return call(type, methodName, msg);
|
||||
@ -153,7 +170,8 @@ class RustDeskMultiWindowManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
final windowId = await newSessionWindow(type, remoteId, msg, windows);
|
||||
final windowId =
|
||||
await newSessionWindow(type, remoteId, msg, windows, withScreenRect);
|
||||
return MultiWindowCallResult(windowId, null);
|
||||
}
|
||||
}
|
||||
@ -195,7 +213,8 @@ class RustDeskMultiWindowManager {
|
||||
}
|
||||
}
|
||||
|
||||
return _newSession(openInTabs, type, methodName, remoteId, windows, msg);
|
||||
return _newSession(
|
||||
openInTabs, type, methodName, remoteId, windows, msg, false);
|
||||
}
|
||||
|
||||
Future<MultiWindowCallResult> newRemoteDesktop(
|
||||
|
||||
@ -290,6 +290,12 @@ pub struct PeerConfig {
|
||||
skip_serializing_if = "String::is_empty"
|
||||
)]
|
||||
pub displays_as_individual_windows: String,
|
||||
#[serde(
|
||||
default = "PeerConfig::default_use_all_my_monitors_when_connecting",
|
||||
deserialize_with = "PeerConfig::deserialize_use_all_my_monitors_when_connecting",
|
||||
skip_serializing_if = "String::is_empty"
|
||||
)]
|
||||
pub use_all_my_monitors_when_connecting: String,
|
||||
|
||||
#[serde(
|
||||
default,
|
||||
@ -335,6 +341,8 @@ impl Default for PeerConfig {
|
||||
view_only: Default::default(),
|
||||
reverse_mouse_wheel: Self::default_reverse_mouse_wheel(),
|
||||
displays_as_individual_windows: Self::default_displays_as_individual_windows(),
|
||||
use_all_my_monitors_when_connecting: Self::default_use_all_my_monitors_when_connecting(
|
||||
),
|
||||
custom_resolutions: Default::default(),
|
||||
options: Self::default_options(),
|
||||
ui_flutter: Default::default(),
|
||||
@ -1156,6 +1164,11 @@ impl PeerConfig {
|
||||
deserialize_displays_as_individual_windows,
|
||||
UserDefaultConfig::read().get("displays_as_individual_windows")
|
||||
);
|
||||
serde_field_string!(
|
||||
default_use_all_my_monitors_when_connecting,
|
||||
deserialize_use_all_my_monitors_when_connecting,
|
||||
UserDefaultConfig::read().get("use_all_my_monitors_when_connecting")
|
||||
);
|
||||
|
||||
fn default_custom_image_quality() -> Vec<i32> {
|
||||
let f: f64 = UserDefaultConfig::read()
|
||||
|
||||
@ -1207,7 +1207,7 @@ impl LoginConfigHandler {
|
||||
self.save_config(config);
|
||||
}
|
||||
|
||||
/// Save reverse mouse wheel ("", "Y") to the current config.
|
||||
/// Save "displays_as_individual_windows" ("", "Y") to the current config.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
@ -1218,6 +1218,17 @@ impl LoginConfigHandler {
|
||||
self.save_config(config);
|
||||
}
|
||||
|
||||
/// Save "use_all_my_monitors_when_connecting" ("", "Y") to the current config.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `value` - The "use_all_my_monitors_when_connecting" value ("", "Y").
|
||||
pub fn save_use_all_my_monitors_when_connecting(&mut self, value: String) {
|
||||
let mut config = self.load_config();
|
||||
config.use_all_my_monitors_when_connecting = value;
|
||||
self.save_config(config);
|
||||
}
|
||||
|
||||
/// Save scroll style to the current config.
|
||||
///
|
||||
/// # Arguments
|
||||
|
||||
@ -339,7 +339,9 @@ pub fn session_set_reverse_mouse_wheel(session_id: SessionID, value: String) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn session_get_displays_as_individual_windows(session_id: SessionID) -> SyncReturn<Option<String>> {
|
||||
pub fn session_get_displays_as_individual_windows(
|
||||
session_id: SessionID,
|
||||
) -> SyncReturn<Option<String>> {
|
||||
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||
SyncReturn(Some(session.get_displays_as_individual_windows()))
|
||||
} else {
|
||||
@ -353,6 +355,22 @@ pub fn session_set_displays_as_individual_windows(session_id: SessionID, value:
|
||||
}
|
||||
}
|
||||
|
||||
pub fn session_get_use_all_my_monitors_when_connecting(
|
||||
session_id: SessionID,
|
||||
) -> SyncReturn<Option<String>> {
|
||||
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||
SyncReturn(Some(session.get_use_all_my_monitors_when_connecting()))
|
||||
} else {
|
||||
SyncReturn(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn session_set_use_all_my_monitors_when_connecting(session_id: SessionID, value: String) {
|
||||
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||
session.save_use_all_my_monitors_when_connecting(value);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn session_get_custom_image_quality(session_id: SessionID) -> Option<Vec<i32>> {
|
||||
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||
Some(session.get_custom_image_quality())
|
||||
@ -1069,6 +1087,29 @@ pub fn main_get_main_display() -> SyncReturn<String> {
|
||||
SyncReturn(display_info)
|
||||
}
|
||||
|
||||
pub fn main_get_displays() -> SyncReturn<String> {
|
||||
#[cfg(target_os = "ios")]
|
||||
let display_info = "".to_owned();
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
let mut display_info = "".to_owned();
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
if let Ok(displays) = crate::display_service::try_get_displays() {
|
||||
let displays = displays
|
||||
.iter()
|
||||
.map(|d| {
|
||||
HashMap::from([
|
||||
("x", d.origin().0),
|
||||
("y", d.origin().1),
|
||||
("w", d.width() as i32),
|
||||
("h", d.height() as i32),
|
||||
])
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
display_info = serde_json::to_string(&displays).unwrap_or_default();
|
||||
}
|
||||
SyncReturn(display_info)
|
||||
}
|
||||
|
||||
pub fn session_add_port_forward(
|
||||
session_id: SessionID,
|
||||
local_port: i32,
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", "切换到主显示器,因为提权后,不支持多显示器画面。"),
|
||||
("Open in new window", "在新的窗口中打开"),
|
||||
("Show displays as individual windows", "在单个窗口中打开显示器"),
|
||||
("Use all my displays when connecting", "建立连接时使用我的所有显示器"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", "Pindah ke tampilan utama, pada mode elevasi, pengggunaan lebih dari satu layar tidak diizinkan"),
|
||||
("Open in new window", "Buka di jendela baru"),
|
||||
("Show displays as individual windows", "Tampilkan dengan jendela terpisah"),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -565,5 +565,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", "Passo allo schermo principale perché in modalità elevata non sono supportati più schermi."),
|
||||
("Open in new window", "Apri in una nuova finestra"),
|
||||
("Show displays as individual windows", "Visualizza schermi come finestre individuali"),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", "Pārslēdzieties uz primāro displeju, jo paaugstinātajā režīmā netiek atbalstīti vairāki displeji."),
|
||||
("Open in new window", "Atvērt jaunā logā"),
|
||||
("Show displays as individual windows", "Rādīt displejus kā atsevišķus logus"),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", "Przełącz się na ekran główny, ponieważ wyświetlanie kilku ekranów nie jest obsługiwane przy podniesionych uprawnieniach."),
|
||||
("Open in new window", "Otwórz w nowym oknie"),
|
||||
("Show displays as individual windows", "Pokaż ekrany w osobnych oknach"),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -564,5 +564,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevated_switch_display_msg", ""),
|
||||
("Open in new window", ""),
|
||||
("Show displays as individual windows", ""),
|
||||
("Use all my displays when connecting", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@ -240,6 +240,10 @@ impl<T: InvokeUiSession> Session<T> {
|
||||
self.lc.read().unwrap().displays_as_individual_windows.clone()
|
||||
}
|
||||
|
||||
pub fn get_use_all_my_monitors_when_connecting(&self) -> String {
|
||||
self.lc.read().unwrap().use_all_my_monitors_when_connecting.clone()
|
||||
}
|
||||
|
||||
pub fn save_reverse_mouse_wheel(&self, value: String) {
|
||||
self.lc.write().unwrap().save_reverse_mouse_wheel(value);
|
||||
}
|
||||
@ -248,6 +252,10 @@ impl<T: InvokeUiSession> Session<T> {
|
||||
self.lc.write().unwrap().save_displays_as_individual_windows(value);
|
||||
}
|
||||
|
||||
pub fn save_use_all_my_monitors_when_connecting(&self, value: String) {
|
||||
self.lc.write().unwrap().save_use_all_my_monitors_when_connecting(value);
|
||||
}
|
||||
|
||||
pub fn save_view_style(&self, value: String) {
|
||||
self.lc.write().unwrap().save_view_style(value);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user