diff --git a/flutter/assets/GitHub.svg b/flutter/assets/GitHub.svg
deleted file mode 100644
index ef0bb12a7..000000000
--- a/flutter/assets/GitHub.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/flutter/assets/Google.svg b/flutter/assets/Google.svg
deleted file mode 100644
index df394a84f..000000000
--- a/flutter/assets/Google.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/flutter/assets/auth-apple.svg b/flutter/assets/auth-apple.svg
new file mode 100644
index 000000000..6933fbc3b
--- /dev/null
+++ b/flutter/assets/auth-apple.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/flutter/assets/auth-auth0.svg b/flutter/assets/auth-auth0.svg
new file mode 100644
index 000000000..dbe3ed236
--- /dev/null
+++ b/flutter/assets/auth-auth0.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/flutter/assets/auth-azure.svg b/flutter/assets/auth-azure.svg
new file mode 100644
index 000000000..b7435604d
--- /dev/null
+++ b/flutter/assets/auth-azure.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/flutter/assets/auth-default.svg b/flutter/assets/auth-default.svg
new file mode 100644
index 000000000..bf5fa9073
--- /dev/null
+++ b/flutter/assets/auth-default.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/flutter/assets/auth-facebook.svg b/flutter/assets/auth-facebook.svg
new file mode 100644
index 000000000..f58725000
--- /dev/null
+++ b/flutter/assets/auth-facebook.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/flutter/assets/auth-github.svg b/flutter/assets/auth-github.svg
new file mode 100644
index 000000000..778b7b341
--- /dev/null
+++ b/flutter/assets/auth-github.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/flutter/assets/auth-google.svg b/flutter/assets/auth-google.svg
new file mode 100644
index 000000000..18970f31a
--- /dev/null
+++ b/flutter/assets/auth-google.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/flutter/assets/Okta.svg b/flutter/assets/auth-okta.svg
similarity index 100%
rename from flutter/assets/Okta.svg
rename to flutter/assets/auth-okta.svg
diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart
index e96f02772..e518a56e0 100644
--- a/flutter/lib/common.dart
+++ b/flutter/lib/common.dart
@@ -11,6 +11,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
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/main.dart';
@@ -1240,7 +1241,7 @@ bool option2bool(String option, String value) {
option == "stop-service" ||
option == "direct-server" ||
option == "stop-rendezvous-service" ||
- option == "force-always-relay") {
+ option == kOptionForceAlwaysRelay) {
res = value == "Y";
} else {
assert(false);
@@ -1257,7 +1258,7 @@ String bool2option(String option, bool b) {
option == "stop-service" ||
option == "direct-server" ||
option == "stop-rendezvous-service" ||
- option == "force-always-relay") {
+ option == kOptionForceAlwaysRelay) {
res = b ? 'Y' : '';
} else {
assert(false);
@@ -1288,6 +1289,14 @@ bool mainGetLocalBoolOptionSync(String key) {
return option2bool(key, bind.mainGetLocalOption(key: key));
}
+bool mainGetPeerBoolOptionSync(String id, String key) {
+ return option2bool(key, bind.mainGetPeerOptionSync(id: id, key: key));
+}
+
+mainSetPeerBoolOptionSync(String id, String key, bool v) {
+ bind.mainSetPeerOptionSync(id: id, key: key, value: bool2option(key, v));
+}
+
Future matchPeer(String searchText, Peer peer) async {
if (searchText.isEmpty) {
return true;
@@ -1416,17 +1425,18 @@ Future saveWindowPosition(WindowType type, {int? windowId}) async {
k: kWindowPrefix + type.name, v: pos.toString());
if (type == WindowType.RemoteDesktop && windowId != null) {
- await _saveSessionWindowPosition(windowId, pos);
+ await _saveSessionWindowPosition(type, windowId, pos);
}
}
-Future _saveSessionWindowPosition(int windowId, LastWindowPosition pos) async {
+Future _saveSessionWindowPosition(
+ WindowType windowType, int windowId, LastWindowPosition pos) async {
final remoteList = await DesktopMultiWindow.invokeMethod(
windowId, kWindowEventGetRemoteList, null);
if (remoteList != null) {
for (final peerId in remoteList.split(',')) {
bind.sessionSetFlutterConfigByPeerId(
- id: peerId, k: kWindowPrefix, v: pos.toString());
+ id: peerId, k: kWindowPrefix + windowType.name, v: pos.toString());
}
}
}
@@ -1533,9 +1543,19 @@ Future restoreWindowPosition(WindowType type,
bool isRemotePeerPos = false;
String? pos;
+ // No need to check mainGetLocalBoolOptionSync(kOptionOpenNewConnInTabs)
+ // Though "open in tabs" is true and the new window restore peer position, it's ok.
if (type == WindowType.RemoteDesktop && windowId != null && peerId != null) {
- pos = await bind.sessionGetFlutterConfigByPeerId(
- id: peerId, k: kWindowPrefix);
+ // If the restore position is called by main window, and the peer id is not null
+ // then we may need to get the position by reading the peer config.
+ // Because the session may not be read at this time.
+ if (desktopType == DesktopType.main) {
+ pos = bind.mainGetPeerFlutterConfigSync(
+ id: peerId, k: kWindowPrefix + type.name);
+ } else {
+ pos = await bind.sessionGetFlutterConfigByPeerId(
+ id: peerId, k: kWindowPrefix + type.name);
+ }
isRemotePeerPos = pos != null;
}
pos ??= bind.getLocalFlutterConfig(k: kWindowPrefix + type.name);
@@ -1545,7 +1565,9 @@ Future restoreWindowPosition(WindowType type,
debugPrint("no window position saved, ignoring position restoration");
return false;
}
- if (type == WindowType.RemoteDesktop && !isRemotePeerPos && windowId != null) {
+ if (type == WindowType.RemoteDesktop &&
+ !isRemotePeerPos &&
+ windowId != null) {
if (lpos.offsetWidth != null) {
lpos.offsetWidth = lpos.offsetWidth! + windowId * 20;
}
@@ -1801,14 +1823,13 @@ connectMainDesktop(
required bool isTcpTunneling,
required bool isRDP,
bool? forceRelay,
- bool forceSeparateWindow = false,
}) async {
if (isFileTransfer) {
await rustDeskWinManager.newFileTransfer(id, forceRelay: forceRelay);
} else if (isTcpTunneling || isRDP) {
await rustDeskWinManager.newPortForward(id, isRDP, forceRelay: forceRelay);
} else {
- await rustDeskWinManager.newRemoteDesktop(id, forceRelay: forceRelay, forceSeparateWindow: forceSeparateWindow);
+ await rustDeskWinManager.newRemoteDesktop(id, forceRelay: forceRelay);
}
}
@@ -1822,9 +1843,16 @@ connect(
bool isFileTransfer = false,
bool isTcpTunneling = false,
bool isRDP = false,
- bool forceSeparateWindow = false,
}) async {
if (id == '') return;
+ if (!isDesktop || desktopType == DesktopType.main) {
+ try {
+ if (Get.isRegistered()) {
+ final idController = Get.find();
+ idController.text = formatID(id);
+ }
+ } catch (_) {}
+ }
id = id.replaceAll(' ', '');
final oldId = id;
id = await bind.mainHandleRelayId(id: id);
@@ -1840,7 +1868,6 @@ connect(
isTcpTunneling: isTcpTunneling,
isRDP: isRDP,
forceRelay: forceRelay,
- forceSeparateWindow: forceSeparateWindow,
);
} else {
await rustDeskWinManager.call(WindowType.Main, kWindowConnect, {
@@ -1849,7 +1876,6 @@ connect(
'isTcpTunneling': isTcpTunneling,
'isRDP': isRDP,
'forceRelay': forceRelay,
- 'forceSeparateWindow': forceSeparateWindow,
});
}
} else {
@@ -2314,3 +2340,10 @@ Widget unreadTopRightBuilder(RxInt? count, {Widget? icon}) {
],
);
}
+
+String toCapitalized(String s) {
+ if (s.isEmpty) {
+ return s;
+ }
+ return s.substring(0, 1).toUpperCase() + s.substring(1);
+}
diff --git a/flutter/lib/common/formatter/id_formatter.dart b/flutter/lib/common/formatter/id_formatter.dart
index bd7f76666..c2329d53f 100644
--- a/flutter/lib/common/formatter/id_formatter.dart
+++ b/flutter/lib/common/formatter/id_formatter.dart
@@ -35,6 +35,11 @@ class IDTextInputFormatter extends TextInputFormatter {
String formatID(String id) {
String id2 = id.replaceAll(' ', '');
+ String suffix = '';
+ if (id2.endsWith(r'\r') || id2.endsWith(r'/r')) {
+ suffix = id2.substring(id2.length - 2, id2.length);
+ id2 = id2.substring(0, id2.length - 2);
+ }
if (int.tryParse(id2) == null) return id;
String newID = '';
if (id2.length <= 3) {
@@ -47,7 +52,7 @@ String formatID(String id) {
newID += " ${id2.substring(i, i + 3)}";
}
}
- return newID;
+ return newID + suffix;
}
String trimID(String id) {
diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart
index 05a105e68..77307418a 100644
--- a/flutter/lib/common/widgets/dialog.dart
+++ b/flutter/lib/common/widgets/dialog.dart
@@ -1352,7 +1352,7 @@ customImageQualityDialog(SessionID sessionId, String id, FFI ffi) async {
msgBoxCommon(ffi.dialogManager, 'Custom Image Quality', content, [btnClose]);
}
-void deletePeerConfirmDialog(Function onSubmit) async {
+void deletePeerConfirmDialog(Function onSubmit, String title) async {
gFFI.dialogManager.show(
(setState, close, context) {
submit() async {
@@ -1368,8 +1368,10 @@ void deletePeerConfirmDialog(Function onSubmit) async {
Icons.delete_rounded,
color: Colors.red,
),
- Text(translate('Delete')).paddingOnly(
- left: 10,
+ Expanded(
+ child: Text(title, overflow: TextOverflow.ellipsis).paddingOnly(
+ left: 10,
+ ),
),
],
),
diff --git a/flutter/lib/common/widgets/login.dart b/flutter/lib/common/widgets/login.dart
index 17cc7090c..d7037e58f 100644
--- a/flutter/lib/common/widgets/login.dart
+++ b/flutter/lib/common/widgets/login.dart
@@ -12,25 +12,33 @@ import 'package:url_launcher/url_launcher.dart';
import '../../common.dart';
import './dialog.dart';
+const kOpSvgList = ['github', 'google', 'apple', 'okta', 'facebook', 'azure', 'auth0'];
+
class _IconOP extends StatelessWidget {
- final String icon;
- final double iconWidth;
+ final String op;
+ final String? icon;
final EdgeInsets margin;
const _IconOP(
{Key? key,
+ required this.op,
required this.icon,
- required this.iconWidth,
this.margin = const EdgeInsets.symmetric(horizontal: 4.0)})
: super(key: key);
@override
Widget build(BuildContext context) {
+ final svgFile = kOpSvgList.contains(op.toLowerCase()) ? op.toLowerCase() : 'default';
return Container(
margin: margin,
- child: SvgPicture.asset(
- 'assets/$icon.svg',
- width: iconWidth,
- ),
+ child: icon == null
+ ? SvgPicture.asset(
+ 'assets/auth-$svgFile.svg',
+ width: 20,
+ )
+ : SvgPicture.string(
+ icon!,
+ width: 20,
+ ),
);
}
}
@@ -38,7 +46,7 @@ class _IconOP extends StatelessWidget {
class ButtonOP extends StatelessWidget {
final String op;
final RxString curOP;
- final double iconWidth;
+ final String? icon;
final Color primaryColor;
final double height;
final Function() onTap;
@@ -47,7 +55,7 @@ class ButtonOP extends StatelessWidget {
Key? key,
required this.op,
required this.curOP,
- required this.iconWidth,
+ required this.icon,
required this.primaryColor,
required this.height,
required this.onTap,
@@ -61,7 +69,7 @@ class ButtonOP extends StatelessWidget {
width: 200,
child: Obx(() => ElevatedButton(
style: ElevatedButton.styleFrom(
- primary: curOP.value.isEmpty || curOP.value == op
+ backgroundColor: curOP.value.isEmpty || curOP.value == op
? primaryColor
: Colors.grey,
).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)),
@@ -69,17 +77,21 @@ class ButtonOP extends StatelessWidget {
child: Row(
children: [
SizedBox(
- width: 30,
- child: _IconOP(
- icon: op,
- iconWidth: iconWidth,
- margin: EdgeInsets.only(right: 5),
- )),
+ width: 30,
+ child: _IconOP(
+ op: op,
+ icon: icon,
+ margin: EdgeInsets.only(right: 5),
+ ),
+ ),
Expanded(
- child: FittedBox(
- fit: BoxFit.scaleDown,
- child: Center(
- child: Text('${translate("Continue with")} $op')))),
+ child: FittedBox(
+ fit: BoxFit.scaleDown,
+ child: Center(
+ child: Text(
+ '${translate("Continue with")} ${op.toLowerCase() == "github" ? "GitHub" : toCapitalized(op)}')),
+ ),
+ ),
],
))),
),
@@ -89,8 +101,8 @@ class ButtonOP extends StatelessWidget {
class ConfigOP {
final String op;
- final double iconWidth;
- ConfigOP({required this.op, required this.iconWidth});
+ final String? icon;
+ ConfigOP({required this.op, required this.icon});
}
class WidgetOP extends StatefulWidget {
@@ -182,7 +194,7 @@ class _WidgetOPState extends State {
ButtonOP(
op: widget.config.op,
curOP: widget.curOP,
- iconWidth: widget.config.iconWidth,
+ icon: widget.config.icon,
primaryColor: str2color(widget.config.op, 0x7f),
height: 36,
onTap: () async {
@@ -380,7 +392,7 @@ Future loginDialog() async {
final loginOptions = [].obs;
Future.delayed(Duration.zero, () async {
- loginOptions.value = await UserModel.queryLoginOptions();
+ loginOptions.value = await UserModel.queryOidcLoginOptions();
});
final res = await gFFI.dialogManager.show((setState, close, context) {
@@ -460,12 +472,8 @@ Future loginDialog() async {
}
thirdAuthWidget() => Obx(() {
- final oidcOptions = loginOptions
- .where((opt) => opt.startsWith(kAuthReqTypeOidc))
- .map((opt) => opt.substring(kAuthReqTypeOidc.length))
- .toList();
return Offstage(
- offstage: oidcOptions.isEmpty,
+ offstage: loginOptions.isEmpty,
child: Column(
children: [
const SizedBox(
@@ -480,12 +488,8 @@ Future loginDialog() async {
height: 8.0,
),
LoginWidgetOP(
- ops: [
- ConfigOP(op: 'GitHub', iconWidth: 20),
- ConfigOP(op: 'Google', iconWidth: 20),
- ConfigOP(op: 'Okta', iconWidth: 38),
- ]
- .where((op) => oidcOptions.contains(op.op.toLowerCase()))
+ ops: loginOptions
+ .map((e) => ConfigOP(op: e['name'], icon: e['icon']))
.toList(),
curOP: curOP,
cbLogin: (Map authBody) {
diff --git a/flutter/lib/common/widgets/peer_card.dart b/flutter/lib/common/widgets/peer_card.dart
index 61c5cb69e..a88bb4930 100644
--- a/flutter/lib/common/widgets/peer_card.dart
+++ b/flutter/lib/common/widgets/peer_card.dart
@@ -404,7 +404,6 @@ abstract class BasePeerCard extends StatelessWidget {
bool isFileTransfer = false,
bool isTcpTunneling = false,
bool isRDP = false,
- bool forceSeparateWindow = false,
}) {
return MenuEntryButton(
childBuilder: (TextStyle? style) => Text(
@@ -418,7 +417,6 @@ abstract class BasePeerCard extends StatelessWidget {
isFileTransfer: isFileTransfer,
isTcpTunneling: isTcpTunneling,
isRDP: isRDP,
- forceSeparateWindow: forceSeparateWindow,
);
},
padding: menuPadding,
@@ -427,25 +425,13 @@ abstract class BasePeerCard extends StatelessWidget {
}
@protected
- List> _connectActions(BuildContext context, Peer peer) {
- final actions = [_connectAction(context, peer, false)];
- if (!mainGetLocalBoolOptionSync(kOptionSeparateRemoteWindow)) {
- actions.add(_connectAction(context, peer, true));
- }
- return actions;
- }
-
- @protected
- MenuEntryBase _connectAction(
- BuildContext context, Peer peer, bool forceSeparateWindow) {
+ MenuEntryBase _connectAction(BuildContext context, Peer peer) {
return _connectCommonAction(
context,
peer.id,
(peer.alias.isEmpty
- ? translate('Connect')
- : '${translate('Connect')} ${peer.id}') +
- (forceSeparateWindow ? ' (${translate('separate window')})' : ''),
- forceSeparateWindow: forceSeparateWindow,
+ ? translate('Connect')
+ : '${translate('Connect')} ${peer.id}'),
);
}
@@ -538,15 +524,40 @@ abstract class BasePeerCard extends StatelessWidget {
);
}
+ Future> _openNewConnInAction(
+ String id, String label, String key) async {
+ return MenuEntrySwitch(
+ switchType: SwitchType.scheckbox,
+ text: translate(label),
+ getter: () async => mainGetPeerBoolOptionSync(id, key),
+ setter: (bool v) async {
+ await bind.mainSetPeerOption(
+ id: id, key: key, value: bool2option(key, v));
+ },
+ padding: menuPadding,
+ dismissOnClicked: true,
+ );
+ }
+
+ _openInTabsAction(String id) async =>
+ await _openNewConnInAction(id, 'Open in New Tab', kOptionOpenInTabs);
+
+ _openInWindowsAction(String id) async => await _openNewConnInAction(
+ id, 'Open in New Window', kOptionOpenInWindows);
+
+ _openNewConnInOptAction(String id) async =>
+ mainGetLocalBoolOptionSync(kOptionOpenNewConnInTabs)
+ ? await _openInWindowsAction(id)
+ : await _openInTabsAction(id);
+
@protected
Future _isForceAlwaysRelay(String id) async {
- return (await bind.mainGetPeerOption(id: id, key: 'force-always-relay'))
+ return (await bind.mainGetPeerOption(id: id, key: kOptionForceAlwaysRelay))
.isNotEmpty;
}
@protected
Future> _forceAlwaysRelayAction(String id) async {
- const option = 'force-always-relay';
return MenuEntrySwitch(
switchType: SwitchType.scheckbox,
text: translate('Always connect via relay'),
@@ -555,7 +566,9 @@ abstract class BasePeerCard extends StatelessWidget {
},
setter: (bool v) async {
await bind.mainSetPeerOption(
- id: id, key: option, value: bool2option(option, v));
+ id: id,
+ key: kOptionForceAlwaysRelay,
+ value: bool2option(kOptionForceAlwaysRelay, v));
},
padding: menuPadding,
dismissOnClicked: true,
@@ -623,7 +636,8 @@ abstract class BasePeerCard extends StatelessWidget {
}
}
- deletePeerConfirmDialog(onSubmit);
+ deletePeerConfirmDialog(onSubmit,
+ '${translate('Delete')} "${peer.alias.isEmpty ? formatID(peer.id) : peer.alias}"?');
},
padding: menuPadding,
dismissOnClicked: true,
@@ -813,7 +827,7 @@ class RecentPeerCard extends BasePeerCard {
Future>> _buildMenuItems(
BuildContext context) async {
final List> menuItems = [
- ..._connectActions(context, peer),
+ _connectAction(context, peer),
_transferFileAction(context, peer.id),
];
@@ -822,6 +836,7 @@ class RecentPeerCard extends BasePeerCard {
if (isDesktop && peer.platform != 'Android') {
menuItems.add(_tcpTunnelingAction(context, peer.id));
}
+ // menuItems.add(await _openNewConnInOptAction(peer.id));
menuItems.add(await _forceAlwaysRelayAction(peer.id));
if (peer.platform == 'Windows') {
menuItems.add(_rdpAction(context, peer.id));
@@ -869,12 +884,13 @@ class FavoritePeerCard extends BasePeerCard {
Future>> _buildMenuItems(
BuildContext context) async {
final List> menuItems = [
- ..._connectActions(context, peer),
+ _connectAction(context, peer),
_transferFileAction(context, peer.id),
];
if (isDesktop && peer.platform != 'Android') {
menuItems.add(_tcpTunnelingAction(context, peer.id));
}
+ // menuItems.add(await _openNewConnInOptAction(peer.id));
menuItems.add(await _forceAlwaysRelayAction(peer.id));
if (peer.platform == 'Windows') {
menuItems.add(_rdpAction(context, peer.id));
@@ -919,7 +935,7 @@ class DiscoveredPeerCard extends BasePeerCard {
Future>> _buildMenuItems(
BuildContext context) async {
final List> menuItems = [
- ..._connectActions(context, peer),
+ _connectAction(context, peer),
_transferFileAction(context, peer.id),
];
@@ -928,6 +944,7 @@ class DiscoveredPeerCard extends BasePeerCard {
if (isDesktop && peer.platform != 'Android') {
menuItems.add(_tcpTunnelingAction(context, peer.id));
}
+ // menuItems.add(await _openNewConnInOptAction(peer.id));
menuItems.add(await _forceAlwaysRelayAction(peer.id));
if (peer.platform == 'Windows') {
menuItems.add(_rdpAction(context, peer.id));
@@ -971,12 +988,13 @@ class AddressBookPeerCard extends BasePeerCard {
Future>> _buildMenuItems(
BuildContext context) async {
final List> menuItems = [
- ..._connectActions(context, peer),
+ _connectAction(context, peer),
_transferFileAction(context, peer.id),
];
if (isDesktop && peer.platform != 'Android') {
menuItems.add(_tcpTunnelingAction(context, peer.id));
}
+ // menuItems.add(await _openNewConnInOptAction(peer.id));
menuItems.add(await _forceAlwaysRelayAction(peer.id));
if (peer.platform == 'Windows') {
menuItems.add(_rdpAction(context, peer.id));
@@ -1033,12 +1051,13 @@ class MyGroupPeerCard extends BasePeerCard {
Future>> _buildMenuItems(
BuildContext context) async {
final List> menuItems = [
- ..._connectActions(context, peer),
+ _connectAction(context, peer),
_transferFileAction(context, peer.id),
];
if (isDesktop && peer.platform != 'Android') {
menuItems.add(_tcpTunnelingAction(context, peer.id));
}
+ // menuItems.add(await _openNewConnInOptAction(peer.id));
menuItems.add(await _forceAlwaysRelayAction(peer.id));
if (peer.platform == 'Windows') {
menuItems.add(_rdpAction(context, peer.id));
diff --git a/flutter/lib/common/widgets/peer_tab_page.dart b/flutter/lib/common/widgets/peer_tab_page.dart
index f2de21dc7..e8cbd2619 100644
--- a/flutter/lib/common/widgets/peer_tab_page.dart
+++ b/flutter/lib/common/widgets/peer_tab_page.dart
@@ -312,7 +312,7 @@ class _PeerTabPageState extends State
showToast(translate('Successful'));
}
- deletePeerConfirmDialog(onSubmit);
+ deletePeerConfirmDialog(onSubmit, translate('Delete'));
},
child: Tooltip(
message: translate('Delete'),
diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart
index 99a87362e..5376196e4 100644
--- a/flutter/lib/consts.dart
+++ b/flutter/lib/consts.dart
@@ -22,8 +22,6 @@ const String kAppTypeDesktopRemote = "remote";
const String kAppTypeDesktopFileTransfer = "file transfer";
const String kAppTypeDesktopPortForward = "port forward";
-const bool kCloseMultiWindowByHide = true;
-
const String kWindowMainWindowOnTop = "main_window_on_top";
const String kWindowGetWindowInfo = "get_window_info";
const String kWindowDisableGrabKeyboard = "disable_grab_keyboard";
@@ -42,7 +40,10 @@ const String kWindowEventGetSessionIdList = "get_session_id_list";
const String kWindowEventMoveTabToNewWindow = "move_tab_to_new_window";
const String kWindowEventCloseForSeparateWindow = "close_for_separate_window";
-const String kOptionSeparateRemoteWindow = "allow-separate-remote-window";
+const String kOptionOpenNewConnInTabs = "enable-open-new-connections-in-tabs";
+const String kOptionOpenInTabs = "allow-open-in-tabs";
+const String kOptionOpenInWindows = "allow-open-in-windows";
+const String kOptionForceAlwaysRelay = "force-always-relay";
const String kUniLinksPrefix = "rustdesk://";
const String kUrlActionClose = "close";
diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart
index 45181c4a3..de87317b3 100644
--- a/flutter/lib/desktop/pages/connection_page.dart
+++ b/flutter/lib/desktop/pages/connection_page.dart
@@ -68,6 +68,7 @@ class _ConnectionPageState extends State
_idController.selection = TextSelection(
baseOffset: 0, extentOffset: _idController.value.text.length);
});
+ Get.put(_idController);
windowManager.addListener(this);
}
@@ -76,6 +77,9 @@ class _ConnectionPageState extends State
_idController.dispose();
_updateTimer?.cancel();
windowManager.removeListener(this);
+ if (Get.isRegistered()) {
+ Get.delete();
+ }
super.dispose();
}
diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart
index b87820b97..7bf303a68 100644
--- a/flutter/lib/desktop/pages/desktop_home_page.dart
+++ b/flutter/lib/desktop/pages/desktop_home_page.dart
@@ -554,13 +554,7 @@ class _DesktopHomePageState extends State
} else if (call.method == kWindowEventShow) {
await rustDeskWinManager.registerActiveWindow(call.arguments["id"]);
} else if (call.method == kWindowEventHide) {
- final wId = call.arguments['id'];
- final isSeparateWindowEnabled =
- mainGetLocalBoolOptionSync(kOptionSeparateRemoteWindow);
- if (isSeparateWindowEnabled && !kCloseMultiWindowByHide) {
- await rustDeskWinManager.destroyWindow(wId);
- }
- await rustDeskWinManager.unregisterActiveWindow(wId);
+ await rustDeskWinManager.unregisterActiveWindow(call.arguments['id']);
} else if (call.method == kWindowConnect) {
await connectMainDesktop(
call.arguments['id'],
@@ -568,7 +562,6 @@ class _DesktopHomePageState extends State
isTcpTunneling: call.arguments['isTcpTunneling'],
isRDP: call.arguments['isRDP'],
forceRelay: call.arguments['forceRelay'],
- forceSeparateWindow: call.arguments['forceSeparateWindow'],
);
} else if (call.method == kWindowEventMoveTabToNewWindow) {
final args = call.arguments.split(',');
diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart
index 4a4992c58..591efd4c7 100644
--- a/flutter/lib/desktop/pages/desktop_setting_page.dart
+++ b/flutter/lib/desktop/pages/desktop_setting_page.dart
@@ -319,8 +319,8 @@ class _GeneralState extends State<_General> {
_OptionCheckBox(context, 'Adaptive bitrate', 'enable-abr'),
_OptionCheckBox(
context,
- 'Separate remote windows',
- kOptionSeparateRemoteWindow,
+ 'Open new connections in tabs',
+ kOptionOpenNewConnInTabs,
isServer: false,
),
];
@@ -995,16 +995,19 @@ class _NetworkState extends State<_Network> with AutomaticKeepAliveClientMixin {
return false;
}
}
- final old = await bind.mainGetOption(key: 'custom-rendezvous-server');
- if (old.isNotEmpty && old != idServer) {
- await gFFI.userModel.logOut();
- }
+ final oldApiServer = await bind.mainGetApiServer();
+
// should set one by one
await bind.mainSetOption(
key: 'custom-rendezvous-server', value: idServer);
await bind.mainSetOption(key: 'relay-server', value: relayServer);
await bind.mainSetOption(key: 'api-server', value: apiServer);
await bind.mainSetOption(key: 'key', value: key);
+
+ final newApiServer = await bind.mainGetApiServer();
+ if (oldApiServer.isNotEmpty && oldApiServer != newApiServer) {
+ await gFFI.userModel.logOut(apiServer: oldApiServer);
+ }
return true;
}
diff --git a/flutter/lib/desktop/pages/install_page.dart b/flutter/lib/desktop/pages/install_page.dart
index 69cf2ac93..871030e15 100644
--- a/flutter/lib/desktop/pages/install_page.dart
+++ b/flutter/lib/desktop/pages/install_page.dart
@@ -282,7 +282,7 @@ class _InstallPageBodyState extends State<_InstallPageBody>
title: null,
content: SelectionArea(
child:
- msgboxContent('info', 'Warning', 'comfirm_install_cert_tip')),
+ msgboxContent('info', 'Warning', 'confirm_install_cert_tip')),
actions: btns,
onCancel: close,
),
diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart
index 32faf08ae..28212e4ca 100644
--- a/flutter/lib/desktop/pages/remote_page.dart
+++ b/flutter/lib/desktop/pages/remote_page.dart
@@ -116,11 +116,7 @@ class _RemotePageState extends State
Wakelock.enable();
}
// Register texture.
- if (mainGetLocalBoolOptionSync(kOptionSeparateRemoteWindow)) {
- _renderTexture = renderTexture;
- } else {
- _renderTexture = RenderTexture();
- }
+ _renderTexture = RenderTexture();
_renderTexture.create(sessionId);
_ffi.ffiModel.updateEventListener(sessionId, widget.id);
diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart
index 2a7d1452e..2645ea0f2 100644
--- a/flutter/lib/desktop/widgets/tabbar_widget.dart
+++ b/flutter/lib/desktop/widgets/tabbar_widget.dart
@@ -582,8 +582,6 @@ class WindowActionPanelState extends State
}
await windowManager.hide();
} else {
- renderTexture.destroy();
-
// it's safe to hide the subwindow
final controller = WindowController.fromWindowId(kWindowId!);
if (Platform.isMacOS && await controller.isFullScreen()) {
diff --git a/flutter/lib/mobile/pages/connection_page.dart b/flutter/lib/mobile/pages/connection_page.dart
index 48c56e6a5..c99462166 100644
--- a/flutter/lib/mobile/pages/connection_page.dart
+++ b/flutter/lib/mobile/pages/connection_page.dart
@@ -66,6 +66,7 @@ class _ConnectionPageState extends State {
_idController.addListener(() {
_idEmpty.value = _idController.text.isEmpty;
});
+ Get.put(_idController);
}
@override
@@ -195,6 +196,9 @@ class _ConnectionPageState extends State {
@override
void dispose() {
_idController.dispose();
+ if (Get.isRegistered()) {
+ Get.delete();
+ }
super.dispose();
}
}
diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart
index eea1498db..e7e6daade 100644
--- a/flutter/lib/mobile/pages/settings_page.dart
+++ b/flutter/lib/mobile/pages/settings_page.dart
@@ -242,7 +242,7 @@ class _SettingsState extends State with WidgetsBindingObserver {
},
),
SettingsTile.switchTile(
- title: Text('${translate('Adaptive Bitrate')} (beta)'),
+ title: Text('${translate('Adaptive bitrate')} (beta)'),
initialValue: _enableAbr,
onToggle: (v) async {
await bind.mainSetOption(key: "enable-abr", value: v ? "" : "N");
diff --git a/flutter/lib/models/desktop_render_texture.dart b/flutter/lib/models/desktop_render_texture.dart
index 37d387eb2..f59373623 100644
--- a/flutter/lib/models/desktop_render_texture.dart
+++ b/flutter/lib/models/desktop_render_texture.dart
@@ -38,9 +38,4 @@ class RenderTexture {
_textureKey = -1;
}
}
-
- static final RenderTexture instance = RenderTexture();
}
-
-// Global instance for separate texture
-final renderTexture = RenderTexture.instance;
diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart
index c9bcc0477..9c4ff281c 100644
--- a/flutter/lib/models/model.dart
+++ b/flutter/lib/models/model.dart
@@ -425,19 +425,18 @@ class FfiModel with ChangeNotifier {
closeConnection();
}
- Future.delayed(Duration.zero, () async {
- await dialogManager.show(
- (setState, close, context) => CustomAlertDialog(
- title: null,
- content: SelectionArea(child: msgboxContent(type, title, text)),
- actions: [
- dialogButton("Cancel", onPressed: onClose, isOutline: true)
- ],
- onCancel: onClose),
- tag: '$sessionId-waiting-for-image',
- );
- _waitForImageDialogShow[sessionId] = true;
- });
+ dialogManager.show(
+ (setState, close, context) => CustomAlertDialog(
+ title: null,
+ content: SelectionArea(child: msgboxContent(type, title, text)),
+ actions: [
+ dialogButton("Cancel", onPressed: onClose, isOutline: true)
+ ],
+ onCancel: onClose),
+ tag: '$sessionId-waiting-for-image',
+ );
+ _waitForImageDialogShow[sessionId] = true;
+ bind.sessionOnWaitingForImageDialogShow(sessionId: sessionId);
}
_updateSessionWidthHeight(SessionID sessionId) {
@@ -1532,12 +1531,13 @@ class RecordingModel with ChangeNotifier {
sessionId: sessionId, start: true, width: width, height: height);
}
- toggle() {
+ toggle() async {
if (isIOS) return;
final sessionId = parent.target?.sessionId;
if (sessionId == null) return;
_start = !_start;
notifyListeners();
+ await bind.sessionRecordStatus(sessionId: sessionId, status: _start);
if (_start) {
bind.sessionRefresh(sessionId: sessionId);
} else {
@@ -1901,10 +1901,5 @@ Future initializeCursorAndCanvas(FFI ffi) async {
}
clearWaitingForImage(OverlayDialogManager? dialogManager, SessionID sessionId) {
- final durations = [100, 500, 1000, 2000];
- for (var duration in durations) {
- Future.delayed(Duration(milliseconds: duration), () {
- dialogManager?.dismissByTag('$sessionId-waiting-for-image');
- });
- }
+ dialogManager?.dismissByTag('$sessionId-waiting-for-image');
}
diff --git a/flutter/lib/models/user_model.dart b/flutter/lib/models/user_model.dart
index 83df2e632..003577ba0 100644
--- a/flutter/lib/models/user_model.dart
+++ b/flutter/lib/models/user_model.dart
@@ -101,10 +101,10 @@ class UserModel {
await Future.wait([gFFI.abModel.pullAb(), gFFI.groupModel.pull()]);
}
- Future logOut() async {
+ Future logOut({String? apiServer}) async {
final tag = gFFI.dialogManager.showLoading(translate('Waiting'));
try {
- final url = await bind.mainGetApiServer();
+ final url = apiServer ?? await bind.mainGetApiServer();
final authHeaders = getHttpHeaders();
authHeaders['Content-Type'] = "application/json";
await http
@@ -163,15 +163,27 @@ class UserModel {
return loginResponse;
}
- static Future> queryLoginOptions() async {
+ static Future> queryOidcLoginOptions() async {
try {
final url = await bind.mainGetApiServer();
if (url.trim().isEmpty) return [];
final resp = await http.get(Uri.parse('$url/api/login-options'));
- return jsonDecode(resp.body);
+ final List ops = [];
+ for (final item in jsonDecode(resp.body)) {
+ ops.add(item as String);
+ }
+ for (final item in ops) {
+ if (item.startsWith('common-oidc/')) {
+ return jsonDecode(item.substring('common-oidc/'.length));
+ }
+ }
+ return ops
+ .where((item) => item.startsWith('oidc/'))
+ .map((item) => {'name': item.substring('oidc/'.length)})
+ .toList();
} catch (e) {
debugPrint(
- "queryLoginOptions: jsonDecode resp body failed: ${e.toString()}");
+ "queryOidcLoginOptions: jsonDecode resp body failed: ${e.toString()}");
return [];
}
}
diff --git a/flutter/lib/utils/multi_window_manager.dart b/flutter/lib/utils/multi_window_manager.dart
index 0a1858c2c..3f7d995b7 100644
--- a/flutter/lib/utils/multi_window_manager.dart
+++ b/flutter/lib/utils/multi_window_manager.dart
@@ -50,7 +50,7 @@ class RustDeskMultiWindowManager {
'session_id': sessionId,
};
await _newSession(
- true,
+ false,
WindowType.RemoteDesktop,
kWindowEventNewRemoteDesktop,
peerId,
@@ -81,19 +81,26 @@ class RustDeskMultiWindowManager {
}
_newSession(
- bool separateWindow,
+ bool openInTabs,
WindowType type,
String methodName,
String remoteId,
List windows,
String msg,
) async {
- if (separateWindow) {
- if (kCloseMultiWindowByHide && _inactiveWindows.isNotEmpty) {
+ if (openInTabs) {
+ if (windows.isEmpty) {
+ await newSessionWindow(type, remoteId, msg, windows);
+ } else {
+ call(type, methodName, msg);
+ }
+ } else {
+ if (_inactiveWindows.isNotEmpty) {
for (final windowId in windows) {
if (_inactiveWindows.contains(windowId)) {
- await DesktopMultiWindow.invokeMethod(
- windowId, methodName, msg);
+ await restoreWindowPosition(type,
+ windowId: windowId, peerId: remoteId);
+ await DesktopMultiWindow.invokeMethod(windowId, methodName, msg);
WindowController.fromWindowId(windowId).show();
registerActiveWindow(windowId);
return;
@@ -101,12 +108,6 @@ class RustDeskMultiWindowManager {
}
}
await newSessionWindow(type, remoteId, msg, windows);
- } else {
- if (windows.isEmpty) {
- await newSessionWindow(type, remoteId, msg, windows);
- } else {
- call(type, methodName, msg);
- }
}
}
@@ -119,7 +120,6 @@ class RustDeskMultiWindowManager {
bool? forceRelay,
String? switchUuid,
bool? isRDP,
- bool forceSeparateWindow = false,
}) async {
var params = {
"type": type.index,
@@ -136,11 +136,10 @@ class RustDeskMultiWindowManager {
final msg = jsonEncode(params);
// separate window for file transfer is not supported
- bool separateWindow = forceSeparateWindow ||
- (type != WindowType.FileTransfer &&
- mainGetLocalBoolOptionSync(kOptionSeparateRemoteWindow));
+ bool openInTabs = type != WindowType.RemoteDesktop ||
+ mainGetLocalBoolOptionSync(kOptionOpenNewConnInTabs);
- if (windows.length > 1 || separateWindow) {
+ if (windows.length > 1 || !openInTabs) {
for (final windowId in windows) {
if (await DesktopMultiWindow.invokeMethod(
windowId, kWindowEventActiveSession, remoteId)) {
@@ -149,7 +148,7 @@ class RustDeskMultiWindowManager {
}
}
- await _newSession(separateWindow, type, methodName, remoteId, windows, msg);
+ await _newSession(openInTabs, type, methodName, remoteId, windows, msg);
}
Future newRemoteDesktop(
@@ -157,7 +156,6 @@ class RustDeskMultiWindowManager {
String? password,
String? switchUuid,
bool? forceRelay,
- bool forceSeparateWindow = false,
}) async {
return await newSession(
WindowType.RemoteDesktop,
@@ -167,7 +165,6 @@ class RustDeskMultiWindowManager {
password: password,
forceRelay: forceRelay,
switchUuid: switchUuid,
- forceSeparateWindow: forceSeparateWindow,
);
}
diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto
index dac137e1c..e6862bc80 100644
--- a/libs/hbb_common/protos/message.proto
+++ b/libs/hbb_common/protos/message.proto
@@ -664,6 +664,7 @@ message Misc {
PluginFailure plugin_failure = 26;
uint32 full_speed_fps = 27;
uint32 auto_adjust_fps = 28;
+ bool client_record_status = 29;
}
}
diff --git a/libs/scrap/examples/benchmark.rs b/libs/scrap/examples/benchmark.rs
index f8f2e9436..83fba99d7 100644
--- a/libs/scrap/examples/benchmark.rs
+++ b/libs/scrap/examples/benchmark.rs
@@ -114,9 +114,9 @@ fn test_vpx(
let config = EncoderCfg::VPX(VpxEncoderConfig {
width: width as _,
height: height as _,
- timebase: [1, 1000],
quality,
codec: codec_id,
+ keyframe_interval: None,
});
let mut encoder = VpxEncoder::new(config).unwrap();
let mut vpxs = vec![];
@@ -161,6 +161,7 @@ fn test_av1(yuvs: &Vec>, width: usize, height: usize, quality: Q, yuv_co
width: width as _,
height: height as _,
quality,
+ keyframe_interval: None,
});
let mut encoder = AomEncoder::new(config).unwrap();
let start = Instant::now();
diff --git a/libs/scrap/examples/record-screen.rs b/libs/scrap/examples/record-screen.rs
index 2430e2872..6640d5698 100644
--- a/libs/scrap/examples/record-screen.rs
+++ b/libs/scrap/examples/record-screen.rs
@@ -113,9 +113,9 @@ fn main() -> io::Result<()> {
let mut vpx = vpx_encode::VpxEncoder::new(EncoderCfg::VPX(vpx_encode::VpxEncoderConfig {
width,
height,
- timebase: [1, 1000],
quality,
codec: vpx_codec,
+ keyframe_interval: None,
}))
.unwrap();
diff --git a/libs/scrap/src/common/aom.rs b/libs/scrap/src/common/aom.rs
index f677858a7..dcb4968e6 100644
--- a/libs/scrap/src/common/aom.rs
+++ b/libs/scrap/src/common/aom.rs
@@ -45,6 +45,7 @@ pub struct AomEncoderConfig {
pub width: u32,
pub height: u32,
pub quality: Quality,
+ pub keyframe_interval: Option,
}
pub struct AomEncoder {
@@ -105,7 +106,12 @@ mod webrtc {
c.g_timebase.num = 1;
c.g_timebase.den = kRtpTicksPerSecond;
c.g_input_bit_depth = kBitDepth;
- c.kf_mode = aom_kf_mode::AOM_KF_DISABLED;
+ if let Some(keyframe_interval) = cfg.keyframe_interval {
+ c.kf_min_dist = 0;
+ c.kf_max_dist = keyframe_interval as _;
+ } else {
+ c.kf_mode = aom_kf_mode::AOM_KF_DISABLED;
+ }
let (q_min, q_max, b) = AomEncoder::convert_quality(cfg.quality);
if q_min > 0 && q_min < q_max && q_max < 64 {
c.rc_min_quantizer = q_min;
diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs
index fac5f82d7..2c3cbff6c 100644
--- a/libs/scrap/src/common/codec.rs
+++ b/libs/scrap/src/common/codec.rs
@@ -45,6 +45,7 @@ pub struct HwEncoderConfig {
pub width: usize,
pub height: usize,
pub quality: Quality,
+ pub keyframe_interval: Option,
}
#[derive(Debug, Clone)]
diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs
index ba834e048..c1fbdfa6e 100644
--- a/libs/scrap/src/common/hwcodec.rs
+++ b/libs/scrap/src/common/hwcodec.rs
@@ -52,6 +52,7 @@ impl EncoderApi for HwEncoder {
if base_bitrate <= 0 {
bitrate = base_bitrate;
}
+ let gop = config.keyframe_interval.unwrap_or(DEFAULT_GOP as _) as i32;
let ctx = EncodeContext {
name: config.name.clone(),
width: config.width as _,
@@ -60,7 +61,7 @@ impl EncoderApi for HwEncoder {
align: HW_STRIDE_ALIGN as _,
bitrate: bitrate as i32 * 1000,
timebase: DEFAULT_TIME_BASE,
- gop: DEFAULT_GOP,
+ gop,
quality: DEFAULT_HW_QUALITY,
rc: DEFAULT_RC,
thread_count: codec_thread_num() as _, // ffmpeg's thread_count is used for cpu
diff --git a/libs/scrap/src/common/vpxcodec.rs b/libs/scrap/src/common/vpxcodec.rs
index d17aad676..1bdde0ca6 100644
--- a/libs/scrap/src/common/vpxcodec.rs
+++ b/libs/scrap/src/common/vpxcodec.rs
@@ -65,8 +65,8 @@ impl EncoderApi for VpxEncoder {
c.g_w = config.width;
c.g_h = config.height;
- c.g_timebase.num = config.timebase[0];
- c.g_timebase.den = config.timebase[1];
+ c.g_timebase.num = 1;
+ c.g_timebase.den = 1000; // Output timestamp precision
c.rc_undershoot_pct = 95;
// When the data buffer falls below this percentage of fullness, a dropped frame is indicated. Set the threshold to zero (0) to disable this feature.
// In dynamic scenes, low bitrate gets low fps while high bitrate gets high fps.
@@ -76,9 +76,13 @@ impl EncoderApi for VpxEncoder {
// https://developers.google.com/media/vp9/bitrate-modes/
// Constant Bitrate mode (CBR) is recommended for live streaming with VP9.
c.rc_end_usage = vpx_rc_mode::VPX_CBR;
- // c.kf_min_dist = 0;
- // c.kf_max_dist = 999999;
- c.kf_mode = vpx_kf_mode::VPX_KF_DISABLED; // reduce bandwidth a lot
+ if let Some(keyframe_interval) = config.keyframe_interval {
+ c.kf_min_dist = 0;
+ c.kf_max_dist = keyframe_interval as _;
+ } else {
+ c.kf_mode = vpx_kf_mode::VPX_KF_DISABLED; // reduce bandwidth a lot
+ }
+
let (q_min, q_max, b) = Self::convert_quality(config.quality);
if q_min > 0 && q_min < q_max && q_max < 64 {
c.rc_min_quantizer = q_min;
@@ -343,12 +347,12 @@ pub struct VpxEncoderConfig {
pub width: c_uint,
/// The height (in pixels).
pub height: c_uint,
- /// The timebase numerator and denominator (in seconds).
- pub timebase: [c_int; 2],
/// The image quality
pub quality: Quality,
/// The codec
pub codec: VpxVideoCodecId,
+ /// keyframe interval
+ pub keyframe_interval: Option,
}
#[derive(Clone, Copy, Debug)]
diff --git a/src/client.rs b/src/client.rs
index dfd73455c..9c039464c 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -1209,6 +1209,10 @@ impl LoginConfigHandler {
/// * `k` - key of option
/// * `v` - value of option
pub fn save_ui_flutter(&mut self, k: String, v: String) {
+ if self.version == 0 && k == "wm_" {
+ log::info!("skip saving {k}");
+ return;
+ }
let mut config = self.load_config();
config.ui_flutter.insert(k, v);
self.save_config(config);
diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs
index c24d0f54c..f8cdb3ea0 100644
--- a/src/client/io_loop.rs
+++ b/src/client/io_loop.rs
@@ -850,6 +850,11 @@ impl Remote {
}
pub async fn sync_jobs_status_to_local(&mut self) -> bool {
+ let peer_version = self.handler.lc.read().unwrap().version;
+ if peer_version == 0 {
+ log::info!("skip saving job status");
+ return false;
+ }
log::info!("sync transfer job status");
let mut config: PeerConfig = self.handler.load_config();
let mut transfer_metas = TransferSerde::default();
diff --git a/src/flutter.rs b/src/flutter.rs
index 88290897d..52190ce2e 100644
--- a/src/flutter.rs
+++ b/src/flutter.rs
@@ -345,6 +345,14 @@ impl FlutterHandler {
*self.notify_rendered.write().unwrap() = false;
self.renderer.write().unwrap().set_size(width, height);
}
+
+ pub fn on_waiting_for_image_dialog_show(&self) {
+ #[cfg(any(feature = "flutter_texture_render"))]
+ {
+ *self.notify_rendered.write().unwrap() = false;
+ }
+ // rgba array render will notify every frame
+ }
}
impl InvokeUiSession for FlutterHandler {
diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs
index c4e9c2eb3..122af0c2b 100644
--- a/src/flutter_ffi.rs
+++ b/src/flutter_ffi.rs
@@ -174,6 +174,12 @@ pub fn session_record_screen(session_id: SessionID, start: bool, width: usize, h
}
}
+pub fn session_record_status(session_id: SessionID, status: bool) {
+ if let Some(session) = SESSIONS.read().unwrap().get(&session_id) {
+ session.record_status(status);
+ }
+}
+
pub fn session_reconnect(session_id: SessionID, force_relay: bool) {
if let Some(session) = SESSIONS.read().unwrap().get(&session_id) {
session.reconnect(force_relay);
@@ -800,6 +806,12 @@ pub fn main_get_peer_option_sync(id: String, key: String) -> SyncReturn
SyncReturn(get_peer_option(id, key))
}
+// Sometimes we need to get the flutter config of a peer by reading the file.
+// Because the session may not be established yet.
+pub fn main_get_peer_flutter_config_sync(id: String, k: String) -> SyncReturn {
+ SyncReturn(get_peer_flutter_config(id, k))
+}
+
pub fn main_set_peer_option(id: String, key: String, value: String) {
set_peer_option(id, key, value)
}
@@ -1254,6 +1266,12 @@ pub fn session_change_prefer_codec(session_id: SessionID) {
}
}
+pub fn session_on_waiting_for_image_dialog_show(session_id: SessionID) {
+ if let Some(session) = SESSIONS.read().unwrap().get(&session_id) {
+ session.ui_handler.on_waiting_for_image_dialog_show();
+ }
+}
+
pub fn main_set_home_dir(_home: String) {
#[cfg(any(target_os = "android", target_os = "ios"))]
{
diff --git a/src/hbbs_http/account.rs b/src/hbbs_http/account.rs
index bc078440a..2afd4b83e 100644
--- a/src/hbbs_http/account.rs
+++ b/src/hbbs_http/account.rs
@@ -1,8 +1,5 @@
use super::HbbHttpResponse;
-use hbb_common::{
- config::{Config, LocalConfig},
- log, ResultType,
-};
+use hbb_common::{config::LocalConfig, log, ResultType};
use reqwest::blocking::Client;
use serde_derive::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
@@ -14,8 +11,6 @@ use std::{
use url::Url;
lazy_static::lazy_static! {
- static ref API_SERVER: String = crate::get_api_server(
- Config::get_option("api-server"), Config::get_option("custom-rendezvous-server"));
static ref OIDC_SESSION: Arc> = Arc::new(RwLock::new(OidcSession::new()));
}
@@ -142,20 +137,30 @@ impl OidcSession {
}
}
- fn auth(op: &str, id: &str, uuid: &str) -> ResultType> {
+ fn auth(
+ api_server: &str,
+ op: &str,
+ id: &str,
+ uuid: &str,
+ ) -> ResultType> {
Ok(OIDC_SESSION
.read()
.unwrap()
.client
- .post(format!("{}/api/oidc/auth", *API_SERVER))
+ .post(format!("{}/api/oidc/auth", api_server))
.json(&HashMap::from([("op", op), ("id", id), ("uuid", uuid)]))
.send()?
.try_into()?)
}
- fn query(code: &str, id: &str, uuid: &str) -> ResultType> {
+ fn query(
+ api_server: &str,
+ code: &str,
+ id: &str,
+ uuid: &str,
+ ) -> ResultType> {
let url = reqwest::Url::parse_with_params(
- &format!("{}/api/oidc/auth-query", *API_SERVER),
+ &format!("{}/api/oidc/auth-query", api_server),
&[("code", code), ("id", id), ("uuid", uuid)],
)?;
Ok(OIDC_SESSION
@@ -189,8 +194,8 @@ impl OidcSession {
std::thread::sleep(std::time::Duration::from_secs_f32(secs));
}
- fn auth_task(op: String, id: String, uuid: String, remember_me: bool) {
- let auth_request_res = Self::auth(&op, &id, &uuid);
+ fn auth_task(api_server: String, op: String, id: String, uuid: String, remember_me: bool) {
+ let auth_request_res = Self::auth(&api_server, &op, &id, &uuid);
log::info!("Request oidc auth result: {:?}", &auth_request_res);
let code_url = match auth_request_res {
Ok(HbbHttpResponse::<_>::Data(code_url)) => code_url,
@@ -226,7 +231,7 @@ impl OidcSession {
let begin = Instant::now();
let query_timeout = OIDC_SESSION.read().unwrap().query_timeout;
while OIDC_SESSION.read().unwrap().keep_querying && begin.elapsed() < query_timeout {
- match Self::query(&code_url.code, &id, &uuid) {
+ match Self::query(&api_server, &code_url.code, &id, &uuid) {
Ok(HbbHttpResponse::<_>::Data(auth_body)) => {
if remember_me {
LocalConfig::set_option(
@@ -289,12 +294,18 @@ impl OidcSession {
}
}
- pub fn account_auth(op: String, id: String, uuid: String, remember_me: bool) {
+ pub fn account_auth(
+ api_server: String,
+ op: String,
+ id: String,
+ uuid: String,
+ remember_me: bool,
+ ) {
Self::auth_cancel();
Self::wait_stop_querying();
OIDC_SESSION.write().unwrap().before_task();
std::thread::spawn(move || {
- Self::auth_task(op, id, uuid, remember_me);
+ Self::auth_task(api_server, op, id, uuid, remember_me);
OIDC_SESSION.write().unwrap().after_task();
});
}
diff --git a/src/lang/ca.rs b/src/lang/ca.rs
index 45cee7d83..b3d913b62 100644
--- a/src/lang/ca.rs
+++ b/src/lang/ca.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", ""),
("Set one-time password length", ""),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", ""),
("Sort by", ""),
("New Connection", ""),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/cn.rs b/src/lang/cn.rs
index d2e3ef28a..83060c6af 100644
--- a/src/lang/cn.rs
+++ b/src/lang/cn.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", "无进行中的传输"),
("Set one-time password length", "设置一次性密码长度"),
("install_cert_tip", "安装 RustDesk 证书"),
- ("comfirm_install_cert_tip", "此证书为 RustDesk 测试证书,您可以信任此证书。证书将被用于信任和安装 RustDesk 驱动。"),
+ ("confirm_install_cert_tip", "此证书为 RustDesk 测试证书,您可以信任此证书。证书将被用于信任和安装 RustDesk 驱动。"),
("RDP Settings", "RDP 设置"),
("Sort by", "排序方式"),
("New Connection", "新连接"),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", "管理的设备数已达到最大值"),
("Sync with recent sessions", "同步最近会话"),
("Sort tags", "对标签进行排序"),
- ("Separate remote windows", "使用独立远程窗口"),
- ("separate window", "独立窗口"),
+ ("Open connection in new tab", "在选项卡中打开新连接"),
("Move tab to new window", "将标签页移至新窗口"),
].iter().cloned().collect();
}
diff --git a/src/lang/cs.rs b/src/lang/cs.rs
index 563f27f89..2d7a7ce16 100644
--- a/src/lang/cs.rs
+++ b/src/lang/cs.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", ""),
("Set one-time password length", ""),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", ""),
("Sort by", ""),
("New Connection", ""),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/da.rs b/src/lang/da.rs
index c695b164c..0a10551d3 100644
--- a/src/lang/da.rs
+++ b/src/lang/da.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", "Ingen overførsler i gang"),
("Set one-time password length", "Sæt engangsadgangskode længde"),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", "RDP indstillinger"),
("Sort by", "Sortér efter"),
("New Connection", "Ny forbindelse"),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/de.rs b/src/lang/de.rs
index 3401d1517..ffb62a45f 100644
--- a/src/lang/de.rs
+++ b/src/lang/de.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", "Keine Übertragungen im Gange"),
("Set one-time password length", "Länge des Einmalpassworts festlegen"),
("install_cert_tip", "RustDesk-Zertifikat installieren"),
- ("comfirm_install_cert_tip", "Dies ist ein RustDesk-Testzertifikat, dem vertraut werden kann. Das Zertifikat wird verwendet, um RustDesk-Treibern bei Bedarf zu vertrauen und diese zu installieren."),
+ ("confirm_install_cert_tip", "Dies ist ein RustDesk-Testzertifikat, dem vertraut werden kann. Das Zertifikat wird verwendet, um RustDesk-Treibern bei Bedarf zu vertrauen und diese zu installieren."),
("RDP Settings", "RDP-Einstellungen"),
("Sort by", "Sortieren nach"),
("New Connection", "Neue Verbindung"),
@@ -506,7 +506,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enable", "Aktivieren"),
("Disable", "Deaktivieren"),
("Options", "Einstellungen"),
- ("resolution_original_tip", "Originalauflösung"),
+ ("resolution_original_tip", "Originale Auflösung"),
("resolution_fit_local_tip", "Lokale Auflösung anpassen"),
("resolution_custom_tip", "Benutzerdefinierte Auflösung"),
("Collapse toolbar", "Symbolleiste einklappen"),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", "Sie haben die maximale Anzahl der verwalteten Geräte erreicht."),
("Sync with recent sessions", "Synchronisierung mit den letzten Sitzungen"),
("Sort tags", "Tags sortieren"),
- ("Separate remote windows", "Separate entfernte Fenster"),
- ("separate window", "Separates Fenster"),
+ ("Open connection in new tab", "Verbindung in neuem Tab öffnen"),
("Move tab to new window", "Tab in neues Fenster verschieben"),
].iter().cloned().collect();
}
diff --git a/src/lang/el.rs b/src/lang/el.rs
index ac0539d35..33f4d74b8 100644
--- a/src/lang/el.rs
+++ b/src/lang/el.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", "Δεν υπάρχει μεταφορά σε εξέλιξη"),
("Set one-time password length", "Μέγεθος κωδικού μιας χρήσης"),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", "Ρυθμίσεις RDP"),
("Sort by", "Ταξινόμηση κατά"),
("New Connection", "Νέα σύνδεση"),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/en.rs b/src/lang/en.rs
index 001d9133d..2a9b40568 100644
--- a/src/lang/en.rs
+++ b/src/lang/en.rs
@@ -12,6 +12,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("not_close_tcp_tip", "Don't close this window while you are using the tunnel"),
("setup_server_tip", "For faster connection, please set up your own server"),
("Auto Login", "Auto Login (Only valid if you set \"Lock after session end\")"),
+ ("Always connect via relay", "Always Connect via Relay"),
("whitelist_tip", "Only whitelisted IP can access me"),
("whitelist_sep", "Separated by comma, semicolon, spaces or new line"),
("Wrong credentials", "Wrong username or password"),
@@ -47,7 +48,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("relay_hint_tip", "It may not be possible to connect directly; you can try connecting via relay. Additionally, if you want to use a relay on your first attempt, you can add the \"/r\" suffix to the ID or select the option \"Always connect via relay\" in the card of recent sessions if it exists."),
("No transfers in progress", ""),
("install_cert_tip", "Install RustDesk certificate"),
- ("comfirm_install_cert_tip", "This is a RustDesk testing certificate, which can be trusted. The certificate will be used to trust and install RustDesk drivers when required."),
+ ("confirm_install_cert_tip", "This is a RustDesk testing certificate, which can be trusted. The certificate will be used to trust and install RustDesk drivers when required."),
("empty_recent_tip", "Oops, no recent sessions!\nTime to plan a new one."),
("empty_favorite_tip", "No favorite peers yet?\nLet's find someone to connect with and add it to your favorites!"),
("empty_lan_tip", "Oh no, it looks like we haven't discovered any peers yet."),
diff --git a/src/lang/eo.rs b/src/lang/eo.rs
index 26a04bc55..8b29e418e 100644
--- a/src/lang/eo.rs
+++ b/src/lang/eo.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", ""),
("Set one-time password length", ""),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", ""),
("Sort by", ""),
("New Connection", ""),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/es.rs b/src/lang/es.rs
index 894e8e266..27752e989 100644
--- a/src/lang/es.rs
+++ b/src/lang/es.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", "No hay transferencias en curso"),
("Set one-time password length", "Establecer contraseña de un solo uso"),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", "Ajustes RDP"),
("Sort by", "Ordenar por"),
("New Connection", "Nueva conexión"),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", "Has alcanzado el máximo número de dispositivos administrados."),
("Sync with recent sessions", "Sincronizar con sesiones recientes"),
("Sort tags", "Ordenar etiquetas"),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/fa.rs b/src/lang/fa.rs
index 448fa5bfd..0b56e073f 100644
--- a/src/lang/fa.rs
+++ b/src/lang/fa.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", "هیچ انتقالی در حال انجام نیست"),
("Set one-time password length", "طول رمز یکبار مصرف را تعیین کنید"),
("install_cert_tip", "RustDesk نصب گواهی"),
- ("comfirm_install_cert_tip", "استفاده خواهد شد RustDesk است و شما می توانید به این گواهی اعتماد کنید. این گواهی برای اعتماد و نصب درایورهای RustDesk این گواهینامه یک گواهی تست"),
+ ("confirm_install_cert_tip", "استفاده خواهد شد RustDesk است و شما می توانید به این گواهی اعتماد کنید. این گواهی برای اعتماد و نصب درایورهای RustDesk این گواهینامه یک گواهی تست"),
("RDP Settings", "RDP تنظیمات"),
("Sort by", "مرتب سازی بر اساس"),
("New Connection", "اتصال جدید"),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/fr.rs b/src/lang/fr.rs
index 8ec1ba8de..8a2baef55 100644
--- a/src/lang/fr.rs
+++ b/src/lang/fr.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", "Pas de transfert en cours"),
("Set one-time password length", "Définir la longueur du mot de passe à usage unique"),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", "Configuration RDP"),
("Sort by", "Trier par"),
("New Connection", "Nouvelle connexion"),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/hu.rs b/src/lang/hu.rs
index 2ef3d2b1f..36c43fec9 100644
--- a/src/lang/hu.rs
+++ b/src/lang/hu.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", ""),
("Set one-time password length", ""),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", ""),
("Sort by", ""),
("New Connection", ""),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/id.rs b/src/lang/id.rs
index ddeadc116..c65eee87d 100644
--- a/src/lang/id.rs
+++ b/src/lang/id.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", ""),
("Set one-time password length", ""),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", ""),
("Sort by", ""),
("New Connection", ""),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/it.rs b/src/lang/it.rs
index e0d79edf8..5a655ce5a 100644
--- a/src/lang/it.rs
+++ b/src/lang/it.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", "Nessun trasferimento in corso"),
("Set one-time password length", "Imposta lunghezza password monouso"),
("install_cert_tip", "Installa certificato RustDesk"),
- ("comfirm_install_cert_tip", "Questo è un certificato di test RustDesk, che può essere considerato attendibile.\nIl certificato verrà usato per certificarsi ed installare i driver RustDesk quando richiesto."),
+ ("confirm_install_cert_tip", "Questo è un certificato di test RustDesk, che può essere considerato attendibile.\nIl certificato verrà usato per certificarsi ed installare i driver RustDesk quando richiesto."),
("RDP Settings", "Impostazioni RDP"),
("Sort by", "Ordina per"),
("New Connection", "Nuova connessione"),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", "Hai raggiunto il numero massimo di dispositivi gestibili."),
("Sync with recent sessions", "Sincronizza con le sessioni recenti"),
("Sort tags", "Ordina etichette"),
- ("Separate remote windows", "Separa finestre remote"),
- ("separate window", "Separa finestra"),
+ ("Open connection in new tab", "Apri connessione in una nuova scheda"),
("Move tab to new window", "Sposta scheda nella finestra successiva"),
].iter().cloned().collect();
}
diff --git a/src/lang/ja.rs b/src/lang/ja.rs
index 3092c9305..6ece9d5c9 100644
--- a/src/lang/ja.rs
+++ b/src/lang/ja.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", ""),
("Set one-time password length", ""),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", ""),
("Sort by", ""),
("New Connection", ""),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/ko.rs b/src/lang/ko.rs
index 52604aa2e..6ea9b94ea 100644
--- a/src/lang/ko.rs
+++ b/src/lang/ko.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", ""),
("Set one-time password length", ""),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", ""),
("Sort by", ""),
("New Connection", ""),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/kz.rs b/src/lang/kz.rs
index c253ecdcd..e5d035b03 100644
--- a/src/lang/kz.rs
+++ b/src/lang/kz.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", ""),
("Set one-time password length", ""),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", ""),
("Sort by", ""),
("New Connection", ""),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/lt.rs b/src/lang/lt.rs
index 618e554d0..47b70acfd 100644
--- a/src/lang/lt.rs
+++ b/src/lang/lt.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", "Nevyksta jokių perdavimų"),
("Set one-time password length", "Nustatyti vienkartinio slaptažodžio ilgį"),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", "RDP nustatymai"),
("Sort by", "Rūšiuoti pagal"),
("New Connection", "Naujas ryšys"),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/nl.rs b/src/lang/nl.rs
index 4e47e2c11..08934cea0 100644
--- a/src/lang/nl.rs
+++ b/src/lang/nl.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", "Geen overdrachten in uitvoering"),
("Set one-time password length", "Stel de lengte van het eenmalige wachtwoord in"),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", "RDP Instellingen"),
("Sort by", "Sorteren op"),
("New Connection", "Nieuwe Verbinding"),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", "Het maximum aantal gecontroleerde apparaten is bereikt."),
("Sync with recent sessions", "Recente sessies synchroniseren"),
("Sort tags", "Labels sorteren"),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/pl.rs b/src/lang/pl.rs
index bfe9ecb3e..84f13c617 100644
--- a/src/lang/pl.rs
+++ b/src/lang/pl.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", "Brak transferów w toku"),
("Set one-time password length", "Ustaw długość jednorazowego hasła"),
("install_cert_tip", "Instalacja certyfikatu RustDesk"),
- ("comfirm_install_cert_tip", "To jest certyfikat testowy RustDesk, któremu można zaufać. Certyfikat jest używany do zaufania i instalowania sterowników RustDesk w razie potrzeby."),
+ ("confirm_install_cert_tip", "To jest certyfikat testowy RustDesk, któremu można zaufać. Certyfikat jest używany do zaufania i instalowania sterowników RustDesk w razie potrzeby."),
("RDP Settings", "Ustawienia RDP"),
("Sort by", "Sortuj wg"),
("New Connection", "Nowe połączenie"),
@@ -518,14 +518,13 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Exit", "Wyjście"),
("Open", "Otwórz"),
("logout_tip", "Na pewno chcesz się wylogować?"),
- ("Service", ""),
- ("Start", ""),
- ("Stop", ""),
- ("exceed_max_devices", ""),
- ("Sync with recent sessions", ""),
- ("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
- ("Move tab to new window", ""),
+ ("Service", "Usługa"),
+ ("Start", "Uruchom"),
+ ("Stop", "Zatrzymaj"),
+ ("exceed_max_devices", "Przekroczona maks. liczba urządzeń"),
+ ("Sync with recent sessions", "Synchronizacja z ostatnimi sesjami"),
+ ("Sort tags", "Znaczniki sortowania"),
+ ("Open connection in new tab", ""),
+ ("Move tab to new window", "Przenieś zakładkę do nowego okna"),
].iter().cloned().collect();
}
diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs
index 0d7d78eb9..b9e155364 100644
--- a/src/lang/pt_PT.rs
+++ b/src/lang/pt_PT.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", ""),
("Set one-time password length", ""),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", ""),
("Sort by", ""),
("New Connection", ""),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs
index abf3aac93..81fd68ac6 100644
--- a/src/lang/ptbr.rs
+++ b/src/lang/ptbr.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", "Nenhuma transferência em andamento"),
("Set one-time password length", "Definir comprimento de senha descartável"),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", "Configurações RDP"),
("Sort by", "Ordenar por"),
("New Connection", "Nova Conexão"),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/ro.rs b/src/lang/ro.rs
index b4b372275..ddd141fa4 100644
--- a/src/lang/ro.rs
+++ b/src/lang/ro.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", "Niciun transfer nu este în desfășurare"),
("Set one-time password length", "Definește lungimea parolei unice"),
("install_cert_tip", "Instalează certificatul RustDesk"),
- ("comfirm_install_cert_tip", "Acesta este un certificat de testare RustDesk și este de încredere. Certificatul va fi utilizat pentru a acorda încredere și instala drivere RustDesk atunci când este necesar."),
+ ("confirm_install_cert_tip", "Acesta este un certificat de testare RustDesk și este de încredere. Certificatul va fi utilizat pentru a acorda încredere și instala drivere RustDesk atunci când este necesar."),
("RDP Settings", "Setări RDP"),
("Sort by", "Sortează după"),
("New Connection", "Conexiune nouă"),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/ru.rs b/src/lang/ru.rs
index 4f0e693eb..c0f129ab0 100644
--- a/src/lang/ru.rs
+++ b/src/lang/ru.rs
@@ -299,7 +299,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Help", "Помощь"),
("Failed", "Не выполнено"),
("Succeeded", "Выполнено"),
- ("Someone turns on privacy mode, exit", "Кто-то включает режим конфиденциальности, выход"),
+ ("Someone turns on privacy mode, exit", "Кто-то включил режим конфиденциальности, выход"),
("Unsupported", "Не поддерживается"),
("Peer denied", "Отклонено удалённым узлом"),
("Please install plugins", "Установите плагины"),
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", "Передача не осуществляется"),
("Set one-time password length", "Установить длину одноразового пароля"),
("install_cert_tip", "Установить сертификат RustDesk"),
- ("comfirm_install_cert_tip", "Это тестовый сертификат RustDesk, которому можно доверять. Он будет использоваться только по необходимости для установки драйверов RustDesk."),
+ ("confirm_install_cert_tip", "Это тестовый сертификат RustDesk, которому можно доверять. Он будет использоваться только по необходимости для установки драйверов RustDesk."),
("RDP Settings", "Настройки RDP"),
("Sort by", "Сортировка"),
("New Connection", "Новое подключение"),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", "Достигнуто максимальне количество управляемых устройств."),
("Sync with recent sessions", "Синхронизация последних сессий"),
("Sort tags", "Сортировка меток"),
- ("Separate remote windows", "Отдельные удалённые окна"),
- ("separate window", "отдельное окно"),
+ ("Open connection in new tab", "Открыть подключение в новой вкладке"),
("Move tab to new window", "Переместить вкладку в отдельное окно"),
].iter().cloned().collect();
}
diff --git a/src/lang/sk.rs b/src/lang/sk.rs
index 549d5acc4..afe52f4ad 100644
--- a/src/lang/sk.rs
+++ b/src/lang/sk.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", ""),
("Set one-time password length", ""),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", ""),
("Sort by", ""),
("New Connection", ""),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/sl.rs b/src/lang/sl.rs
index c92336fc6..dc79f9e44 100755
--- a/src/lang/sl.rs
+++ b/src/lang/sl.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", ""),
("Set one-time password length", ""),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", ""),
("Sort by", ""),
("New Connection", ""),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/sq.rs b/src/lang/sq.rs
index fc311bd37..86f3f3544 100644
--- a/src/lang/sq.rs
+++ b/src/lang/sq.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", ""),
("Set one-time password length", ""),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", ""),
("Sort by", ""),
("New Connection", ""),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/sr.rs b/src/lang/sr.rs
index 5b4d6a122..06667f4c5 100644
--- a/src/lang/sr.rs
+++ b/src/lang/sr.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", ""),
("Set one-time password length", ""),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", ""),
("Sort by", ""),
("New Connection", ""),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/sv.rs b/src/lang/sv.rs
index 8b7ad3387..2e8ea8713 100644
--- a/src/lang/sv.rs
+++ b/src/lang/sv.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", ""),
("Set one-time password length", ""),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", ""),
("Sort by", ""),
("New Connection", ""),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/template.rs b/src/lang/template.rs
index 733d45268..ed3f41efc 100644
--- a/src/lang/template.rs
+++ b/src/lang/template.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", ""),
("Set one-time password length", ""),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", ""),
("Sort by", ""),
("New Connection", ""),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/th.rs b/src/lang/th.rs
index c817d4fe9..09ade050f 100644
--- a/src/lang/th.rs
+++ b/src/lang/th.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", ""),
("Set one-time password length", ""),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", ""),
("Sort by", ""),
("New Connection", ""),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/tr.rs b/src/lang/tr.rs
index bab7adf31..629a144fd 100644
--- a/src/lang/tr.rs
+++ b/src/lang/tr.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", ""),
("Set one-time password length", ""),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", ""),
("Sort by", ""),
("New Connection", ""),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/tw.rs b/src/lang/tw.rs
index 52f10c9ba..a86613651 100644
--- a/src/lang/tw.rs
+++ b/src/lang/tw.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", "沒有正在進行的傳輸"),
("Set one-time password length", "設定一次性密碼長度"),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", "RDP 設定"),
("Sort by", "排序方式"),
("New Connection", "新連線"),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/ua.rs b/src/lang/ua.rs
index 307d222e3..a12e2fd10 100644
--- a/src/lang/ua.rs
+++ b/src/lang/ua.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", "Наразі нічого не пересилається"),
("Set one-time password length", "Вказати довжину одноразового пароля"),
("install_cert_tip", "Додати сертифікат Rustdesk"),
- ("comfirm_install_cert_tip", "Це сертифікат тестування Rustdesk, якому можна довіряти. За потреби сертифікат буде використано для погодження та встановлення драйверів Rustdesk."),
+ ("confirm_install_cert_tip", "Це сертифікат тестування Rustdesk, якому можна довіряти. За потреби сертифікат буде використано для погодження та встановлення драйверів Rustdesk."),
("RDP Settings", "Налаштування RDP"),
("Sort by", "Сортувати за"),
("New Connection", "Нове підключення"),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/vn.rs b/src/lang/vn.rs
index 0c68d1c62..0db48584e 100644
--- a/src/lang/vn.rs
+++ b/src/lang/vn.rs
@@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("No transfers in progress", "Không có tệp tin nào đang được truyền"),
("Set one-time password length", "Thiết lập độ dài mật khẩu một lần"),
("install_cert_tip", ""),
- ("comfirm_install_cert_tip", ""),
+ ("confirm_install_cert_tip", ""),
("RDP Settings", "Cài đặt RDP"),
("Sort by", "Sắp xếp theo"),
("New Connection", "Kết nối mới"),
@@ -524,8 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("exceed_max_devices", ""),
("Sync with recent sessions", ""),
("Sort tags", ""),
- ("Separate remote windows", ""),
- ("separate window", ""),
+ ("Open connection in new tab", ""),
("Move tab to new window", ""),
].iter().cloned().collect();
}
diff --git a/src/server/connection.rs b/src/server/connection.rs
index f3e059696..e32a4c1c3 100644
--- a/src/server/connection.rs
+++ b/src/server/connection.rs
@@ -1907,6 +1907,10 @@ impl Connection {
.lock()
.unwrap()
.user_auto_adjust_fps(self.inner.id(), fps),
+ Some(misc::Union::ClientRecordStatus(status)) => video_service::VIDEO_QOS
+ .lock()
+ .unwrap()
+ .user_record(self.inner.id(), status),
_ => {}
},
Some(message::Union::AudioFrame(frame)) => {
diff --git a/src/server/video_qos.rs b/src/server/video_qos.rs
index 2ebcdaa84..e9eb9dc79 100644
--- a/src/server/video_qos.rs
+++ b/src/server/video_qos.rs
@@ -36,6 +36,7 @@ struct UserData {
quality: Option<(i64, Quality)>, // (time, quality)
delay: Option,
response_delayed: bool,
+ record: bool,
}
pub struct VideoQoS {
@@ -114,6 +115,10 @@ impl VideoQoS {
self.quality
}
+ pub fn record(&self) -> bool {
+ self.users.iter().any(|u| u.1.record)
+ }
+
pub fn abr_enabled() -> bool {
"N" != Config::get_option("enable-abr")
}
@@ -388,6 +393,12 @@ impl VideoQoS {
}
}
+ pub fn user_record(&mut self, id: i32, v: bool) {
+ if let Some(user) = self.users.get_mut(&id) {
+ user.record = v;
+ }
+ }
+
pub fn on_connection_close(&mut self, id: i32) {
self.users.remove(&id);
self.refresh(None);
diff --git a/src/server/video_service.rs b/src/server/video_service.rs
index bb0697409..a74211fcd 100644
--- a/src/server/video_service.rs
+++ b/src/server/video_service.rs
@@ -36,7 +36,7 @@ use hbb_common::{
use scrap::Capturer;
use scrap::{
aom::AomEncoderConfig,
- codec::{Encoder, EncoderCfg, HwEncoderConfig},
+ codec::{Encoder, EncoderCfg, HwEncoderConfig, Quality},
record::{Recorder, RecorderContext},
vpxcodec::{VpxEncoderConfig, VpxVideoCodecId},
CodecName, Display, TraitCapturer,
@@ -518,37 +518,13 @@ fn run(sp: GenericService) -> ResultType<()> {
let mut spf;
let mut quality = video_qos.quality();
let abr = VideoQoS::abr_enabled();
- drop(video_qos);
log::info!("init quality={:?}, abr enabled:{}", quality, abr);
-
- let encoder_cfg = match Encoder::negotiated_codec() {
- scrap::CodecName::H264(name) | scrap::CodecName::H265(name) => {
- EncoderCfg::HW(HwEncoderConfig {
- name,
- width: c.width,
- height: c.height,
- quality,
- })
- }
- name @ (scrap::CodecName::VP8 | scrap::CodecName::VP9) => {
- EncoderCfg::VPX(VpxEncoderConfig {
- width: c.width as _,
- height: c.height as _,
- timebase: [1, 1000], // Output timestamp precision
- quality,
- codec: if name == scrap::CodecName::VP8 {
- VpxVideoCodecId::VP8
- } else {
- VpxVideoCodecId::VP9
- },
- })
- }
- scrap::CodecName::AV1 => EncoderCfg::AOM(AomEncoderConfig {
- width: c.width as _,
- height: c.height as _,
- quality,
- }),
- };
+ let codec_name = Encoder::negotiated_codec();
+ let recorder = get_recorder(c.width, c.height, &codec_name);
+ let last_recording =
+ (recorder.lock().unwrap().is_some() || video_qos.record()) && codec_name != CodecName::AV1;
+ drop(video_qos);
+ let encoder_cfg = get_encoder_config(&c, quality, last_recording);
let mut encoder;
match Encoder::new(encoder_cfg) {
@@ -597,8 +573,6 @@ fn run(sp: GenericService) -> ResultType<()> {
let mut try_gdi = 1;
#[cfg(windows)]
log::info!("gdi: {}", c.is_gdi());
- let codec_name = Encoder::negotiated_codec();
- let recorder = get_recorder(c.width, c.height, &codec_name);
#[cfg(windows)]
start_uac_elevation_check();
@@ -617,6 +591,11 @@ fn run(sp: GenericService) -> ResultType<()> {
allow_err!(encoder.set_quality(quality));
video_qos.store_bitrate(encoder.bitrate());
}
+ let recording = (recorder.lock().unwrap().is_some() || video_qos.record())
+ && codec_name != CodecName::AV1;
+ if recording != last_recording {
+ bail!("SWITCH");
+ }
drop(video_qos);
if *SWITCH.lock().unwrap() {
@@ -789,6 +768,41 @@ fn run(sp: GenericService) -> ResultType<()> {
Ok(())
}
+fn get_encoder_config(c: &CapturerInfo, quality: Quality, recording: bool) -> EncoderCfg {
+ // https://www.wowza.com/community/t/the-correct-keyframe-interval-in-obs-studio/95162
+ let keyframe_interval = if recording { Some(240) } else { None };
+ match Encoder::negotiated_codec() {
+ scrap::CodecName::H264(name) | scrap::CodecName::H265(name) => {
+ EncoderCfg::HW(HwEncoderConfig {
+ name,
+ width: c.width,
+ height: c.height,
+ quality,
+ keyframe_interval,
+ })
+ }
+ name @ (scrap::CodecName::VP8 | scrap::CodecName::VP9) => {
+ EncoderCfg::VPX(VpxEncoderConfig {
+ width: c.width as _,
+ height: c.height as _,
+ quality,
+ codec: if name == scrap::CodecName::VP8 {
+ VpxVideoCodecId::VP8
+ } else {
+ VpxVideoCodecId::VP9
+ },
+ keyframe_interval,
+ })
+ }
+ scrap::CodecName::AV1 => EncoderCfg::AOM(AomEncoderConfig {
+ width: c.width as _,
+ height: c.height as _,
+ quality,
+ keyframe_interval,
+ }),
+ }
+}
+
fn get_recorder(
width: usize,
height: usize,
diff --git a/src/ui/header.tis b/src/ui/header.tis
index 666150fb3..2adc37027 100644
--- a/src/ui/header.tis
+++ b/src/ui/header.tis
@@ -297,6 +297,7 @@ class Header: Reactor.Component {
event click $(span#recording) (_, me) {
recording = !recording;
header.update();
+ handler.record_status(recording);
if (recording)
handler.refresh_video();
else
diff --git a/src/ui/index.tis b/src/ui/index.tis
index f0bfc13d2..c6286e903 100644
--- a/src/ui/index.tis
+++ b/src/ui/index.tis
@@ -215,7 +215,7 @@ class Enhancements: Reactor.Component {
return {translate('Enhancements')}
;
diff --git a/src/ui/remote.rs b/src/ui/remote.rs
index 6826c7e54..4ec0d5a5c 100644
--- a/src/ui/remote.rs
+++ b/src/ui/remote.rs
@@ -451,6 +451,7 @@ impl sciter::EventHandler for SciterSession {
fn save_custom_image_quality(i32);
fn refresh_video();
fn record_screen(bool, i32, i32);
+ fn record_status(bool);
fn get_toggle_option(String);
fn is_privacy_mode_supported();
fn toggle_option(String);
diff --git a/src/ui_interface.rs b/src/ui_interface.rs
index 2979f4ceb..dbd673880 100644
--- a/src/ui_interface.rs
+++ b/src/ui_interface.rs
@@ -201,6 +201,13 @@ pub fn get_peer_option(id: String, name: String) -> String {
c.options.get(&name).unwrap_or(&"".to_owned()).to_owned()
}
+#[inline]
+#[cfg(feature = "flutter")]
+pub fn get_peer_flutter_config(id: String, name: String) -> String {
+ let c = PeerConfig::load(&id);
+ c.ui_flutter.get(&name).unwrap_or(&"".to_owned()).to_owned()
+}
+
#[inline]
pub fn set_peer_option(id: String, name: String, value: String) {
let mut c = PeerConfig::load(&id);
@@ -912,7 +919,7 @@ fn check_connect_status(reconnect: bool) -> mpsc::UnboundedSender {
#[cfg(feature = "flutter")]
pub fn account_auth(op: String, id: String, uuid: String, remember_me: bool) {
- account::OidcSession::account_auth(op, id, uuid, remember_me);
+ account::OidcSession::account_auth(get_api_server(), op, id, uuid, remember_me);
}
#[cfg(feature = "flutter")]
diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs
index 704b91cf0..7ffcaac6d 100644
--- a/src/ui_session_interface.rs
+++ b/src/ui_session_interface.rs
@@ -202,7 +202,7 @@ impl Session {
}
pub fn get_flutter_config(&self, k: String) -> String {
- self.lc.write().unwrap().get_ui_flutter(&k)
+ self.lc.read().unwrap().get_ui_flutter(&k)
}
pub fn toggle_option(&mut self, name: String) {
@@ -240,6 +240,14 @@ impl Session {
self.send(Data::RecordScreen(start, w, h, self.id.clone()));
}
+ pub fn record_status(&self, status: bool) {
+ let mut misc = Misc::new();
+ misc.set_client_record_status(status);
+ let mut msg = Message::new();
+ msg.set_misc(misc);
+ self.send(Data::Message(msg));
+ }
+
pub fn save_custom_image_quality(&mut self, custom_image_quality: i32) {
let msg = self
.lc