mirror of
https://github.com/rustdesk/rustdesk.git
synced 2025-06-07 09:52:49 +08: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:
parent
4f1a4dc6a5
commit
0f44de7dc3
@ -2990,3 +2990,83 @@ ColorFilter? svgColor(Color? color) {
|
|||||||
return ColorFilter.mode(color, BlendMode.srcIn);
|
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:async';
|
||||||
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
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(
|
void showWindowsSessionsDialog(
|
||||||
String type,
|
String type,
|
||||||
String title,
|
String title,
|
||||||
@ -1870,97 +1872,40 @@ void showWindowsSessionsDialog(
|
|||||||
SessionID sessionId,
|
SessionID sessionId,
|
||||||
String peerId,
|
String peerId,
|
||||||
String sessions) {
|
String sessions) {
|
||||||
List<String> sessionsList = sessions.split(',');
|
List<dynamic> sessionsList = [];
|
||||||
Map<String, String> sessionMap = {};
|
try {
|
||||||
for (var session in sessionsList) {
|
sessionsList = json.decode(sessions);
|
||||||
var sessionInfo = session.split('-');
|
} catch (e) {
|
||||||
if (sessionInfo.isNotEmpty) {
|
print(e);
|
||||||
sessionMap[sessionInfo[0]] = sessionInfo[1];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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.dismissAll();
|
||||||
dialogManager.show((setState, close, context) {
|
dialogManager.show((setState, close, context) {
|
||||||
onConnect() {
|
submit() {
|
||||||
bind.sessionReconnect(
|
bind.sessionSendSelectedSessionId(
|
||||||
sessionId: sessionId,
|
sessionId: sessionId, sid: selectedUserValue);
|
||||||
forceRelay: false,
|
close();
|
||||||
userSessionId: selectedUserValue);
|
|
||||||
dialogManager.dismissAll();
|
|
||||||
dialogManager.showLoading(translate('Connecting...'),
|
|
||||||
onCancel: closeConnection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return CustomAlertDialog(
|
return CustomAlertDialog(
|
||||||
title: null,
|
title: null,
|
||||||
content: msgboxContent(type, title, text),
|
content: msgboxContent(type, title, text),
|
||||||
actions: [
|
actions: [
|
||||||
SessionsDropdown(peerId, sessionId, sessionMap, (value) {
|
ComboBox(
|
||||||
setState(() {
|
keys: sids,
|
||||||
selectedUserValue = value;
|
values: names,
|
||||||
});
|
initialKey: selectedUserValue,
|
||||||
}),
|
onChanged: (value) {
|
||||||
dialogButton('Connect', onPressed: onConnect, isOutline: false),
|
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)) {
|
if (!keys.contains(currentKey)) {
|
||||||
currentKey = '';
|
currentKey = '';
|
||||||
}
|
}
|
||||||
return _ComboBox(
|
return ComboBox(
|
||||||
keys: keys,
|
keys: keys,
|
||||||
values: values,
|
values: values,
|
||||||
initialKey: currentKey,
|
initialKey: currentKey,
|
||||||
@ -600,7 +600,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
|||||||
child: Text(
|
child: Text(
|
||||||
translate('enable-2fa-title'),
|
translate('enable-2fa-title'),
|
||||||
style:
|
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: [
|
return _Card(title: 'Permissions', children: [
|
||||||
_ComboBox(
|
ComboBox(
|
||||||
keys: [
|
keys: [
|
||||||
'',
|
'',
|
||||||
'full',
|
'full',
|
||||||
@ -761,7 +761,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
|||||||
Text(
|
Text(
|
||||||
value,
|
value,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: _disabledTextColor(
|
color: disabledTextColor(
|
||||||
context, onChanged != null)),
|
context, onChanged != null)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -781,7 +781,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
|||||||
final usePassword = model.approveMode != 'click';
|
final usePassword = model.approveMode != 'click';
|
||||||
|
|
||||||
return _Card(title: 'Password', children: [
|
return _Card(title: 'Password', children: [
|
||||||
_ComboBox(
|
ComboBox(
|
||||||
enabled: !locked,
|
enabled: !locked,
|
||||||
keys: modeKeys,
|
keys: modeKeys,
|
||||||
values: modeValues,
|
values: modeValues,
|
||||||
@ -841,7 +841,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Text(translate('Enable RDP session sharing'),
|
child: Text(translate('Enable RDP session sharing'),
|
||||||
style:
|
style:
|
||||||
TextStyle(color: _disabledTextColor(context, enabled))),
|
TextStyle(color: disabledTextColor(context, enabled))),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
).marginOnly(left: _kCheckBoxLeftMargin),
|
).marginOnly(left: _kCheckBoxLeftMargin),
|
||||||
@ -944,7 +944,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
|||||||
child: Text(
|
child: Text(
|
||||||
translate('Use IP Whitelisting'),
|
translate('Use IP Whitelisting'),
|
||||||
style:
|
style:
|
||||||
TextStyle(color: _disabledTextColor(context, enabled)),
|
TextStyle(color: disabledTextColor(context, enabled)),
|
||||||
))
|
))
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
@ -988,7 +988,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
|||||||
child: Text(
|
child: Text(
|
||||||
translate('Hide connection management window'),
|
translate('Hide connection management window'),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: _disabledTextColor(
|
color: disabledTextColor(
|
||||||
context, enabled && enableHideCm)),
|
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
|
// ignore: non_constant_identifier_names
|
||||||
Widget _OptionCheckBox(BuildContext context, String label, String key,
|
Widget _OptionCheckBox(BuildContext context, String label, String key,
|
||||||
{Function()? update,
|
{Function()? update,
|
||||||
@ -1740,7 +1734,7 @@ Widget _OptionCheckBox(BuildContext context, String label, String key,
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
translate(label),
|
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,
|
overflow: autoNewLine ? null : TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: _kContentFontSize,
|
fontSize: _kContentFontSize,
|
||||||
color: _disabledTextColor(context, enabled)))
|
color: disabledTextColor(context, enabled)))
|
||||||
.marginOnly(left: 5),
|
.marginOnly(left: 5),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -1827,7 +1821,7 @@ Widget _SubLabeledWidget(BuildContext context, String label, Widget child,
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'${translate(label)}: ',
|
'${translate(label)}: ',
|
||||||
style: TextStyle(color: _disabledTextColor(context, enabled)),
|
style: TextStyle(color: disabledTextColor(context, enabled)),
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 10,
|
width: 10,
|
||||||
@ -1891,7 +1885,7 @@ _LabeledTextField(
|
|||||||
'${translate(label)}:',
|
'${translate(label)}:',
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16, color: _disabledTextColor(context, enabled)),
|
fontSize: 16, color: disabledTextColor(context, enabled)),
|
||||||
).marginOnly(right: 10)),
|
).marginOnly(right: 10)),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextField(
|
child: TextField(
|
||||||
@ -1901,87 +1895,13 @@ _LabeledTextField(
|
|||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
errorText: errorText.isNotEmpty ? errorText : null),
|
errorText: errorText.isNotEmpty ? errorText : null),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: _disabledTextColor(context, enabled),
|
color: disabledTextColor(context, enabled),
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
).marginOnly(bottom: 8);
|
).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 {
|
class _CountDownButton extends StatefulWidget {
|
||||||
_CountDownButton({
|
_CountDownButton({
|
||||||
Key? key,
|
Key? key,
|
||||||
|
@ -245,8 +245,8 @@ class FfiModel with ChangeNotifier {
|
|||||||
var name = evt['name'];
|
var name = evt['name'];
|
||||||
if (name == 'msgbox') {
|
if (name == 'msgbox') {
|
||||||
handleMsgBox(evt, sessionId, peerId);
|
handleMsgBox(evt, sessionId, peerId);
|
||||||
} else if (name == 'set_multiple_user_session') {
|
} else if (name == 'set_multiple_windows_session') {
|
||||||
handleMultipleUserSession(evt, sessionId, peerId);
|
handleMultipleWindowsSession(evt, sessionId, peerId);
|
||||||
} else if (name == 'peer_info') {
|
} else if (name == 'peer_info') {
|
||||||
handlePeerInfo(evt, peerId, false);
|
handlePeerInfo(evt, peerId, false);
|
||||||
} else if (name == 'sync_peer_info') {
|
} else if (name == 'sync_peer_info') {
|
||||||
@ -490,7 +490,7 @@ class FfiModel with ChangeNotifier {
|
|||||||
dialogManager.dismissByTag(tag);
|
dialogManager.dismissByTag(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMultipleUserSession(
|
handleMultipleWindowsSession(
|
||||||
Map<String, dynamic> evt, SessionID sessionId, String peerId) {
|
Map<String, dynamic> evt, SessionID sessionId, String peerId) {
|
||||||
if (parent.target == null) return;
|
if (parent.target == null) return;
|
||||||
final dialogManager = parent.target!.dialogManager;
|
final dialogManager = parent.target!.dialogManager;
|
||||||
@ -564,8 +564,7 @@ class FfiModel with ChangeNotifier {
|
|||||||
|
|
||||||
void reconnect(OverlayDialogManager dialogManager, SessionID sessionId,
|
void reconnect(OverlayDialogManager dialogManager, SessionID sessionId,
|
||||||
bool forceRelay) {
|
bool forceRelay) {
|
||||||
bind.sessionReconnect(
|
bind.sessionReconnect(sessionId: sessionId, forceRelay: forceRelay);
|
||||||
sessionId: sessionId, forceRelay: forceRelay, userSessionId: "");
|
|
||||||
clearPermissions();
|
clearPermissions();
|
||||||
dialogManager.dismissAll();
|
dialogManager.dismissAll();
|
||||||
dialogManager.showLoading(translate('Connecting...'),
|
dialogManager.showLoading(translate('Connecting...'),
|
||||||
|
@ -122,11 +122,12 @@ message PeerInfo {
|
|||||||
// Use JSON's key-value format which is friendly for peer to handle.
|
// Use JSON's key-value format which is friendly for peer to handle.
|
||||||
// NOTE: Only support one-level dictionaries (for peer to update), and the key is of type string.
|
// NOTE: Only support one-level dictionaries (for peer to update), and the key is of type string.
|
||||||
string platform_additions = 12;
|
string platform_additions = 12;
|
||||||
|
WindowsSessions windows_sessions = 13;
|
||||||
}
|
}
|
||||||
|
|
||||||
message RdpUserSession {
|
message WindowsSession {
|
||||||
string user_session_id = 1;
|
uint32 sid = 1;
|
||||||
string user_name = 2;
|
string name = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message LoginResponse {
|
message LoginResponse {
|
||||||
@ -594,7 +595,7 @@ message OptionMessage {
|
|||||||
BoolOption disable_keyboard = 12;
|
BoolOption disable_keyboard = 12;
|
||||||
// Position 13 is used for Resolution. Remove later.
|
// Position 13 is used for Resolution. Remove later.
|
||||||
// Resolution custom_resolution = 13;
|
// Resolution custom_resolution = 13;
|
||||||
string user_session = 14;
|
BoolOption support_windows_specific_session = 14;
|
||||||
}
|
}
|
||||||
|
|
||||||
message TestDelay {
|
message TestDelay {
|
||||||
@ -709,8 +710,9 @@ message PluginFailure {
|
|||||||
string msg = 3;
|
string msg = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message RdpUserSessions {
|
message WindowsSessions {
|
||||||
repeated RdpUserSession rdp_user_sessions = 1;
|
repeated WindowsSession sessions = 1;
|
||||||
|
uint32 current_sid = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Misc {
|
message Misc {
|
||||||
@ -744,7 +746,7 @@ message Misc {
|
|||||||
ToggleVirtualDisplay toggle_virtual_display = 32;
|
ToggleVirtualDisplay toggle_virtual_display = 32;
|
||||||
TogglePrivacyMode toggle_privacy_mode = 33;
|
TogglePrivacyMode toggle_privacy_mode = 33;
|
||||||
SupportedEncoding supported_encoding = 34;
|
SupportedEncoding supported_encoding = 34;
|
||||||
RdpUserSessions rdp_user_sessions = 35;
|
uint32 selected_sid = 35;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1149,7 +1149,7 @@ pub struct LoginConfigHandler {
|
|||||||
pub custom_fps: Arc<Mutex<Option<usize>>>,
|
pub custom_fps: Arc<Mutex<Option<usize>>>,
|
||||||
pub adapter_luid: Option<i64>,
|
pub adapter_luid: Option<i64>,
|
||||||
pub mark_unsupported: Vec<CodecFormat>,
|
pub mark_unsupported: Vec<CodecFormat>,
|
||||||
pub selected_user_session_id: String,
|
pub selected_windows_session_id: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for LoginConfigHandler {
|
impl Deref for LoginConfigHandler {
|
||||||
@ -1236,7 +1236,7 @@ impl LoginConfigHandler {
|
|||||||
self.received = false;
|
self.received = false;
|
||||||
self.switch_uuid = switch_uuid;
|
self.switch_uuid = switch_uuid;
|
||||||
self.adapter_luid = adapter_luid;
|
self.adapter_luid = adapter_luid;
|
||||||
self.selected_user_session_id = "".to_owned();
|
self.selected_windows_session_id = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the client should auto login.
|
/// Check if the client should auto login.
|
||||||
@ -1518,7 +1518,7 @@ impl LoginConfigHandler {
|
|||||||
}
|
}
|
||||||
let mut n = 0;
|
let mut n = 0;
|
||||||
let mut msg = OptionMessage::new();
|
let mut msg = OptionMessage::new();
|
||||||
msg.user_session = self.selected_user_session_id.clone();
|
msg.support_windows_specific_session = BoolOption::Yes.into();
|
||||||
n += 1;
|
n += 1;
|
||||||
|
|
||||||
if self.conn_type.eq(&ConnType::FILE_TRANSFER) {
|
if self.conn_type.eq(&ConnType::FILE_TRANSFER) {
|
||||||
@ -2595,7 +2595,7 @@ pub async fn handle_hash(
|
|||||||
peer: &mut Stream,
|
peer: &mut Stream,
|
||||||
) {
|
) {
|
||||||
lc.write().unwrap().hash = hash.clone();
|
lc.write().unwrap().hash = hash.clone();
|
||||||
let uuid = lc.read().unwrap().switch_uuid.clone();
|
let uuid = lc.write().unwrap().switch_uuid.take();
|
||||||
if let Some(uuid) = uuid {
|
if let Some(uuid) = uuid {
|
||||||
if let Ok(uuid) = uuid::Uuid::from_str(&uuid) {
|
if let Ok(uuid) = uuid::Uuid::from_str(&uuid) {
|
||||||
send_switch_login_request(lc.clone(), peer, uuid).await;
|
send_switch_login_request(lc.clone(), peer, uuid).await;
|
||||||
@ -2744,7 +2744,7 @@ pub trait Interface: Send + Clone + 'static + Sized {
|
|||||||
fn msgbox(&self, msgtype: &str, title: &str, text: &str, link: &str);
|
fn msgbox(&self, msgtype: &str, title: &str, text: &str, link: &str);
|
||||||
fn handle_login_error(&self, err: &str) -> bool;
|
fn handle_login_error(&self, err: &str) -> bool;
|
||||||
fn handle_peer_info(&self, pi: PeerInfo);
|
fn handle_peer_info(&self, pi: PeerInfo);
|
||||||
fn set_multiple_user_sessions(&self, sessions: Vec<hbb_common::message_proto::RdpUserSession>);
|
fn set_multiple_windows_session(&self, sessions: Vec<WindowsSession>);
|
||||||
fn on_error(&self, err: &str) {
|
fn on_error(&self, err: &str) {
|
||||||
self.msgbox("error", "Error", err, "");
|
self.msgbox("error", "Error", err, "");
|
||||||
}
|
}
|
||||||
|
@ -1314,13 +1314,6 @@ impl<T: InvokeUiSession> Remote<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(message::Union::Misc(misc)) => match misc.union {
|
Some(message::Union::Misc(misc)) => match misc.union {
|
||||||
Some(misc::Union::RdpUserSessions(sessions)) => {
|
|
||||||
if !sessions.rdp_user_sessions.is_empty() {
|
|
||||||
self.handler
|
|
||||||
.set_multiple_user_session(sessions.rdp_user_sessions);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(misc::Union::AudioFormat(f)) => {
|
Some(misc::Union::AudioFormat(f)) => {
|
||||||
self.audio_sender.send(MediaData::AudioFormat(f)).ok();
|
self.audio_sender.send(MediaData::AudioFormat(f)).ok();
|
||||||
}
|
}
|
||||||
|
@ -826,15 +826,21 @@ impl InvokeUiSession for FlutterHandler {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_multiple_user_session(&self, sessions: Vec<hbb_common::message_proto::RdpUserSession>) {
|
fn set_multiple_windows_session(&self, sessions: Vec<WindowsSession>) {
|
||||||
let formatted_sessions: Vec<String> = sessions
|
let mut msg_vec = Vec::new();
|
||||||
.iter()
|
let mut sessions = sessions;
|
||||||
.map(|session| format!("{}-{}", session.user_session_id, session.user_name))
|
for d in sessions.drain(..) {
|
||||||
.collect();
|
let mut h: HashMap<&str, String> = Default::default();
|
||||||
let sessions = formatted_sessions.join(",");
|
h.insert("sid", d.sid.to_string());
|
||||||
|
h.insert("name", d.name);
|
||||||
|
msg_vec.push(h);
|
||||||
|
}
|
||||||
self.push_event(
|
self.push_event(
|
||||||
"set_multiple_user_session",
|
"set_multiple_windows_session",
|
||||||
vec![("user_sessions", &sessions)],
|
vec![(
|
||||||
|
"user_sessions",
|
||||||
|
&serde_json::ser::to_string(&msg_vec).unwrap_or("".to_owned()),
|
||||||
|
)],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,9 +217,9 @@ pub fn session_record_status(session_id: SessionID, status: bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn session_reconnect(session_id: SessionID, force_relay: bool, user_session_id: String) {
|
pub fn session_reconnect(session_id: SessionID, force_relay: bool) {
|
||||||
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||||
session.reconnect(force_relay, user_session_id);
|
session.reconnect(force_relay);
|
||||||
}
|
}
|
||||||
session_on_waiting_for_image_dialog_show(session_id);
|
session_on_waiting_for_image_dialog_show(session_id);
|
||||||
}
|
}
|
||||||
@ -701,6 +701,12 @@ pub fn session_set_size(_session_id: SessionID, _display: usize, _width: usize,
|
|||||||
super::flutter::session_set_size(_session_id, _display, _width, _height)
|
super::flutter::session_set_size(_session_id, _display, _width, _height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn session_send_selected_session_id(session_id: SessionID, sid: String) {
|
||||||
|
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||||
|
session.send_selected_session_id(sid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main_get_sound_inputs() -> Vec<String> {
|
pub fn main_get_sound_inputs() -> Vec<String> {
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
return get_sound_inputs();
|
return get_sound_inputs();
|
||||||
|
@ -434,17 +434,6 @@ extern "C"
|
|||||||
return nout;
|
return nout;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t get_current_process_session_id()
|
|
||||||
{
|
|
||||||
DWORD sessionId = 0;
|
|
||||||
HANDLE hProcess = GetCurrentProcess();
|
|
||||||
if (hProcess) {
|
|
||||||
ProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
|
|
||||||
CloseHandle(hProcess);
|
|
||||||
}
|
|
||||||
return sessionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t get_session_user_info(PWSTR bufin, uint32_t nin, BOOL rdp, uint32_t id)
|
uint32_t get_session_user_info(PWSTR bufin, uint32_t nin, BOOL rdp, uint32_t id)
|
||||||
{
|
{
|
||||||
uint32_t nout = 0;
|
uint32_t nout = 0;
|
||||||
|
@ -12,9 +12,10 @@ use hbb_common::{
|
|||||||
bail,
|
bail,
|
||||||
config::{self, Config},
|
config::{self, Config},
|
||||||
log,
|
log,
|
||||||
message_proto::Resolution,
|
message_proto::{Resolution, WindowsSession},
|
||||||
sleep, timeout, tokio,
|
sleep, timeout, tokio,
|
||||||
};
|
};
|
||||||
|
use sha2::digest::generic_array::functional::FunctionalSequence;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
@ -38,7 +39,7 @@ use winapi::{
|
|||||||
minwinbase::STILL_ACTIVE,
|
minwinbase::STILL_ACTIVE,
|
||||||
processthreadsapi::{
|
processthreadsapi::{
|
||||||
GetCurrentProcess, GetCurrentProcessId, GetExitCodeProcess, OpenProcess,
|
GetCurrentProcess, GetCurrentProcessId, GetExitCodeProcess, OpenProcess,
|
||||||
OpenProcessToken, PROCESS_INFORMATION, STARTUPINFOW,
|
OpenProcessToken, ProcessIdToSessionId, PROCESS_INFORMATION, STARTUPINFOW,
|
||||||
},
|
},
|
||||||
securitybaseapi::GetTokenInformation,
|
securitybaseapi::GetTokenInformation,
|
||||||
shellapi::ShellExecuteW,
|
shellapi::ShellExecuteW,
|
||||||
@ -511,8 +512,11 @@ async fn run_service(_arguments: Vec<OsString>) -> ResultType<()> {
|
|||||||
let mut incoming = ipc::new_listener(crate::POSTFIX_SERVICE).await?;
|
let mut incoming = ipc::new_listener(crate::POSTFIX_SERVICE).await?;
|
||||||
let mut stored_usid = None;
|
let mut stored_usid = None;
|
||||||
loop {
|
loop {
|
||||||
let sids = get_all_active_session_ids();
|
let sids: Vec<_> = get_available_sessions(false)
|
||||||
if !sids.contains(&format!("{}", session_id)) || !is_share_rdp() {
|
.iter()
|
||||||
|
.map(|e| e.sid)
|
||||||
|
.collect();
|
||||||
|
if !sids.contains(&session_id) || !is_share_rdp() {
|
||||||
let current_active_session = unsafe { get_current_session(share_rdp()) };
|
let current_active_session = unsafe { get_current_session(share_rdp()) };
|
||||||
if session_id != current_active_session {
|
if session_id != current_active_session {
|
||||||
session_id = current_active_session;
|
session_id = current_active_session;
|
||||||
@ -628,16 +632,15 @@ async fn launch_server(session_id: DWORD, close_first: bool) -> ResultType<HANDL
|
|||||||
Ok(h)
|
Ok(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_as_user(arg: Vec<&str>, usid: Option<u32>) -> ResultType<Option<std::process::Child>> {
|
pub fn run_as_user(arg: Vec<&str>) -> ResultType<Option<std::process::Child>> {
|
||||||
let cmd = format!(
|
let cmd = format!(
|
||||||
"\"{}\" {}",
|
"\"{}\" {}",
|
||||||
std::env::current_exe()?.to_str().unwrap_or(""),
|
std::env::current_exe()?.to_str().unwrap_or(""),
|
||||||
arg.join(" "),
|
arg.join(" "),
|
||||||
);
|
);
|
||||||
let mut session_id = get_current_process_session_id();
|
let Some(session_id) = get_current_process_session_id() else {
|
||||||
if let Some(usid) = usid {
|
bail!("Failed to get current process session id");
|
||||||
session_id = usid;
|
};
|
||||||
}
|
|
||||||
use std::os::windows::ffi::OsStrExt;
|
use std::os::windows::ffi::OsStrExt;
|
||||||
let wstr: Vec<u16> = std::ffi::OsStr::new(&cmd)
|
let wstr: Vec<u16> = std::ffi::OsStr::new(&cmd)
|
||||||
.encode_wide()
|
.encode_wide()
|
||||||
@ -733,11 +736,13 @@ pub fn set_share_rdp(enable: bool) {
|
|||||||
run_cmds(cmd, false, "share_rdp").ok();
|
run_cmds(cmd, false, "share_rdp").ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_current_process_session_id() -> u32 {
|
pub fn get_current_process_session_id() -> Option<u32> {
|
||||||
extern "C" {
|
let mut sid = 0;
|
||||||
fn get_current_process_session_id() -> u32;
|
if unsafe { ProcessIdToSessionId(GetCurrentProcessId(), &mut sid) == TRUE } {
|
||||||
|
Some(sid)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
unsafe { get_current_process_session_id() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_active_username() -> String {
|
pub fn get_active_username() -> String {
|
||||||
@ -762,74 +767,91 @@ pub fn get_active_username() -> String {
|
|||||||
.to_owned()
|
.to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_all_active_sessions() -> Vec<Vec<String>> {
|
fn get_session_username(session_id: u32) -> String {
|
||||||
let sids = get_all_active_session_ids_with_station();
|
|
||||||
let mut out = Vec::new();
|
|
||||||
for sid in sids.split(',') {
|
|
||||||
let username = get_session_username(sid.to_owned());
|
|
||||||
if !username.is_empty() {
|
|
||||||
let sid_split = sid.split(':').collect::<Vec<_>>()[1];
|
|
||||||
let v = vec![sid_split.to_owned(), username];
|
|
||||||
out.push(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_session_username(session_id_with_station_name: String) -> String {
|
|
||||||
let mut session_id = session_id_with_station_name.split(':');
|
|
||||||
let station = session_id.next().unwrap_or("");
|
|
||||||
let session_id = session_id.next().unwrap_or("");
|
|
||||||
if session_id == "" {
|
|
||||||
return "".to_owned();
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn get_session_user_info(path: *mut u16, n: u32, rdp: bool, session_id: u32) -> u32;
|
fn get_session_user_info(path: *mut u16, n: u32, rdp: bool, session_id: u32) -> u32;
|
||||||
}
|
}
|
||||||
let buff_size = 256;
|
let buff_size = 256;
|
||||||
let mut buff: Vec<u16> = Vec::with_capacity(buff_size);
|
let mut buff: Vec<u16> = Vec::with_capacity(buff_size);
|
||||||
buff.resize(buff_size, 0);
|
buff.resize(buff_size, 0);
|
||||||
let n = unsafe {
|
let n = unsafe { get_session_user_info(buff.as_mut_ptr(), buff_size as _, true, session_id) };
|
||||||
get_session_user_info(
|
|
||||||
buff.as_mut_ptr(),
|
|
||||||
buff_size as _,
|
|
||||||
true,
|
|
||||||
session_id.parse::<u32>().unwrap(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return "".to_owned();
|
return "".to_owned();
|
||||||
}
|
}
|
||||||
let sl = unsafe { std::slice::from_raw_parts(buff.as_ptr(), n as _) };
|
let sl = unsafe { std::slice::from_raw_parts(buff.as_ptr(), n as _) };
|
||||||
let out = String::from_utf16(sl)
|
String::from_utf16(sl)
|
||||||
.unwrap_or("".to_owned())
|
.unwrap_or("".to_owned())
|
||||||
.trim_end_matches('\0')
|
.trim_end_matches('\0')
|
||||||
.to_owned();
|
.to_owned()
|
||||||
station.to_owned() + ": " + &out
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_all_active_session_ids_with_station() -> String {
|
pub fn get_available_sessions(name: bool) -> Vec<WindowsSession> {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn get_available_session_ids(buf: *mut wchar_t, buf_size: c_int, include_rdp: bool);
|
fn get_available_session_ids(buf: *mut wchar_t, buf_size: c_int, include_rdp: bool);
|
||||||
}
|
}
|
||||||
const BUF_SIZE: c_int = 1024;
|
const BUF_SIZE: c_int = 1024;
|
||||||
let mut buf: Vec<wchar_t> = vec![0; BUF_SIZE as usize];
|
let mut buf: Vec<wchar_t> = vec![0; BUF_SIZE as usize];
|
||||||
|
|
||||||
unsafe {
|
let station_session_id_array = unsafe {
|
||||||
get_available_session_ids(buf.as_mut_ptr(), BUF_SIZE, true);
|
get_available_session_ids(buf.as_mut_ptr(), BUF_SIZE, true);
|
||||||
let session_ids = String::from_utf16_lossy(&buf);
|
let session_ids = String::from_utf16_lossy(&buf);
|
||||||
session_ids.trim_matches(char::from(0)).trim().to_string()
|
session_ids.trim_matches(char::from(0)).trim().to_string()
|
||||||
|
};
|
||||||
|
let mut v: Vec<WindowsSession> = vec![];
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-wtsgetactiveconsolesessionid
|
||||||
|
let physical_console_session_id = unsafe { get_current_session(FALSE) };
|
||||||
|
let physical_console_username = get_session_username(physical_console_session_id);
|
||||||
|
let physical_console_name = if name {
|
||||||
|
if physical_console_username.is_empty() {
|
||||||
|
"Console".to_owned()
|
||||||
|
} else {
|
||||||
|
format!("Console:{physical_console_username}")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
"".to_owned()
|
||||||
|
};
|
||||||
|
v.push(WindowsSession {
|
||||||
|
sid: physical_console_session_id,
|
||||||
|
name: physical_console_name,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
// https://learn.microsoft.com/en-us/previous-versions//cc722458(v=technet.10)?redirectedfrom=MSDN
|
||||||
|
for type_session_id in station_session_id_array.split(",") {
|
||||||
|
let split: Vec<_> = type_session_id.split(":").collect();
|
||||||
|
if split.len() == 2 {
|
||||||
|
if let Ok(sid) = split[1].parse::<u32>() {
|
||||||
|
if !v.iter().any(|e| (*e).sid == sid) {
|
||||||
|
let name = if name {
|
||||||
|
format!("{}:{}", split[0], get_session_username(sid))
|
||||||
|
} else {
|
||||||
|
"".to_owned()
|
||||||
|
};
|
||||||
|
v.push(WindowsSession {
|
||||||
|
sid,
|
||||||
|
name,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if name {
|
||||||
|
let mut name_count: HashMap<String, usize> = HashMap::new();
|
||||||
pub fn get_all_active_session_ids() -> String {
|
for session in &v {
|
||||||
let out = get_all_active_session_ids_with_station()
|
*name_count.entry(session.name.clone()).or_insert(0) += 1;
|
||||||
.split(',')
|
}
|
||||||
.map(|x| x.split(':').nth(1).unwrap_or(""))
|
let current_sid = get_current_process_session_id().unwrap_or_default();
|
||||||
.collect::<Vec<_>>()
|
for e in v.iter_mut() {
|
||||||
.join(",");
|
let running = e.sid == current_sid && current_sid != 0;
|
||||||
out.trim_matches(char::from(0)).trim().to_string()
|
if name_count.get(&e.name).map(|v| *v).unwrap_or_default() > 1 {
|
||||||
|
e.name = format!("{} (sid = {})", e.name, e.sid);
|
||||||
|
}
|
||||||
|
if running {
|
||||||
|
e.name = format!("{} (running)", e.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_active_user_home() -> Option<PathBuf> {
|
pub fn get_active_user_home() -> Option<PathBuf> {
|
||||||
@ -845,7 +867,11 @@ pub fn get_active_user_home() -> Option<PathBuf> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_prelogin() -> bool {
|
pub fn is_prelogin() -> bool {
|
||||||
let username = get_active_username();
|
let Some(sid) = get_current_process_session_id() else {
|
||||||
|
log::error!("get_current_process_session_id failed");
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let username = get_session_username(sid);
|
||||||
username.is_empty() || username == "SYSTEM"
|
username.is_empty() || username == "SYSTEM"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,8 +237,8 @@ pub struct Connection {
|
|||||||
file_remove_log_control: FileRemoveLogControl,
|
file_remove_log_control: FileRemoveLogControl,
|
||||||
#[cfg(feature = "gpucodec")]
|
#[cfg(feature = "gpucodec")]
|
||||||
supported_encoding_flag: (bool, Option<bool>),
|
supported_encoding_flag: (bool, Option<bool>),
|
||||||
user_session_id: Option<u32>,
|
need_sub_remote_service: bool,
|
||||||
checked_multiple_session: bool,
|
remote_service_subed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConnInner {
|
impl ConnInner {
|
||||||
@ -386,8 +386,8 @@ impl Connection {
|
|||||||
file_remove_log_control: FileRemoveLogControl::new(id),
|
file_remove_log_control: FileRemoveLogControl::new(id),
|
||||||
#[cfg(feature = "gpucodec")]
|
#[cfg(feature = "gpucodec")]
|
||||||
supported_encoding_flag: (false, None),
|
supported_encoding_flag: (false, None),
|
||||||
user_session_id: None,
|
need_sub_remote_service: false,
|
||||||
checked_multiple_session: false,
|
remote_service_subed: false,
|
||||||
};
|
};
|
||||||
let addr = hbb_common::try_into_v4(addr);
|
let addr = hbb_common::try_into_v4(addr);
|
||||||
if !conn.on_open(addr).await {
|
if !conn.on_open(addr).await {
|
||||||
@ -1194,6 +1194,9 @@ impl Connection {
|
|||||||
.into();
|
.into();
|
||||||
|
|
||||||
let mut sub_service = false;
|
let mut sub_service = false;
|
||||||
|
let mut delay_sub_service = false;
|
||||||
|
#[cfg(windows)]
|
||||||
|
self.handle_windows_specific_session(&mut pi, &mut delay_sub_service);
|
||||||
if self.file_transfer.is_some() {
|
if self.file_transfer.is_some() {
|
||||||
res.set_peer_info(pi);
|
res.set_peer_info(pi);
|
||||||
} else {
|
} else {
|
||||||
@ -1255,6 +1258,16 @@ impl Connection {
|
|||||||
};
|
};
|
||||||
self.read_dir(dir, show_hidden);
|
self.read_dir(dir, show_hidden);
|
||||||
} else if sub_service {
|
} else if sub_service {
|
||||||
|
self.need_sub_remote_service = true;
|
||||||
|
if !delay_sub_service {
|
||||||
|
self.check_sub_remote_services();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_sub_remote_services(&mut self) {
|
||||||
|
if self.need_sub_remote_service && !self.remote_service_subed {
|
||||||
|
self.remote_service_subed = true;
|
||||||
if let Some(s) = self.server.upgrade() {
|
if let Some(s) = self.server.upgrade() {
|
||||||
let mut noperms = Vec::new();
|
let mut noperms = Vec::new();
|
||||||
if !self.peer_keyboard_enabled() && !self.show_remote_cursor {
|
if !self.peer_keyboard_enabled() && !self.show_remote_cursor {
|
||||||
@ -1279,6 +1292,27 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn handle_windows_specific_session(&mut self, pi: &mut PeerInfo, delay_sub_service: &mut bool) {
|
||||||
|
let sessions = crate::platform::get_available_sessions(true);
|
||||||
|
let current_sid = crate::platform::get_current_process_session_id().unwrap_or_default();
|
||||||
|
if crate::platform::is_installed()
|
||||||
|
&& crate::platform::is_share_rdp()
|
||||||
|
&& raii::AuthedConnID::remote_and_file_conn_count() == 1
|
||||||
|
&& sessions.len() > 1
|
||||||
|
&& current_sid != 0
|
||||||
|
&& self.lr.option.support_windows_specific_session == BoolOption::Yes.into()
|
||||||
|
{
|
||||||
|
pi.windows_sessions = Some(WindowsSessions {
|
||||||
|
sessions,
|
||||||
|
current_sid,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.into();
|
||||||
|
*delay_sub_service = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn on_remote_authorized(&self) {
|
fn on_remote_authorized(&self) {
|
||||||
self.update_codec_on_login();
|
self.update_codec_on_login();
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||||
@ -1495,50 +1529,8 @@ impl Connection {
|
|||||||
self.video_ack_required = lr.video_ack_required;
|
self.video_ack_required = lr.video_ack_required;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
async fn handle_multiple_user_sessions(&mut self, usid: Option<u32>) -> bool {
|
|
||||||
if self.port_forward_socket.is_some() {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
let active_sessions = crate::platform::get_all_active_sessions();
|
|
||||||
if active_sessions.len() <= 1 {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
let current_process_usid = crate::platform::get_current_process_session_id();
|
|
||||||
if usid.is_none() {
|
|
||||||
let mut res = Misc::new();
|
|
||||||
let mut rdp = Vec::new();
|
|
||||||
for session in active_sessions {
|
|
||||||
let u_sid = &session[0];
|
|
||||||
let u_name = &session[1];
|
|
||||||
let mut rdp_session = RdpUserSession::new();
|
|
||||||
rdp_session.user_session_id = u_sid.clone();
|
|
||||||
rdp_session.user_name = u_name.clone();
|
|
||||||
rdp.push(rdp_session);
|
|
||||||
}
|
|
||||||
res.set_rdp_user_sessions(RdpUserSessions {
|
|
||||||
rdp_user_sessions: rdp,
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
let mut msg_out = Message::new();
|
|
||||||
msg_out.set_misc(res);
|
|
||||||
self.send(msg_out).await;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if usid != Some(current_process_usid) {
|
|
||||||
self.on_close("Reconnecting...", false).await;
|
|
||||||
std::thread::spawn(move || {
|
|
||||||
let _ = ipc::connect_to_user_session(usid);
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
fn try_start_cm_ipc(&mut self) {
|
fn try_start_cm_ipc(&mut self) {
|
||||||
let usid = self.user_session_id;
|
|
||||||
if let Some(p) = self.start_cm_ipc_para.take() {
|
if let Some(p) = self.start_cm_ipc_para.take() {
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
@ -1548,7 +1540,6 @@ impl Connection {
|
|||||||
p.tx_from_cm,
|
p.tx_from_cm,
|
||||||
p.rx_desktop_ready,
|
p.rx_desktop_ready,
|
||||||
p.tx_cm_stream_ready,
|
p.tx_cm_stream_ready,
|
||||||
usid.clone(),
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
@ -1562,7 +1553,7 @@ impl Connection {
|
|||||||
#[cfg(all(windows, feature = "flutter"))]
|
#[cfg(all(windows, feature = "flutter"))]
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
if crate::is_server() && !crate::check_process("--tray", false) {
|
if crate::is_server() && !crate::check_process("--tray", false) {
|
||||||
crate::platform::run_as_user(vec!["--tray"], usid).ok();
|
crate::platform::run_as_user(vec!["--tray"]).ok();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1570,19 +1561,6 @@ impl Connection {
|
|||||||
|
|
||||||
async fn on_message(&mut self, msg: Message) -> bool {
|
async fn on_message(&mut self, msg: Message) -> bool {
|
||||||
if let Some(message::Union::LoginRequest(lr)) = msg.union {
|
if let Some(message::Union::LoginRequest(lr)) = msg.union {
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
{
|
|
||||||
if !self.checked_multiple_session {
|
|
||||||
let usid;
|
|
||||||
match lr.option.user_session.parse::<u32>() {
|
|
||||||
Ok(n) => usid = Some(n),
|
|
||||||
Err(..) => usid = None,
|
|
||||||
}
|
|
||||||
if usid.is_some() {
|
|
||||||
self.user_session_id = usid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.handle_login_request_without_validation(&lr).await;
|
self.handle_login_request_without_validation(&lr).await;
|
||||||
if self.authorized {
|
if self.authorized {
|
||||||
return true;
|
return true;
|
||||||
@ -1821,22 +1799,6 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if self.authorized {
|
} else if self.authorized {
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
if !self.checked_multiple_session {
|
|
||||||
self.checked_multiple_session = true;
|
|
||||||
if crate::platform::is_installed()
|
|
||||||
&& crate::platform::is_share_rdp()
|
|
||||||
&& Self::alive_conns().len() == 1
|
|
||||||
&& get_version_number(&self.lr.version) >= get_version_number("1.2.4")
|
|
||||||
{
|
|
||||||
if !self
|
|
||||||
.handle_multiple_user_sessions(self.user_session_id)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match msg.union {
|
match msg.union {
|
||||||
Some(message::Union::MouseEvent(me)) => {
|
Some(message::Union::MouseEvent(me)) => {
|
||||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
@ -2304,6 +2266,26 @@ impl Connection {
|
|||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.user_record(self.inner.id(), status),
|
.user_record(self.inner.id(), status),
|
||||||
|
#[cfg(windows)]
|
||||||
|
Some(misc::Union::SelectedSid(sid)) => {
|
||||||
|
let current_process_usid =
|
||||||
|
crate::platform::get_current_process_session_id().unwrap_or_default();
|
||||||
|
let sessions = crate::platform::get_available_sessions(false);
|
||||||
|
if crate::platform::is_installed()
|
||||||
|
&& crate::platform::is_share_rdp()
|
||||||
|
&& raii::AuthedConnID::remote_and_file_conn_count() == 1
|
||||||
|
&& sessions.len() > 1
|
||||||
|
&& current_process_usid != 0
|
||||||
|
&& current_process_usid != sid
|
||||||
|
&& sessions.iter().any(|e| e.sid == sid)
|
||||||
|
{
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
let _ = ipc::connect_to_user_session(Some(sid));
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self.check_sub_remote_services();
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
Some(message::Union::AudioFrame(frame)) => {
|
Some(message::Union::AudioFrame(frame)) => {
|
||||||
@ -3087,7 +3069,6 @@ async fn start_ipc(
|
|||||||
tx_from_cm: mpsc::UnboundedSender<ipc::Data>,
|
tx_from_cm: mpsc::UnboundedSender<ipc::Data>,
|
||||||
mut _rx_desktop_ready: mpsc::Receiver<()>,
|
mut _rx_desktop_ready: mpsc::Receiver<()>,
|
||||||
tx_stream_ready: mpsc::Sender<()>,
|
tx_stream_ready: mpsc::Sender<()>,
|
||||||
user_session_id: Option<u32>,
|
|
||||||
) -> ResultType<()> {
|
) -> ResultType<()> {
|
||||||
use hbb_common::anyhow::anyhow;
|
use hbb_common::anyhow::anyhow;
|
||||||
|
|
||||||
@ -3135,7 +3116,7 @@ async fn start_ipc(
|
|||||||
if crate::platform::is_root() {
|
if crate::platform::is_root() {
|
||||||
let mut res = Ok(None);
|
let mut res = Ok(None);
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
#[cfg(not(any(target_os = "linux", target_os = "windows")))]
|
#[cfg(not(any(target_os = "linux")))]
|
||||||
{
|
{
|
||||||
log::debug!("Start cm");
|
log::debug!("Start cm");
|
||||||
res = crate::platform::run_as_user(args.clone());
|
res = crate::platform::run_as_user(args.clone());
|
||||||
@ -3149,14 +3130,10 @@ async fn start_ipc(
|
|||||||
None::<(&str, &str)>,
|
None::<(&str, &str)>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
{
|
|
||||||
log::debug!("Start cm");
|
|
||||||
res = crate::platform::run_as_user(args.clone(), user_session_id);
|
|
||||||
}
|
|
||||||
if res.is_ok() {
|
if res.is_ok() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
log::error!("Failed to run cm: {res:?}");
|
||||||
sleep(1.).await;
|
sleep(1.).await;
|
||||||
}
|
}
|
||||||
if let Some(task) = res? {
|
if let Some(task) = res? {
|
||||||
@ -3540,6 +3517,15 @@ mod raii {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.send((conn_count, remote_count)));
|
.send((conn_count, remote_count)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn remote_and_file_conn_count() -> usize {
|
||||||
|
AUTHED_CONNS
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.filter(|c| c.1 == AuthConnType::Remote || c.1 == AuthConnType::FileTransfer)
|
||||||
|
.count()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for AuthedConnID {
|
impl Drop for AuthedConnID {
|
||||||
|
@ -50,7 +50,7 @@ use scrap::hwcodec::{HwEncoder, HwEncoderConfig};
|
|||||||
use scrap::Capturer;
|
use scrap::Capturer;
|
||||||
use scrap::{
|
use scrap::{
|
||||||
aom::AomEncoderConfig,
|
aom::AomEncoderConfig,
|
||||||
codec::{Encoder, EncoderCfg, EncodingUpdate, Quality},
|
codec::{Encoder, EncoderCfg, Quality},
|
||||||
record::{Recorder, RecorderContext},
|
record::{Recorder, RecorderContext},
|
||||||
vpxcodec::{VpxEncoderConfig, VpxVideoCodecId},
|
vpxcodec::{VpxEncoderConfig, VpxVideoCodecId},
|
||||||
CodecName, Display, Frame, TraitCapturer,
|
CodecName, Display, Frame, TraitCapturer,
|
||||||
@ -643,7 +643,7 @@ fn get_encoder_config(
|
|||||||
GpuEncoder::set_not_use(_display_idx, true);
|
GpuEncoder::set_not_use(_display_idx, true);
|
||||||
}
|
}
|
||||||
#[cfg(feature = "gpucodec")]
|
#[cfg(feature = "gpucodec")]
|
||||||
Encoder::update(EncodingUpdate::Check);
|
Encoder::update(scrap::codec::EncodingUpdate::Check);
|
||||||
// https://www.wowza.com/community/t/the-correct-keyframe-interval-in-obs-studio/95162
|
// https://www.wowza.com/community/t/the-correct-keyframe-interval-in-obs-studio/95162
|
||||||
let keyframe_interval = if record { Some(240) } else { None };
|
let keyframe_interval = if record { Some(240) } else { None };
|
||||||
let negotiated_codec = Encoder::negotiated_codec();
|
let negotiated_codec = Encoder::negotiated_codec();
|
||||||
|
@ -304,21 +304,7 @@ function msgbox(type, title, content, link="", callback=null, height=180, width=
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else if (type === "multiple-sessions") {
|
}
|
||||||
var parts = content.split("-");
|
|
||||||
var ids = parts[0].split(",");
|
|
||||||
var names = parts[1].split(",");
|
|
||||||
var sessionData = [];
|
|
||||||
for (var i = 0; i < ids.length; i++) {
|
|
||||||
sessionData.push({ id: ids[i], name: names[i] });
|
|
||||||
}
|
|
||||||
content = <MultipleSessionComponent sessions={sessionData} />;
|
|
||||||
callback = function () {
|
|
||||||
retryConnect();
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
height += 50;
|
|
||||||
}
|
|
||||||
last_msgbox_tag = type + "-" + title + "-" + content + "-" + link;
|
last_msgbox_tag = type + "-" + title + "-" + content + "-" + link;
|
||||||
$(#msgbox).content(<MsgboxComponent width={width} height={height} autoLogin={autoLogin} type={type} title={title} content={content} link={link} remember={remember} callback={callback} contentStyle={contentStyle} hasRetry={hasRetry} />);
|
$(#msgbox).content(<MsgboxComponent width={width} height={height} autoLogin={autoLogin} type={type} title={title} content={content} link={link} remember={remember} callback={callback} contentStyle={contentStyle} hasRetry={hasRetry} />);
|
||||||
}
|
}
|
||||||
@ -353,7 +339,7 @@ handler.msgbox_retry = function(type, title, text, link, hasRetry) {
|
|||||||
function retryConnect(cancelTimer=false) {
|
function retryConnect(cancelTimer=false) {
|
||||||
if (cancelTimer) self.timer(0, retryConnect);
|
if (cancelTimer) self.timer(0, retryConnect);
|
||||||
if (!is_port_forward) connecting();
|
if (!is_port_forward) connecting();
|
||||||
handler.reconnect(false, "");
|
handler.reconnect(false);
|
||||||
}
|
}
|
||||||
/******************** end of msgbox ****************************************/
|
/******************** end of msgbox ****************************************/
|
||||||
|
|
||||||
@ -474,19 +460,12 @@ function awake() {
|
|||||||
|
|
||||||
class MultipleSessionComponent extends Reactor.Component {
|
class MultipleSessionComponent extends Reactor.Component {
|
||||||
this var sessions = [];
|
this var sessions = [];
|
||||||
this var selectedSessionId = null;
|
|
||||||
this var sessionlength = 0;
|
|
||||||
this var messageText = translate("Please select the user you want to connect to");
|
this var messageText = translate("Please select the user you want to connect to");
|
||||||
|
|
||||||
function this(params) {
|
function this(params) {
|
||||||
if (params && params.sessions) {
|
if (params && params.sessions) {
|
||||||
this.sessions = params.sessions;
|
this.sessions = params.sessions;
|
||||||
this.selectedSessionId = params.sessions[0].id;
|
|
||||||
this.sessions.map(session => {
|
|
||||||
this.sessionlength += session.name.length;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
handler.set_selected_user_session_id(this.selectedSessionId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
@ -494,15 +473,9 @@ class MultipleSessionComponent extends Reactor.Component {
|
|||||||
<div .title>{this.messageText}</div>
|
<div .title>{this.messageText}</div>
|
||||||
<select>
|
<select>
|
||||||
{this.sessions.map(session =>
|
{this.sessions.map(session =>
|
||||||
<option value={session.id}>{session.name}</option>
|
<option value={session.sid}>{session.name}</option>
|
||||||
)}
|
)}
|
||||||
</select>
|
</select>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
event change {
|
|
||||||
var selectedSessionName = this.value.substr(this.messageText.length + this.sessionlength);
|
|
||||||
this.selectedSessionId = this.sessions.find(session => session.name == selectedSessionName).id;
|
|
||||||
handler.set_selected_user_session_id(this.selectedSessionId);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -527,8 +527,15 @@ handler.updateDisplays = function(v) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handler.setMultipleUserSession = function(usid,uname) {
|
handler.setMultipleWindowsSession = function(sessions) {
|
||||||
msgbox("multiple-sessions", translate("Multiple active user sessions found"), usid+"-"+uname, "", function(res) {});
|
// It will be covered by other message box if the timer is not used,
|
||||||
|
self.timer(1000ms, function() {
|
||||||
|
msgbox("multiple-sessions-nocancel", translate("Multiple active user sessions found"), <MultipleSessionComponent sessions={sessions} />, "", function(res) {
|
||||||
|
if (res && res.sid) {
|
||||||
|
handler.set_selected_windows_session_id("" + res.sid);
|
||||||
|
}
|
||||||
|
}, 230);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function updatePrivacyMode() {
|
function updatePrivacyMode() {
|
||||||
|
@ -312,6 +312,9 @@ class MsgboxComponent: Reactor.Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (this.type == "multiple-sessions-nocancel") {
|
||||||
|
values.sid = (this.$$(select))[0].value;
|
||||||
|
}
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,14 +259,17 @@ impl InvokeUiSession for SciterHandler {
|
|||||||
// Ignore for sciter version.
|
// Ignore for sciter version.
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_multiple_user_session(&self, sessions: Vec<hbb_common::message_proto::RdpUserSession>) {
|
fn set_multiple_windows_session(&self, sessions: Vec<WindowsSession>) {
|
||||||
let formatted_sessions: Vec<String> = sessions.iter()
|
let mut v = Value::array(0);
|
||||||
.map(|session| format!("{}-{}", session.user_session_id, session.user_name))
|
let mut sessions = sessions;
|
||||||
.collect();
|
for s in sessions.drain(..) {
|
||||||
let u_sids: String = formatted_sessions.iter().map(|s| s.split("-").next().unwrap().to_string()).collect::<Vec<String>>().join(",");
|
let mut obj = Value::map();
|
||||||
let u_names:String = formatted_sessions.iter().map(|s| s.split("-").nth(1).unwrap().to_string()).collect::<Vec<String>>().join(",");
|
obj.set_item("sid", s.sid.to_string());
|
||||||
self.call("setMultipleUserSession", &make_args!(u_sids, u_names));
|
obj.set_item("name", s.name);
|
||||||
}
|
v.push(obj);
|
||||||
|
}
|
||||||
|
self.call("setMultipleWindowsSession", &make_args!(v));
|
||||||
|
}
|
||||||
|
|
||||||
fn on_connected(&self, conn_type: ConnType) {
|
fn on_connected(&self, conn_type: ConnType) {
|
||||||
match conn_type {
|
match conn_type {
|
||||||
@ -355,7 +358,6 @@ impl sciter::EventHandler for SciterSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn detached(&mut self, _root: HELEMENT) {
|
fn detached(&mut self, _root: HELEMENT) {
|
||||||
self.set_selected_user_session_id("".to_string());
|
|
||||||
*self.element.lock().unwrap() = None;
|
*self.element.lock().unwrap() = None;
|
||||||
self.sender.write().unwrap().take().map(|sender| {
|
self.sender.write().unwrap().take().map(|sender| {
|
||||||
sender.send(Data::Close).ok();
|
sender.send(Data::Close).ok();
|
||||||
@ -386,7 +388,7 @@ impl sciter::EventHandler for SciterSession {
|
|||||||
let site = AssetPtr::adopt(ptr as *mut video_destination);
|
let site = AssetPtr::adopt(ptr as *mut video_destination);
|
||||||
log::debug!("[video] start video");
|
log::debug!("[video] start video");
|
||||||
*VIDEO.lock().unwrap() = Some(site);
|
*VIDEO.lock().unwrap() = Some(site);
|
||||||
self.reconnect(false, "".to_string());
|
self.reconnect(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BEHAVIOR_EVENTS::VIDEO_INITIALIZED => {
|
BEHAVIOR_EVENTS::VIDEO_INITIALIZED => {
|
||||||
@ -436,7 +438,7 @@ impl sciter::EventHandler for SciterSession {
|
|||||||
fn transfer_file();
|
fn transfer_file();
|
||||||
fn tunnel();
|
fn tunnel();
|
||||||
fn lock_screen();
|
fn lock_screen();
|
||||||
fn reconnect(bool, String);
|
fn reconnect(bool);
|
||||||
fn get_chatbox();
|
fn get_chatbox();
|
||||||
fn get_icon();
|
fn get_icon();
|
||||||
fn get_home_dir();
|
fn get_home_dir();
|
||||||
@ -487,7 +489,7 @@ impl sciter::EventHandler for SciterSession {
|
|||||||
fn request_voice_call();
|
fn request_voice_call();
|
||||||
fn close_voice_call();
|
fn close_voice_call();
|
||||||
fn version_cmp(String, String);
|
fn version_cmp(String, String);
|
||||||
fn set_selected_user_session_id(String);
|
fn set_selected_windows_session_id(String);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -591,8 +593,8 @@ impl SciterSession {
|
|||||||
log::info!("size saved");
|
log::info!("size saved");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_selected_user_session_id(&mut self, u_sid: String) {
|
fn set_selected_windows_session_id(&mut self, u_sid: String) {
|
||||||
self.lc.write().unwrap().selected_user_session_id = u_sid;
|
self.send_selected_session_id(u_sid);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_port_forwards(&mut self) -> Value {
|
fn get_port_forwards(&mut self) -> Value {
|
||||||
|
@ -1003,7 +1003,7 @@ impl<T: InvokeUiSession> Session<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reconnect(&self, force_relay: bool, user_session_id: String) {
|
pub fn reconnect(&self, force_relay: bool) {
|
||||||
// 1. If current session is connecting, do not reconnect.
|
// 1. If current session is connecting, do not reconnect.
|
||||||
// 2. If the connection is established, send `Data::Close`.
|
// 2. If the connection is established, send `Data::Close`.
|
||||||
// 3. If the connection is disconnected, do nothing.
|
// 3. If the connection is disconnected, do nothing.
|
||||||
@ -1023,9 +1023,6 @@ impl<T: InvokeUiSession> Session<T> {
|
|||||||
if true == force_relay {
|
if true == force_relay {
|
||||||
self.lc.write().unwrap().force_relay = true;
|
self.lc.write().unwrap().force_relay = true;
|
||||||
}
|
}
|
||||||
if !user_session_id.is_empty() {
|
|
||||||
self.lc.write().unwrap().selected_user_session_id = user_session_id;
|
|
||||||
}
|
|
||||||
let mut lock = self.thread.lock().unwrap();
|
let mut lock = self.thread.lock().unwrap();
|
||||||
// No need to join the previous thread, because it will exit automatically.
|
// No need to join the previous thread, because it will exit automatically.
|
||||||
// And the previous thread will not change important states.
|
// And the previous thread will not change important states.
|
||||||
@ -1254,6 +1251,19 @@ impl<T: InvokeUiSession> Session<T> {
|
|||||||
pub fn close_voice_call(&self) {
|
pub fn close_voice_call(&self) {
|
||||||
self.send(Data::CloseVoiceCall);
|
self.send(Data::CloseVoiceCall);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn send_selected_session_id(&self, sid: String) {
|
||||||
|
if let Ok(sid) = sid.parse::<u32>() {
|
||||||
|
self.lc.write().unwrap().selected_windows_session_id = Some(sid);
|
||||||
|
let mut misc = Misc::new();
|
||||||
|
misc.set_selected_sid(sid);
|
||||||
|
let mut msg = Message::new();
|
||||||
|
msg.set_misc(misc);
|
||||||
|
self.send(Data::Message(msg));
|
||||||
|
} else {
|
||||||
|
log::error!("selected invalid sid: {}", sid);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default {
|
pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default {
|
||||||
@ -1313,7 +1323,7 @@ pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default {
|
|||||||
fn next_rgba(&self, display: usize);
|
fn next_rgba(&self, display: usize);
|
||||||
#[cfg(all(feature = "gpucodec", feature = "flutter"))]
|
#[cfg(all(feature = "gpucodec", feature = "flutter"))]
|
||||||
fn on_texture(&self, display: usize, texture: *mut c_void);
|
fn on_texture(&self, display: usize, texture: *mut c_void);
|
||||||
fn set_multiple_user_session(&self, sessions: Vec<hbb_common::message_proto::RdpUserSession>);
|
fn set_multiple_windows_session(&self, sessions: Vec<WindowsSession>);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: InvokeUiSession> Deref for Session<T> {
|
impl<T: InvokeUiSession> Deref for Session<T> {
|
||||||
@ -1355,8 +1365,8 @@ impl<T: InvokeUiSession> Interface for Session<T> {
|
|||||||
handle_login_error(self.lc.clone(), err, self)
|
handle_login_error(self.lc.clone(), err, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_multiple_user_sessions(&self, sessions: Vec<hbb_common::message_proto::RdpUserSession>) {
|
fn set_multiple_windows_session(&self, sessions: Vec<WindowsSession>) {
|
||||||
self.ui_handler.set_multiple_user_session(sessions);
|
self.ui_handler.set_multiple_windows_session(sessions);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_peer_info(&self, mut pi: PeerInfo) {
|
fn handle_peer_info(&self, mut pi: PeerInfo) {
|
||||||
@ -1419,6 +1429,19 @@ impl<T: InvokeUiSession> Interface for Session<T> {
|
|||||||
crate::platform::windows::add_recent_document(&path);
|
crate::platform::windows::add_recent_document(&path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !pi.windows_sessions.sessions.is_empty() {
|
||||||
|
let selected = self
|
||||||
|
.lc
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.selected_windows_session_id
|
||||||
|
.to_owned();
|
||||||
|
if selected == Some(pi.windows_sessions.current_sid) {
|
||||||
|
self.send_selected_session_id(pi.windows_sessions.current_sid.to_string());
|
||||||
|
} else {
|
||||||
|
self.set_multiple_windows_session(pi.windows_sessions.sessions.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_hash(&self, pass: &str, hash: Hash, peer: &mut Stream) {
|
async fn handle_hash(&self, pass: &str, hash: Hash, peer: &mut Stream) {
|
||||||
|
Loading…
Reference in New Issue
Block a user