mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
refactor windows specific session (#7170)
1. Modify the process to have the control side lead the session switching: After the control side sends a `LoginRequest`, the controlled side will add all session information and the current session ID in the `LoginResponse`. Upon receiving the `LoginResponse`, the control side will check if the current session ID matches the ID in the `LoginConfigHandler`. If they match, the control side will send the current session ID. If they don't match, a session selection dialog will pop up, the selected session id will be sent. Upon receiving this message, the controlled side will restart if different or sub service if same . 2. Always show physical console session on the top 3. Show running session and distinguish sessions with the same name 4. Not sub service until correct session id is ensured 5. Fix switch sides not work for multisession session 6. Remove all session string join/split except get_available_sessions in windows.rs 7. Fix prelogin, when share rdp is enabled and there is a rdp session, the console is in login screen, get_active_username will be the rdp's username and prelogin will be false, cm can't be created an that causes disconnection in a loop 8. Rename all user session to windows session Known issue: 1. Use current process session id for `run_as_user`, sahil says it can be wrong but I didn't reproduce. 2. Have not change tray process to current session 3. File transfer doesn't update home directory when session changed 4. When it's in login screen, remote file directory is empty, because cm have not start up Signed-off-by: 21pages <pages21@163.com>
This commit is contained in:
@@ -2990,3 +2990,83 @@ ColorFilter? svgColor(Color? color) {
|
||||
return ColorFilter.mode(color, BlendMode.srcIn);
|
||||
}
|
||||
}
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class ComboBox extends StatelessWidget {
|
||||
late final List<String> keys;
|
||||
late final List<String> values;
|
||||
late final String initialKey;
|
||||
late final Function(String key) onChanged;
|
||||
late final bool enabled;
|
||||
late String current;
|
||||
|
||||
ComboBox({
|
||||
Key? key,
|
||||
required this.keys,
|
||||
required this.values,
|
||||
required this.initialKey,
|
||||
required this.onChanged,
|
||||
this.enabled = true,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var index = keys.indexOf(initialKey);
|
||||
if (index < 0) {
|
||||
index = 0;
|
||||
}
|
||||
var ref = values[index].obs;
|
||||
current = keys[index];
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: enabled
|
||||
? MyTheme.color(context).border2 ?? MyTheme.border
|
||||
: MyTheme.border,
|
||||
),
|
||||
borderRadius:
|
||||
BorderRadius.circular(8), //border raiuds of dropdown button
|
||||
),
|
||||
height: 42, // should be the height of a TextField
|
||||
child: Obx(() => DropdownButton<String>(
|
||||
isExpanded: true,
|
||||
value: ref.value,
|
||||
elevation: 16,
|
||||
underline: Container(),
|
||||
style: TextStyle(
|
||||
color: enabled
|
||||
? Theme.of(context).textTheme.titleMedium?.color
|
||||
: disabledTextColor(context, enabled)),
|
||||
icon: const Icon(
|
||||
Icons.expand_more_sharp,
|
||||
size: 20,
|
||||
).marginOnly(right: 15),
|
||||
onChanged: enabled
|
||||
? (String? newValue) {
|
||||
if (newValue != null && newValue != ref.value) {
|
||||
ref.value = newValue;
|
||||
current = newValue;
|
||||
onChanged(keys[values.indexOf(newValue)]);
|
||||
}
|
||||
}
|
||||
: null,
|
||||
items: values.map<DropdownMenuItem<String>>((String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(
|
||||
value,
|
||||
style: const TextStyle(fontSize: 15),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
).marginOnly(left: 15),
|
||||
);
|
||||
}).toList(),
|
||||
)),
|
||||
).marginOnly(bottom: 5);
|
||||
}
|
||||
}
|
||||
|
||||
Color? disabledTextColor(BuildContext context, bool enabled) {
|
||||
return enabled
|
||||
? null
|
||||
: Theme.of(context).textTheme.titleLarge?.color?.withOpacity(0.6);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -1862,6 +1863,7 @@ void enter2FaDialog(
|
||||
});
|
||||
}
|
||||
|
||||
// This dialog should not be dismissed, otherwise it will be black screen, have not reproduced this.
|
||||
void showWindowsSessionsDialog(
|
||||
String type,
|
||||
String title,
|
||||
@@ -1870,97 +1872,40 @@ void showWindowsSessionsDialog(
|
||||
SessionID sessionId,
|
||||
String peerId,
|
||||
String sessions) {
|
||||
List<String> sessionsList = sessions.split(',');
|
||||
Map<String, String> sessionMap = {};
|
||||
for (var session in sessionsList) {
|
||||
var sessionInfo = session.split('-');
|
||||
if (sessionInfo.isNotEmpty) {
|
||||
sessionMap[sessionInfo[0]] = sessionInfo[1];
|
||||
}
|
||||
List<dynamic> sessionsList = [];
|
||||
try {
|
||||
sessionsList = json.decode(sessions);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
String selectedUserValue = sessionMap.keys.first;
|
||||
List<String> sids = [];
|
||||
List<String> names = [];
|
||||
for (var session in sessionsList) {
|
||||
sids.add(session['sid']);
|
||||
names.add(session['name']);
|
||||
}
|
||||
String selectedUserValue = sids.first;
|
||||
dialogManager.dismissAll();
|
||||
dialogManager.show((setState, close, context) {
|
||||
onConnect() {
|
||||
bind.sessionReconnect(
|
||||
sessionId: sessionId,
|
||||
forceRelay: false,
|
||||
userSessionId: selectedUserValue);
|
||||
dialogManager.dismissAll();
|
||||
dialogManager.showLoading(translate('Connecting...'),
|
||||
onCancel: closeConnection);
|
||||
submit() {
|
||||
bind.sessionSendSelectedSessionId(
|
||||
sessionId: sessionId, sid: selectedUserValue);
|
||||
close();
|
||||
}
|
||||
|
||||
return CustomAlertDialog(
|
||||
title: null,
|
||||
content: msgboxContent(type, title, text),
|
||||
actions: [
|
||||
SessionsDropdown(peerId, sessionId, sessionMap, (value) {
|
||||
setState(() {
|
||||
selectedUserValue = value;
|
||||
});
|
||||
}),
|
||||
dialogButton('Connect', onPressed: onConnect, isOutline: false),
|
||||
ComboBox(
|
||||
keys: sids,
|
||||
values: names,
|
||||
initialKey: selectedUserValue,
|
||||
onChanged: (value) {
|
||||
selectedUserValue = value;
|
||||
}),
|
||||
dialogButton('Connect', onPressed: submit, isOutline: false),
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
class SessionsDropdown extends StatefulWidget {
|
||||
final String peerId;
|
||||
final SessionID sessionId;
|
||||
final Map<String, String> sessions;
|
||||
final Function(String) onValueChanged;
|
||||
|
||||
SessionsDropdown(
|
||||
this.peerId, this.sessionId, this.sessions, this.onValueChanged);
|
||||
|
||||
@override
|
||||
_SessionsDropdownState createState() => _SessionsDropdownState();
|
||||
}
|
||||
|
||||
class _SessionsDropdownState extends State<SessionsDropdown> {
|
||||
late String selectedValue;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
selectedValue = widget.sessions.keys.first;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: 300,
|
||||
child: DropdownButton<String>(
|
||||
value: selectedValue,
|
||||
isExpanded: true,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||
items: widget.sessions.entries.map((entry) {
|
||||
return DropdownMenuItem(
|
||||
value: entry.key,
|
||||
child: Text(
|
||||
entry.value,
|
||||
style: TextStyle(
|
||||
color: MyTheme.currentThemeMode() == ThemeMode.dark
|
||||
? Colors.white
|
||||
: MyTheme.dark,
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
setState(() {
|
||||
selectedValue = value;
|
||||
});
|
||||
widget.onValueChanged(value);
|
||||
}
|
||||
},
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -514,7 +514,7 @@ class _GeneralState extends State<_General> {
|
||||
if (!keys.contains(currentKey)) {
|
||||
currentKey = '';
|
||||
}
|
||||
return _ComboBox(
|
||||
return ComboBox(
|
||||
keys: keys,
|
||||
values: values,
|
||||
initialKey: currentKey,
|
||||
@@ -600,7 +600,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
child: Text(
|
||||
translate('enable-2fa-title'),
|
||||
style:
|
||||
TextStyle(color: _disabledTextColor(context, enabled)),
|
||||
TextStyle(color: disabledTextColor(context, enabled)),
|
||||
))
|
||||
],
|
||||
)),
|
||||
@@ -654,7 +654,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
}
|
||||
|
||||
return _Card(title: 'Permissions', children: [
|
||||
_ComboBox(
|
||||
ComboBox(
|
||||
keys: [
|
||||
'',
|
||||
'full',
|
||||
@@ -761,7 +761,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
Text(
|
||||
value,
|
||||
style: TextStyle(
|
||||
color: _disabledTextColor(
|
||||
color: disabledTextColor(
|
||||
context, onChanged != null)),
|
||||
),
|
||||
],
|
||||
@@ -781,7 +781,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
final usePassword = model.approveMode != 'click';
|
||||
|
||||
return _Card(title: 'Password', children: [
|
||||
_ComboBox(
|
||||
ComboBox(
|
||||
enabled: !locked,
|
||||
keys: modeKeys,
|
||||
values: modeValues,
|
||||
@@ -841,7 +841,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
Expanded(
|
||||
child: Text(translate('Enable RDP session sharing'),
|
||||
style:
|
||||
TextStyle(color: _disabledTextColor(context, enabled))),
|
||||
TextStyle(color: disabledTextColor(context, enabled))),
|
||||
)
|
||||
],
|
||||
).marginOnly(left: _kCheckBoxLeftMargin),
|
||||
@@ -944,7 +944,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
child: Text(
|
||||
translate('Use IP Whitelisting'),
|
||||
style:
|
||||
TextStyle(color: _disabledTextColor(context, enabled)),
|
||||
TextStyle(color: disabledTextColor(context, enabled)),
|
||||
))
|
||||
],
|
||||
)),
|
||||
@@ -988,7 +988,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
child: Text(
|
||||
translate('Hide connection management window'),
|
||||
style: TextStyle(
|
||||
color: _disabledTextColor(
|
||||
color: disabledTextColor(
|
||||
context, enabled && enableHideCm)),
|
||||
),
|
||||
),
|
||||
@@ -1686,12 +1686,6 @@ Widget _Card(
|
||||
);
|
||||
}
|
||||
|
||||
Color? _disabledTextColor(BuildContext context, bool enabled) {
|
||||
return enabled
|
||||
? null
|
||||
: Theme.of(context).textTheme.titleLarge?.color?.withOpacity(0.6);
|
||||
}
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
Widget _OptionCheckBox(BuildContext context, String label, String key,
|
||||
{Function()? update,
|
||||
@@ -1740,7 +1734,7 @@ Widget _OptionCheckBox(BuildContext context, String label, String key,
|
||||
Expanded(
|
||||
child: Text(
|
||||
translate(label),
|
||||
style: TextStyle(color: _disabledTextColor(context, enabled)),
|
||||
style: TextStyle(color: disabledTextColor(context, enabled)),
|
||||
))
|
||||
],
|
||||
),
|
||||
@@ -1777,7 +1771,7 @@ Widget _Radio<T>(BuildContext context,
|
||||
overflow: autoNewLine ? null : TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: _kContentFontSize,
|
||||
color: _disabledTextColor(context, enabled)))
|
||||
color: disabledTextColor(context, enabled)))
|
||||
.marginOnly(left: 5),
|
||||
),
|
||||
],
|
||||
@@ -1827,7 +1821,7 @@ Widget _SubLabeledWidget(BuildContext context, String label, Widget child,
|
||||
children: [
|
||||
Text(
|
||||
'${translate(label)}: ',
|
||||
style: TextStyle(color: _disabledTextColor(context, enabled)),
|
||||
style: TextStyle(color: disabledTextColor(context, enabled)),
|
||||
),
|
||||
SizedBox(
|
||||
width: 10,
|
||||
@@ -1891,7 +1885,7 @@ _LabeledTextField(
|
||||
'${translate(label)}:',
|
||||
textAlign: TextAlign.right,
|
||||
style: TextStyle(
|
||||
fontSize: 16, color: _disabledTextColor(context, enabled)),
|
||||
fontSize: 16, color: disabledTextColor(context, enabled)),
|
||||
).marginOnly(right: 10)),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
@@ -1901,87 +1895,13 @@ _LabeledTextField(
|
||||
decoration: InputDecoration(
|
||||
errorText: errorText.isNotEmpty ? errorText : null),
|
||||
style: TextStyle(
|
||||
color: _disabledTextColor(context, enabled),
|
||||
color: disabledTextColor(context, enabled),
|
||||
)),
|
||||
),
|
||||
],
|
||||
).marginOnly(bottom: 8);
|
||||
}
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class _ComboBox extends StatelessWidget {
|
||||
late final List<String> keys;
|
||||
late final List<String> values;
|
||||
late final String initialKey;
|
||||
late final Function(String key) onChanged;
|
||||
late final bool enabled;
|
||||
late String current;
|
||||
|
||||
_ComboBox({
|
||||
Key? key,
|
||||
required this.keys,
|
||||
required this.values,
|
||||
required this.initialKey,
|
||||
required this.onChanged,
|
||||
this.enabled = true,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var index = keys.indexOf(initialKey);
|
||||
if (index < 0) {
|
||||
index = 0;
|
||||
}
|
||||
var ref = values[index].obs;
|
||||
current = keys[index];
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: enabled
|
||||
? MyTheme.color(context).border2 ?? MyTheme.border
|
||||
: MyTheme.border,
|
||||
),
|
||||
borderRadius:
|
||||
BorderRadius.circular(8), //border raiuds of dropdown button
|
||||
),
|
||||
height: 42, // should be the height of a TextField
|
||||
child: Obx(() => DropdownButton<String>(
|
||||
isExpanded: true,
|
||||
value: ref.value,
|
||||
elevation: 16,
|
||||
underline: Container(),
|
||||
style: TextStyle(
|
||||
color: enabled
|
||||
? Theme.of(context).textTheme.titleMedium?.color
|
||||
: _disabledTextColor(context, enabled)),
|
||||
icon: const Icon(
|
||||
Icons.expand_more_sharp,
|
||||
size: 20,
|
||||
).marginOnly(right: 15),
|
||||
onChanged: enabled
|
||||
? (String? newValue) {
|
||||
if (newValue != null && newValue != ref.value) {
|
||||
ref.value = newValue;
|
||||
current = newValue;
|
||||
onChanged(keys[values.indexOf(newValue)]);
|
||||
}
|
||||
}
|
||||
: null,
|
||||
items: values.map<DropdownMenuItem<String>>((String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(
|
||||
value,
|
||||
style: const TextStyle(fontSize: _kContentFontSize),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
).marginOnly(left: 15),
|
||||
);
|
||||
}).toList(),
|
||||
)),
|
||||
).marginOnly(bottom: 5);
|
||||
}
|
||||
}
|
||||
|
||||
class _CountDownButton extends StatefulWidget {
|
||||
_CountDownButton({
|
||||
Key? key,
|
||||
|
||||
@@ -245,8 +245,8 @@ class FfiModel with ChangeNotifier {
|
||||
var name = evt['name'];
|
||||
if (name == 'msgbox') {
|
||||
handleMsgBox(evt, sessionId, peerId);
|
||||
} else if (name == 'set_multiple_user_session') {
|
||||
handleMultipleUserSession(evt, sessionId, peerId);
|
||||
} else if (name == 'set_multiple_windows_session') {
|
||||
handleMultipleWindowsSession(evt, sessionId, peerId);
|
||||
} else if (name == 'peer_info') {
|
||||
handlePeerInfo(evt, peerId, false);
|
||||
} else if (name == 'sync_peer_info') {
|
||||
@@ -490,7 +490,7 @@ class FfiModel with ChangeNotifier {
|
||||
dialogManager.dismissByTag(tag);
|
||||
}
|
||||
|
||||
handleMultipleUserSession(
|
||||
handleMultipleWindowsSession(
|
||||
Map<String, dynamic> evt, SessionID sessionId, String peerId) {
|
||||
if (parent.target == null) return;
|
||||
final dialogManager = parent.target!.dialogManager;
|
||||
@@ -564,8 +564,7 @@ class FfiModel with ChangeNotifier {
|
||||
|
||||
void reconnect(OverlayDialogManager dialogManager, SessionID sessionId,
|
||||
bool forceRelay) {
|
||||
bind.sessionReconnect(
|
||||
sessionId: sessionId, forceRelay: forceRelay, userSessionId: "");
|
||||
bind.sessionReconnect(sessionId: sessionId, forceRelay: forceRelay);
|
||||
clearPermissions();
|
||||
dialogManager.dismissAll();
|
||||
dialogManager.showLoading(translate('Connecting...'),
|
||||
|
||||
Reference in New Issue
Block a user