diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 73334baae..59e3bf891 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -52,7 +52,6 @@ enum DesktopType { fileTransfer, cm, portForward, - rdp, } class IconFont { @@ -196,7 +195,7 @@ class MyTheme { ); static changeTo(bool dark) { - if (Get.isDarkMode != dark) { + if (isDarkTheme() != dark) { Get.find().setString("darkTheme", dark ? "Y" : ""); Get.changeThemeMode(dark ? ThemeMode.dark : ThemeMode.light); if (desktopType == DesktopType.main) { @@ -211,7 +210,7 @@ class MyTheme { bool dark; // Brightnesss is always light on windows, Flutter 3.0.5 if (_themeInitialed || !mainPage || Platform.isWindows) { - dark = "Y" == Get.find().getString("darkTheme"); + dark = isDarkTheme(); } else { dark = WidgetsBinding.instance.platformDispatcher.platformBrightness == Brightness.dark; @@ -231,7 +230,7 @@ class MyTheme { } bool isDarkTheme() { - return Get.isDarkMode; + return "Y" == Get.find().getString("darkTheme"); } final ButtonStyle flatButtonStyle = TextButton.styleFrom( @@ -572,9 +571,7 @@ void msgBox( submit() { dialogManager.dismissAll(); // https://github.com/fufesou/rustdesk/blob/5e9a31340b899822090a3731769ae79c6bf5f3e5/src/ui/common.tis#L263 - if (!type.contains("custom") && - !(desktopType == DesktopType.portForward || - desktopType == DesktopType.rdp)) { + if (!type.contains("custom") && desktopType != DesktopType.portForward) { closeConnection(); } } diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index e48c85b0d..70fc9f065 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -5,7 +5,6 @@ const String kAppTypeMain = "main"; const String kAppTypeDesktopRemote = "remote"; const String kAppTypeDesktopFileTransfer = "file transfer"; const String kAppTypeDesktopPortForward = "port forward"; -const String kAppTypeDesktopRDP = "rdp"; const String kTabLabelHomePage = "Home"; const String kTabLabelSettingPage = "Settings"; diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart index b113d8a56..64a74d22a 100644 --- a/flutter/lib/desktop/pages/connection_page.dart +++ b/flutter/lib/desktop/pages/connection_page.dart @@ -428,8 +428,12 @@ class _ConnectionPageState extends State { light, Text(translate("Service is not running")), TextButton( - onPressed: () => - bind.mainSetOption(key: "stop-service", value: ""), + onPressed: () async { + bool checked = await bind.mainCheckSuperUserPermission(); + if (checked) { + bind.mainSetOption(key: "stop-service", value: ""); + } + }, child: Text(translate("Start Service"))) ], ); @@ -1019,6 +1023,7 @@ class _PeerTabbedPageState extends State<_PeerTabbedPage> return ListView( scrollDirection: Axis.horizontal, shrinkWrap: true, + controller: ScrollController(), children: super.widget.tabs.asMap().entries.map((t) { return Obx(() => GestureDetector( child: Container( diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart index 3cda8aa60..47f1cc026 100644 --- a/flutter/lib/desktop/pages/desktop_home_page.dart +++ b/flutter/lib/desktop/pages/desktop_home_page.dart @@ -525,7 +525,7 @@ class _DesktopHomePageState extends State final option = await bind.mainGetOption(key: key); bind.mainSetOption(key: key, value: option == "Y" ? "" : "Y"); } else if (key == "change-id") { - changeId(); + changeIdDialog(); } else if (key == "custom-server") { changeServer(); } else if (key == "whitelist") { @@ -648,85 +648,6 @@ class _DesktopHomePageState extends State ); } - /// change local ID - void changeId() { - var newId = ""; - var msg = ""; - var isInProgress = false; - TextEditingController controller = TextEditingController(); - gFFI.dialogManager.show((setState, close) { - submit() async { - newId = controller.text.trim(); - setState(() { - msg = ""; - isInProgress = true; - bind.mainChangeId(newId: newId); - }); - - var status = await bind.mainGetAsyncStatus(); - while (status == " ") { - await Future.delayed(const Duration(milliseconds: 100)); - status = await bind.mainGetAsyncStatus(); - } - if (status.isEmpty) { - // ok - close(); - return; - } - setState(() { - isInProgress = false; - msg = translate(status); - }); - } - - return CustomAlertDialog( - title: Text(translate("Change ID")), - content: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(translate("id_change_tip")), - const SizedBox( - height: 8.0, - ), - Row( - children: [ - const Text("ID:").marginOnly(bottom: 16.0), - const SizedBox( - width: 24.0, - ), - Expanded( - child: TextField( - decoration: InputDecoration( - border: const OutlineInputBorder(), - errorText: msg.isEmpty ? null : translate(msg)), - inputFormatters: [ - LengthLimitingTextInputFormatter(16), - // FilteringTextInputFormatter(RegExp(r"[a-zA-z][a-zA-z0-9\_]*"), allow: true) - ], - maxLength: 16, - controller: controller, - focusNode: FocusNode()..requestFocus(), - ), - ), - ], - ), - const SizedBox( - height: 4.0, - ), - Offstage( - offstage: !isInProgress, child: const LinearProgressIndicator()) - ], - ), - actions: [ - TextButton(onPressed: close, child: Text(translate("Cancel"))), - TextButton(onPressed: submit, child: Text(translate("OK"))), - ], - onSubmit: submit, - onCancel: close, - ); - }); - } - void about() async { final appName = await bind.mainGetAppName(); final license = await bind.mainGetLicense(); diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 823a32fb6..9aae9dc29 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -8,7 +9,6 @@ import 'package:flutter_hbb/models/platform_model.dart'; import 'package:flutter_hbb/models/server_model.dart'; import 'package:get/get.dart'; import 'package:provider/provider.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:url_launcher/url_launcher_string.dart'; const double _kTabWidth = 235; @@ -32,7 +32,7 @@ class _TabInfo { } class DesktopSettingPage extends StatefulWidget { - DesktopSettingPage({Key? key}) : super(key: key); + const DesktopSettingPage({Key? key}) : super(key: key); @override State createState() => _DesktopSettingPageState(); @@ -40,19 +40,18 @@ class DesktopSettingPage extends StatefulWidget { class _DesktopSettingPageState extends State with TickerProviderStateMixin, AutomaticKeepAliveClientMixin { - final List<_TabInfo> _setting_tabs = <_TabInfo>[ - _TabInfo('User Interface', Icons.language_outlined, Icons.language_sharp), + final List<_TabInfo> settingTabs = <_TabInfo>[ + _TabInfo('General', Icons.settings_outlined, Icons.settings), + _TabInfo('Language', Icons.language_outlined, Icons.language), _TabInfo('Security', Icons.enhanced_encryption_outlined, - Icons.enhanced_encryption_sharp), - _TabInfo( - 'Display', Icons.desktop_windows_outlined, Icons.desktop_windows_sharp), - _TabInfo('Audio', Icons.volume_up_outlined, Icons.volume_up_sharp), - _TabInfo('Connection', Icons.link_outlined, Icons.link_sharp), - _TabInfo('About', Icons.info_outline, Icons.info_sharp) + Icons.enhanced_encryption), + _TabInfo('Network', Icons.link_outlined, Icons.link), + _TabInfo('Acount', Icons.person_outline, Icons.person), + _TabInfo('About', Icons.info_outline, Icons.info) ]; late PageController controller; - RxInt _selectedIndex = 0.obs; + RxInt selectedIndex = 0.obs; @override bool get wantKeepAlive => true; @@ -70,12 +69,12 @@ class _DesktopSettingPageState extends State backgroundColor: MyTheme.color(context).bg, body: Row( children: [ - Container( + SizedBox( width: _kTabWidth, child: Column( children: [ _header(), - Flexible(child: _listView(tabs: _setting_tabs)), + Flexible(child: _listView(tabs: settingTabs)), ], ), ), @@ -85,12 +84,12 @@ class _DesktopSettingPageState extends State color: MyTheme.color(context).grayBg, child: PageView( controller: controller, - children: [ - _UserInterface(), + children: const [ + _General(), + _Language(), _Safety(), - _Display(), - _Audio(), - _Connection(), + _Network(), + _Acount(), _About(), ], ), @@ -109,20 +108,21 @@ class _DesktopSettingPageState extends State child: Text( translate('Settings'), textAlign: TextAlign.left, - style: TextStyle( + style: const TextStyle( color: _accentColor, fontSize: _kTitleFontSize, fontWeight: FontWeight.w400, ), ), ).marginOnly(left: 20, top: 10), - Spacer(), + const Spacer(), ], ); } Widget _listView({required List<_TabInfo> tabs}) { return ListView( + controller: ScrollController(), children: tabs .asMap() .entries @@ -133,16 +133,16 @@ class _DesktopSettingPageState extends State Widget _listItem({required _TabInfo tab, required int index}) { return Obx(() { - bool selected = index == _selectedIndex.value; - return Container( + bool selected = index == selectedIndex.value; + return SizedBox( width: _kTabWidth, height: _kTabHeight, child: InkWell( onTap: () { - if (_selectedIndex.value != index) { + if (selectedIndex.value != index) { controller.jumpToPage(index); } - _selectedIndex.value = index; + selectedIndex.value = index; }, child: Row(children: [ Container( @@ -171,14 +171,129 @@ class _DesktopSettingPageState extends State //#region pages -class _UserInterface extends StatefulWidget { - _UserInterface({Key? key}) : super(key: key); +class _General extends StatefulWidget { + const _General({Key? key}) : super(key: key); @override - State<_UserInterface> createState() => _UserInterfaceState(); + State<_General> createState() => _GeneralState(); } -class _UserInterfaceState extends State<_UserInterface> +class _GeneralState extends State<_General> { + @override + Widget build(BuildContext context) { + return ListView( + controller: ScrollController(), + children: [ + theme(), + abr(), + hwcodec(), + audio(context), + ], + ).marginOnly(bottom: _kListViewBottomMargin); + } + + Widget theme() { + change() { + MyTheme.changeTo(!isDarkTheme()); + setState(() {}); + } + + return _Card(title: 'Theme', children: [ + GestureDetector( + onTap: change, + child: Row( + children: [ + Checkbox(value: isDarkTheme(), onChanged: (_) => change()) + .marginOnly(right: 5), + Expanded(child: Text(translate('Dark Theme'))), + ], + ).marginOnly(left: _kCheckBoxLeftMargin), + ) + ]); + } + + Widget abr() { + return _Card(title: 'Adaptive Bitrate', children: [ + _OptionCheckBox(context, 'Adaptive Bitrate', 'enable-abr'), + ]); + } + + Widget hwcodec() { + return _futureBuilder( + future: bind.mainHasHwcodec(), + hasData: (data) { + return Offstage( + offstage: !(data as bool), + child: _Card(title: 'Hardware Codec', children: [ + _OptionCheckBox( + context, 'Enable hardware codec', 'enable-hwcodec'), + ]), + ); + }); + } + + Widget audio(BuildContext context) { + String getDefault() { + if (Platform.isWindows) return "System Sound"; + return ""; + } + + Future getValue() async { + String device = await bind.mainGetOption(key: 'audio-input'); + if (device.isNotEmpty) { + return device; + } else { + return getDefault(); + } + } + + setDevice(String device) { + if (device == getDefault()) device = ""; + bind.mainSetOption(key: 'audio-input', value: device); + } + + return _futureBuilder(future: () async { + List devices = (await bind.mainGetSoundInputs()).toList(); + if (Platform.isWindows) { + devices.insert(0, 'System Sound'); + } + String current = await getValue(); + return {'devices': devices, 'current': current}; + }(), hasData: (data) { + String currentDevice = data['current']; + List devices = data['devices'] as List; + if (devices.isEmpty) { + return const Offstage(); + } + List keys = devices.toList(); + List values = devices.toList(); + // TODO + if (!devices.contains(currentDevice)) { + currentDevice = ""; + keys.insert(0, currentDevice); + values.insert(0, 'default'); + } + return _Card(title: 'Audio Input Device', children: [ + _ComboBox( + keys: keys, + values: values, + initialKey: currentDevice, + onChanged: (key) { + setDevice(key); + }).marginOnly(left: _kContentHMargin), + ]); + }); + } +} + +class _Language extends StatefulWidget { + const _Language({Key? key}) : super(key: key); + + @override + State<_Language> createState() => _LanguageState(); +} + +class _LanguageState extends State<_Language> with AutomaticKeepAliveClientMixin { @override bool get wantKeepAlive => true; @@ -187,9 +302,9 @@ class _UserInterfaceState extends State<_UserInterface> Widget build(BuildContext context) { super.build(context); return ListView( + controller: ScrollController(), children: [ _Card(title: 'Language', children: [language()]), - _Card(title: 'Theme', children: [theme()]), ], ).marginOnly(bottom: _kListViewBottomMargin); } @@ -223,22 +338,6 @@ class _UserInterfaceState extends State<_UserInterface> ).marginOnly(left: _kContentHMargin); }); } - - Widget theme() { - change() { - MyTheme.changeTo(!isDarkTheme()); - } - - return GestureDetector( - onTap: change, - child: Row( - children: [ - Checkbox(value: isDarkTheme(), onChanged: (_) => change()), - Expanded(child: Text(translate('Dark Theme'))), - ], - ).marginOnly(left: _kCheckBoxLeftMargin), - ); - } } class _Safety extends StatefulWidget { @@ -257,6 +356,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin { Widget build(BuildContext context) { super.build(context); return ListView( + controller: ScrollController(), children: [ Column( children: [ @@ -269,7 +369,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin { child: Column(children: [ permissions(context), password(context), - whitelist(), + connection(context), ]), ), ], @@ -379,73 +479,32 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin { }))); } - Widget whitelist() { - return _Card(title: 'IP Whitelisting', children: [ - _Button('IP Whitelisting', changeWhiteList, - tip: 'whitelist_tip', enabled: !locked) + Widget connection(BuildContext context) { + bool enabled = !locked; + return _Card(title: 'Connection', children: [ + _OptionCheckBox(context, 'Deny remote access', 'stop-service', + checkedIcon: const Icon( + Icons.warning, + color: Colors.yellowAccent, + ), + enabled: enabled), + _OptionCheckBox(context, 'Enable TCP Tunneling', 'enable-tunnel', + enabled: enabled), + Offstage( + offstage: !Platform.isWindows, + child: _OptionCheckBox(context, 'Enable RDP', 'enable-rdp', + enabled: enabled), + ), + ...directIp(context), + whitelist(), ]); } -} -class _Connection extends StatefulWidget { - const _Connection({Key? key}) : super(key: key); - - @override - State<_Connection> createState() => _ConnectionState(); -} - -class _ConnectionState extends State<_Connection> - with AutomaticKeepAliveClientMixin { - @override - bool get wantKeepAlive => true; - bool locked = true; - - @override - Widget build(BuildContext context) { - super.build(context); - bool enabled = !locked; - return ListView(children: [ - Column( - children: [ - _lock(locked, 'Unlock Connection Settings', () { - locked = false; - setState(() => {}); - }), - AbsorbPointer( - absorbing: locked, - child: Column(children: [ - _Card(title: 'Server', children: [ - _Button('ID/Relay Server', changeServer, enabled: enabled), - ]), - _Card(title: 'Service', children: [ - _OptionCheckBox(context, 'Enable Service', 'stop-service', - reverse: true, enabled: enabled), - // TODO: Not implemented - // _option_check('Always connected via relay', 'allow-always-relay', enabled: enabled), - // _option_check('Start ID/relay service', 'stop-rendezvous-service', - // reverse: true, enabled: enabled), - ]), - _Card(title: 'TCP Tunneling', children: [ - _OptionCheckBox( - context, 'Enable TCP Tunneling', 'enable-tunnel', - enabled: enabled), - ]), - direct_ip(context), - _Card(title: 'Proxy', children: [ - _Button('Socks5 Proxy', changeSocks5Proxy, enabled: enabled), - ]), - ]), - ), - ], - ) - ]).marginOnly(bottom: _kListViewBottomMargin); - } - - Widget direct_ip(BuildContext context) { + List directIp(BuildContext context) { TextEditingController controller = TextEditingController(); - var update = () => setState(() {}); - RxBool apply_enabled = false.obs; - return _Card(title: 'Direct IP Access', children: [ + update() => setState(() {}); + RxBool applyEnabled = false.obs; + return [ _OptionCheckBox(context, 'Enable Direct IP Access', 'direct-server', update: update, enabled: !locked), _futureBuilder( @@ -457,194 +516,179 @@ class _ConnectionState extends State<_Connection> hasData: (data) { bool enabled = option2bool('direct-server', data['enabled'].toString()); - if (!enabled) apply_enabled.value = false; + if (!enabled) applyEnabled.value = false; controller.text = data['port'].toString(); - return Row(children: [ - _SubLabeledWidget( - 'Port', - Container( - width: 80, - child: TextField( - controller: controller, - enabled: enabled && !locked, - onChanged: (_) => apply_enabled.value = true, - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp( - '\^([0-9]|[1-9]\\d|[1-9]\\d{2}|[1-9]\\d{3}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])\$')), - ], - textAlign: TextAlign.end, - decoration: InputDecoration( - hintText: '21118', - border: InputBorder.none, - contentPadding: EdgeInsets.only(right: 5), - isCollapsed: true, + return Offstage( + offstage: !enabled, + child: Row(children: [ + _SubLabeledWidget( + 'Port', + SizedBox( + width: 80, + child: TextField( + controller: controller, + enabled: enabled && !locked, + onChanged: (_) => applyEnabled.value = true, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp( + r'^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$')), + ], + textAlign: TextAlign.end, + decoration: const InputDecoration( + hintText: '21118', + border: InputBorder.none, + contentPadding: EdgeInsets.only(right: 5), + isCollapsed: true, + ), ), ), - ), - enabled: enabled && !locked, - ).marginOnly(left: 5), - Obx(() => ElevatedButton( - onPressed: apply_enabled.value && enabled && !locked - ? () async { - apply_enabled.value = false; - await bind.mainSetOption( - key: 'direct-access-port', - value: controller.text); - } - : null, - child: Text( - translate('Apply'), - ), - ).marginOnly(left: 20)) - ]); + enabled: enabled && !locked, + ).marginOnly(left: 5), + Obx(() => ElevatedButton( + onPressed: applyEnabled.value && enabled && !locked + ? () async { + applyEnabled.value = false; + await bind.mainSetOption( + key: 'direct-access-port', + value: controller.text); + } + : null, + child: Text( + translate('Apply'), + ), + ).marginOnly(left: 20)) + ]), + ); }, ), - ]); + ]; + } + + Widget whitelist() { + bool enabled = !locked; + return _futureBuilder(future: () async { + return await bind.mainGetOption(key: 'whitelist'); + }(), hasData: (data) { + RxBool hasWhitelist = (data as String).isNotEmpty.obs; + update() async { + hasWhitelist.value = + (await bind.mainGetOption(key: 'whitelist')).isNotEmpty; + } + + onChanged(bool? checked) async { + changeWhiteList(callback: update); + } + + return GestureDetector( + child: Tooltip( + message: translate('whitelist_tip'), + child: Obx(() => Row( + children: [ + Checkbox( + value: hasWhitelist.value, + onChanged: enabled ? onChanged : null) + .marginOnly(right: 5), + Offstage( + offstage: !hasWhitelist.value, + child: const Icon(Icons.warning, color: Colors.yellowAccent) + .marginOnly(right: 5), + ), + Expanded( + child: Text( + translate('Use IP Whitelisting'), + style: + TextStyle(color: _disabledTextColor(context, enabled)), + )) + ], + )), + ), + onTap: () { + onChanged(!hasWhitelist.value); + }, + ).marginOnly(left: _kCheckBoxLeftMargin); + }); } } -class _Display extends StatefulWidget { - const _Display({Key? key}) : super(key: key); +class _Network extends StatefulWidget { + const _Network({Key? key}) : super(key: key); @override - State<_Display> createState() => _DisplayState(); + State<_Network> createState() => _NetworkState(); } -class _DisplayState extends State<_Display> with AutomaticKeepAliveClientMixin { +class _NetworkState extends State<_Network> with AutomaticKeepAliveClientMixin { @override bool get wantKeepAlive => true; + bool locked = true; @override Widget build(BuildContext context) { super.build(context); + bool enabled = !locked; + return ListView(controller: ScrollController(), children: [ + Column( + children: [ + _lock(locked, 'Unlock Network Settings', () { + locked = false; + setState(() => {}); + }), + AbsorbPointer( + absorbing: locked, + child: Column(children: [ + _Card(title: 'Server', children: [ + _Button('ID/Relay Server', changeServer, enabled: enabled), + ]), + _Card(title: 'Proxy', children: [ + _Button('Socks5 Proxy', changeSocks5Proxy, enabled: enabled), + ]), + ]), + ), + ], + ) + ]).marginOnly(bottom: _kListViewBottomMargin); + } +} + +class _Acount extends StatefulWidget { + const _Acount({Key? key}) : super(key: key); + + @override + State<_Acount> createState() => _AcountState(); +} + +class _AcountState extends State<_Acount> { + @override + Widget build(BuildContext context) { return ListView( + controller: ScrollController(), children: [ - _Card(title: 'Adaptive Bitrate', children: [ - _OptionCheckBox(context, 'Adaptive Bitrate', 'enable-abr'), - ]), - hwcodec(), + _Card(title: 'Acount', children: [login()]), + _Card(title: 'ID', children: [changeId()]), ], ).marginOnly(bottom: _kListViewBottomMargin); } - Widget hwcodec() { - return _futureBuilder( - future: bind.mainHasHwcodec(), - hasData: (data) { - return Offstage( - offstage: !(data as bool), - child: _Card(title: 'Hardware Codec', children: [ - _OptionCheckBox( - context, 'Enable hardware codec', 'enable-hwcodec'), - ]), - ); - }); + Widget login() { + return _futureBuilder(future: () async { + return await gFFI.userModel.getUserName(); + }(), hasData: (data) { + String username = data as String; + return _Button( + username.isEmpty ? 'Login' : 'Logout', + () => { + loginDialog().then((success) { + if (success) { + // refresh frame + setState(() {}); + } + }) + }); + }); } -} -class _Audio extends StatefulWidget { - const _Audio({Key? key}) : super(key: key); - - @override - State<_Audio> createState() => _AudioState(); -} - -enum _AudioInputType { - Mute, - Standard, - Specify, -} - -class _AudioState extends State<_Audio> with AutomaticKeepAliveClientMixin { - @override - bool get wantKeepAlive => true; - - @override - Widget build(BuildContext context) { - super.build(context); - var update = () => setState(() {}); - var set_enabled = (bool enabled) => bind.mainSetOption( - key: 'enable-audio', value: bool2option('enable-audio', enabled)); - var set_device = (String device) => - bind.mainSetOption(key: 'audio-input', value: device); - return ListView(children: [ - _Card( - title: 'Audio Input', - children: [ - _futureBuilder(future: () async { - List devices = await bind.mainGetSoundInputs(); - String current = await bind.mainGetOption(key: 'audio-input'); - String enabled = await bind.mainGetOption(key: 'enable-audio'); - return {'devices': devices, 'current': current, 'enabled': enabled}; - }(), hasData: (data) { - bool mute = - !option2bool('enable-audio', data['enabled'].toString()); - String currentDevice = data['current']; - List devices = (data['devices'] as List).toList(); - _AudioInputType groupValue; - if (mute) { - groupValue = _AudioInputType.Mute; - } else if (devices.contains(currentDevice)) { - groupValue = _AudioInputType.Specify; - } else { - groupValue = _AudioInputType.Standard; - } - List deviceWidget = [].toList(); - if (devices.isNotEmpty) { - var combo = _ComboBox( - keys: devices, - values: devices, - initialKey: devices.contains(currentDevice) - ? currentDevice - : devices[0], - onChanged: (key) { - set_device(key); - }, - enabled: groupValue == _AudioInputType.Specify, - ); - deviceWidget.addAll([ - _Radio<_AudioInputType>( - context, - value: _AudioInputType.Specify, - groupValue: groupValue, - label: 'Specify device', - onChanged: (value) { - set_device(combo.current); - set_enabled(true); - update(); - }, - ), - combo.marginOnly(left: _kContentHSubMargin, top: 5), - ]); - } - return Column(children: [ - _Radio<_AudioInputType>( - context, - value: _AudioInputType.Mute, - groupValue: groupValue, - label: 'Mute', - onChanged: (value) { - set_enabled(false); - update(); - }, - ), - _Radio( - context, - value: _AudioInputType.Standard, - groupValue: groupValue, - label: 'Use standard device', - onChanged: (value) { - set_device(''); - set_enabled(true); - update(); - }, - ), - ...deviceWidget, - ]); - }), - ], - ) - ]).marginOnly(bottom: _kListViewBottomMargin); + Widget changeId() { + return _Button('Change ID', changeIdDialog); } } @@ -665,13 +709,13 @@ class _AboutState extends State<_About> { }(), hasData: (data) { final license = data['license'].toString(); final version = data['version'].toString(); - final linkStyle = TextStyle(decoration: TextDecoration.underline); - return ListView(children: [ + const linkStyle = TextStyle(decoration: TextDecoration.underline); + return ListView(controller: ScrollController(), children: [ _Card(title: "About RustDesk", children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox( + const SizedBox( height: 8.0, ), Text("Version: $version").marginSymmetric(vertical: 4.0), @@ -679,7 +723,7 @@ class _AboutState extends State<_About> { onTap: () { launchUrlString("https://rustdesk.com/privacy"); }, - child: Text( + child: const Text( "Privacy Statement", style: linkStyle, ).marginSymmetric(vertical: 4.0)), @@ -687,13 +731,14 @@ class _AboutState extends State<_About> { onTap: () { launchUrlString("https://rustdesk.com"); }, - child: Text( + child: const Text( "Website", style: linkStyle, ).marginSymmetric(vertical: 4.0)), Container( - decoration: BoxDecoration(color: Color(0xFF2c8cff)), - padding: EdgeInsets.symmetric(vertical: 24, horizontal: 8), + decoration: const BoxDecoration(color: Color(0xFF2c8cff)), + padding: + const EdgeInsets.symmetric(vertical: 24, horizontal: 8), child: Row( children: [ Expanded( @@ -702,9 +747,9 @@ class _AboutState extends State<_About> { children: [ Text( "Copyright © 2022 Purslane Ltd.\n$license", - style: TextStyle(color: Colors.white), + style: const TextStyle(color: Colors.white), ), - Text( + const Text( "Made with heart in this chaotic world!", style: TextStyle( fontWeight: FontWeight.w800, @@ -728,10 +773,11 @@ class _AboutState extends State<_About> { //#region components +// ignore: non_constant_identifier_names Widget _Card({required String title, required List children}) { return Row( children: [ - Container( + SizedBox( width: _kCardFixedWidth, child: Card( child: Column( @@ -741,11 +787,11 @@ Widget _Card({required String title, required List children}) { Text( translate(title), textAlign: TextAlign.start, - style: TextStyle( + style: const TextStyle( fontSize: _kTitleFontSize, ), ), - Spacer(), + const Spacer(), ], ).marginOnly(left: _kContentHMargin, top: 10, bottom: 10), ...children @@ -762,15 +808,19 @@ Color? _disabledTextColor(BuildContext context, bool enabled) { return enabled ? null : MyTheme.color(context).lighterText; } +// ignore: non_constant_identifier_names Widget _OptionCheckBox(BuildContext context, String label, String key, - {Function()? update = null, bool reverse = false, bool enabled = true}) { + {Function()? update, + bool reverse = false, + bool enabled = true, + Icon? checkedIcon}) { return _futureBuilder( future: bind.mainGetOption(key: key), hasData: (data) { bool value = option2bool(key, data.toString()); if (reverse) value = !value; var ref = value.obs; - var onChanged = (option) async { + onChanged(option) async { if (option != null) { ref.value = option; if (reverse) option = !option; @@ -778,14 +828,19 @@ Widget _OptionCheckBox(BuildContext context, String label, String key, bind.mainSetOption(key: key, value: value); update?.call(); } - }; + } + return GestureDetector( child: Obx( () => Row( children: [ Checkbox( value: ref.value, onChanged: enabled ? onChanged : null) - .marginOnly(right: 10), + .marginOnly(right: 5), + Offstage( + offstage: !ref.value || checkedIcon == null, + child: checkedIcon?.marginOnly(right: 5), + ), Expanded( child: Text( translate(label), @@ -801,13 +856,14 @@ Widget _OptionCheckBox(BuildContext context, String label, String key, }); } +// ignore: non_constant_identifier_names Widget _Radio(BuildContext context, {required T value, required T groupValue, required String label, required Function(T value) onChanged, bool enabled = true}) { - var on_change = enabled + var onChange = enabled ? (T? value) { if (value != null) { onChanged(value); @@ -817,7 +873,7 @@ Widget _Radio(BuildContext context, return GestureDetector( child: Row( children: [ - Radio(value: value, groupValue: groupValue, onChanged: on_change), + Radio(value: value, groupValue: groupValue, onChanged: onChange), Expanded( child: Text(translate(label), style: TextStyle( @@ -827,10 +883,11 @@ Widget _Radio(BuildContext context, ), ], ).marginOnly(left: _kRadioLeftMargin), - onTap: () => on_change?.call(value), + onTap: () => onChange?.call(value), ); } +// ignore: non_constant_identifier_names Widget _Button(String label, Function() onPressed, {bool enabled = true, String? tip}) { var button = ElevatedButton( @@ -840,7 +897,7 @@ Widget _Button(String label, Function() onPressed, translate(label), ).marginSymmetric(horizontal: 15), )); - var child; + StatefulWidget child; if (tip == null) { child = button; } else { @@ -851,6 +908,7 @@ Widget _Button(String label, Function() onPressed, ]).marginOnly(left: _kContentHMargin); } +// ignore: non_constant_identifier_names Widget _SubButton(String label, Function() onPressed, [bool enabled = true]) { return Row( children: [ @@ -865,6 +923,7 @@ Widget _SubButton(String label, Function() onPressed, [bool enabled = true]) { ).marginOnly(left: _kContentHSubMargin); } +// ignore: non_constant_identifier_names Widget _SubLabeledWidget(String label, Widget child, {bool enabled = true}) { RxBool hover = false.obs; return Row( @@ -879,23 +938,23 @@ Widget _SubLabeledWidget(String label, Widget child, {bool enabled = true}) { decoration: BoxDecoration( border: Border.all( color: hover.value && enabled - ? Color(0xFFD7D7D7) - : Color(0xFFCBCBCB), + ? const Color(0xFFD7D7D7) + : const Color(0xFFCBCBCB), width: hover.value && enabled ? 2 : 1)), child: Row( children: [ Container( height: 28, color: (hover.value && enabled) - ? Color(0xFFD7D7D7) - : Color(0xFFCBCBCB), - child: Text( - label + ': ', - style: TextStyle(fontWeight: FontWeight.w300), - ), + ? const Color(0xFFD7D7D7) + : const Color(0xFFCBCBCB), alignment: Alignment.center, - padding: - EdgeInsets.symmetric(horizontal: 5, vertical: 2), + padding: const EdgeInsets.symmetric( + horizontal: 5, vertical: 2), + child: Text( + '${translate(label)}: ', + style: const TextStyle(fontWeight: FontWeight.w300), + ), ).paddingAll(2), child, ], @@ -915,7 +974,7 @@ Widget _futureBuilder( return hasData(snapshot.data!); } else { if (snapshot.hasError) { - print(snapshot.error.toString()); + debugPrint(snapshot.error.toString()); } return Container(); } @@ -931,16 +990,16 @@ Widget _lock( offstage: !locked, child: Row( children: [ - Container( + SizedBox( width: _kCardFixedWidth, child: Card( child: ElevatedButton( - child: Container( + child: SizedBox( height: 25, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon( + const Icon( Icons.security_sharp, size: 20, ), @@ -996,7 +1055,7 @@ class _ComboBox extends StatelessWidget { underline: Container( height: 25, ), - icon: Icon( + icon: const Icon( Icons.expand_more_sharp, size: 20, ), @@ -1014,7 +1073,7 @@ class _ComboBox extends StatelessWidget { value: value, child: Text( value, - style: TextStyle(fontSize: _kContentFontSize), + style: const TextStyle(fontSize: _kContentFontSize), overflow: TextOverflow.ellipsis, ).marginOnly(left: 5), ); @@ -1047,9 +1106,9 @@ void changeServer() async { gFFI.dialogManager.show((setState, close) { submit() async { setState(() { - [idServerMsg, relayServerMsg, apiServerMsg].forEach((element) { - element = ""; - }); + idServerMsg = ""; + relayServerMsg = ""; + apiServerMsg = ""; isInProgress = true; }); cancel() { @@ -1224,7 +1283,7 @@ void changeServer() async { }); } -void changeWhiteList() async { +void changeWhiteList({Function()? callback}) async { Map oldOptions = jsonDecode(await bind.mainGetOptions()); var newWhiteList = ((oldOptions['whitelist'] ?? "") as String).split(','); var newWhiteListField = newWhiteList.join('\n'); @@ -1263,11 +1322,14 @@ void changeWhiteList() async { ], ), actions: [ + TextButton(onPressed: close, child: Text(translate("Cancel"))), TextButton( - onPressed: () { + onPressed: () async { + await bind.mainSetOption(key: 'whitelist', value: ''); + callback?.call(); close(); }, - child: Text(translate("Cancel"))), + child: Text(translate("Clear"))), TextButton( onPressed: () async { setState(() { @@ -1296,6 +1358,7 @@ void changeWhiteList() async { } oldOptions['whitelist'] = newWhiteList; await bind.mainSetOptions(json: jsonEncode(oldOptions)); + callback?.call(); close(); }, child: Text(translate("OK"))), @@ -1444,4 +1507,82 @@ void changeSocks5Proxy() async { }); } +void changeIdDialog() { + var newId = ""; + var msg = ""; + var isInProgress = false; + TextEditingController controller = TextEditingController(); + gFFI.dialogManager.show((setState, close) { + submit() async { + newId = controller.text.trim(); + setState(() { + msg = ""; + isInProgress = true; + bind.mainChangeId(newId: newId); + }); + + var status = await bind.mainGetAsyncStatus(); + while (status == " ") { + await Future.delayed(const Duration(milliseconds: 100)); + status = await bind.mainGetAsyncStatus(); + } + if (status.isEmpty) { + // ok + close(); + return; + } + setState(() { + isInProgress = false; + msg = translate(status); + }); + } + + return CustomAlertDialog( + title: Text(translate("Change ID")), + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(translate("id_change_tip")), + const SizedBox( + height: 8.0, + ), + Row( + children: [ + const Text("ID:").marginOnly(bottom: 16.0), + const SizedBox( + width: 24.0, + ), + Expanded( + child: TextField( + decoration: InputDecoration( + border: const OutlineInputBorder(), + errorText: msg.isEmpty ? null : translate(msg)), + inputFormatters: [ + LengthLimitingTextInputFormatter(16), + // FilteringTextInputFormatter(RegExp(r"[a-zA-z][a-zA-z0-9\_]*"), allow: true) + ], + maxLength: 16, + controller: controller, + focusNode: FocusNode()..requestFocus(), + ), + ), + ], + ), + const SizedBox( + height: 4.0, + ), + Offstage( + offstage: !isInProgress, child: const LinearProgressIndicator()) + ], + ), + actions: [ + TextButton(onPressed: close, child: Text(translate("Cancel"))), + TextButton(onPressed: submit, child: Text(translate("OK"))), + ], + onSubmit: submit, + onCancel: close, + ); + }); +} + //#endregion diff --git a/flutter/lib/desktop/pages/file_manager_page.dart b/flutter/lib/desktop/pages/file_manager_page.dart index bd6e4cb63..eee3c226b 100644 --- a/flutter/lib/desktop/pages/file_manager_page.dart +++ b/flutter/lib/desktop/pages/file_manager_page.dart @@ -184,6 +184,7 @@ class _FileManagerPageState extends State children: [ Expanded( child: SingleChildScrollView( + controller: ScrollController(), child: ObxValue( (searchText) { final filteredEntries = searchText.isEmpty @@ -309,6 +310,7 @@ class _FileManagerPageState extends State // Center(child: listTail(isLocal: isLocal)), // Expanded( // child: ListView.builder( + // controller: ScrollController(), // itemCount: entries.length + 1, // itemBuilder: (context, index) { // if (index >= entries.length) { @@ -424,6 +426,7 @@ class _FileManagerPageState extends State decoration: BoxDecoration(border: Border.all(color: Colors.grey)), child: Obx( () => ListView.builder( + controller: ScrollController(), itemBuilder: (BuildContext context, int index) { final item = model.jobTable[index]; return Column( diff --git a/flutter/lib/desktop/pages/port_forward_page.dart b/flutter/lib/desktop/pages/port_forward_page.dart index 6cfd0cdb2..f3e988744 100644 --- a/flutter/lib/desktop/pages/port_forward_page.dart +++ b/flutter/lib/desktop/pages/port_forward_page.dart @@ -37,7 +37,6 @@ class PortForwardPage extends StatefulWidget { class _PortForwardPageState extends State with AutomaticKeepAliveClientMixin { - final bool isRdp = false; final TextEditingController localPortController = TextEditingController(); final TextEditingController remoteHostController = TextEditingController(); final TextEditingController remotePortController = TextEditingController(); @@ -53,7 +52,7 @@ class _PortForwardPageState extends State if (!Platform.isLinux) { Wakelock.enable(); } - print("init success with id ${widget.id}"); + debugPrint("init success with id ${widget.id}"); } @override @@ -73,7 +72,7 @@ class _PortForwardPageState extends State return Scaffold( backgroundColor: MyTheme.color(context).grayBg, body: FutureBuilder(future: () async { - if (!isRdp) { + if (!widget.isRDP) { refreshTunnelConfig(); } }(), builder: (context, snapshot) { @@ -134,6 +133,7 @@ class _PortForwardPageState extends State data: Theme.of(context) .copyWith(backgroundColor: MyTheme.color(context).bg), child: Obx(() => ListView.builder( + controller: ScrollController(), itemCount: pfs.length + 2, itemBuilder: ((context, index) { if (index == 0) { @@ -283,17 +283,18 @@ class _PortForwardPageState extends State } buildRdp(BuildContext context) { - text1(String lable) => - Expanded(child: Text(lable).marginOnly(left: _kTextLeftMargin)); + text1(String lable) => Expanded( + child: Text(translate(lable)).marginOnly(left: _kTextLeftMargin)); text2(String lable) => Expanded( child: Text( lable, - style: TextStyle(fontSize: 20), + style: const TextStyle(fontSize: 20), ).marginOnly(left: _kTextLeftMargin)); return Theme( data: Theme.of(context) .copyWith(backgroundColor: MyTheme.color(context).bg), child: ListView.builder( + controller: ScrollController(), itemCount: 2, itemBuilder: ((context, index) { if (index == 0) { @@ -321,10 +322,10 @@ class _PortForwardPageState extends State style: ElevatedButton.styleFrom( elevation: 0, side: const BorderSide(color: MyTheme.border)), - onPressed: () {}, + onPressed: () => bind.sessionNewRdp(id: widget.id), child: Text( translate('New RDP'), - style: TextStyle( + style: const TextStyle( fontWeight: FontWeight.w300, fontSize: 14), ), ).marginSymmetric(vertical: 10), diff --git a/flutter/lib/desktop/pages/port_forward_tab_page.dart b/flutter/lib/desktop/pages/port_forward_tab_page.dart index 5dd69e8eb..d078af458 100644 --- a/flutter/lib/desktop/pages/port_forward_tab_page.dart +++ b/flutter/lib/desktop/pages/port_forward_tab_page.dart @@ -26,8 +26,8 @@ class _PortForwardTabPageState extends State { _PortForwardTabPageState(Map params) { isRDP = params['isRDP']; - tabController = Get.put(DesktopTabController( - tabType: isRDP ? DesktopTabType.rdp : DesktopTabType.portForward)); + tabController = + Get.put(DesktopTabController(tabType: DesktopTabType.portForward)); tabController.add(TabInfo( key: params['id'], label: params['id'], @@ -55,6 +55,11 @@ class _PortForwardTabPageState extends State { final id = args['id']; final isRDP = args['isRDP']; window_on_top(windowId()); + if (tabController.state.value.tabs.indexWhere((e) => e.key == id) >= + 0) { + debugPrint("port forward $id exists"); + return; + } tabController.add(TabInfo( key: id, label: id, diff --git a/flutter/lib/desktop/pages/server_page.dart b/flutter/lib/desktop/pages/server_page.dart index be14a8537..b49bfdc27 100644 --- a/flutter/lib/desktop/pages/server_page.dart +++ b/flutter/lib/desktop/pages/server_page.dart @@ -60,20 +60,27 @@ class _DesktopServerPageState extends State ], child: Consumer( builder: (context, serverModel, child) => Container( - decoration: BoxDecoration( - border: Border.all(color: MyTheme.color(context).border!)), - child: Scaffold( - backgroundColor: MyTheme.color(context).bg, - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Expanded(child: ConnectionManager()), - SizedBox.fromSize(size: Size(0, 15.0)), - ], - ), - ), - )))); + decoration: BoxDecoration( + border: + Border.all(color: MyTheme.color(context).border!)), + child: Overlay(initialEntries: [ + OverlayEntry(builder: (context) { + gFFI.dialogManager.setOverlayState(Overlay.of(context)); + return Scaffold( + backgroundColor: MyTheme.color(context).bg, + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded(child: ConnectionManager()), + SizedBox.fromSize(size: Size(0, 15.0)), + ], + ), + ), + ); + }) + ]), + ))); } @override diff --git a/flutter/lib/desktop/widgets/material_mod_popup_menu.dart b/flutter/lib/desktop/widgets/material_mod_popup_menu.dart index 8b0acba9a..1345f72f1 100644 --- a/flutter/lib/desktop/widgets/material_mod_popup_menu.dart +++ b/flutter/lib/desktop/widgets/material_mod_popup_menu.dart @@ -614,6 +614,7 @@ class _PopupMenu extends StatelessWidget { padding: const EdgeInsets.symmetric( vertical: _kMenuVerticalPadding, ), + controller: ScrollController(), child: ListBody(children: children), ), ), diff --git a/flutter/lib/desktop/widgets/peer_widget.dart b/flutter/lib/desktop/widgets/peer_widget.dart index 02b5b9f00..32976fb5b 100644 --- a/flutter/lib/desktop/widgets/peer_widget.dart +++ b/flutter/lib/desktop/widgets/peer_widget.dart @@ -85,6 +85,7 @@ class _PeerWidgetState extends State<_PeerWidget> with WindowListener { child: Text(translate("Empty")), ) : SingleChildScrollView( + controller: ScrollController(), child: ObxValue((searchText) { return FutureBuilder>( builder: (context, snapshot) { diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index fef5fbf1f..fb7989108 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -44,7 +44,6 @@ enum DesktopTabType { remoteScreen, fileTransfer, portForward, - rdp, } class DesktopTabState { diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index 0ae5b583d..98ac20bfe 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -51,8 +51,7 @@ Future main(List args) async { runFileTransferScreen(argument); break; case WindowType.PortForward: - desktopType = - argument['isRDP'] ? DesktopType.rdp : DesktopType.portForward; + desktopType = DesktopType.portForward; runPortForwardScreen(argument); break; default: diff --git a/flutter/lib/mobile/pages/connection_page.dart b/flutter/lib/mobile/pages/connection_page.dart index ba34b31e8..7549bbae7 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 { Widget build(BuildContext context) { Provider.of(context); return SingleChildScrollView( + controller: ScrollController(), child: Column( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, diff --git a/flutter/lib/mobile/pages/file_manager_page.dart b/flutter/lib/mobile/pages/file_manager_page.dart index 87169b987..dd1cbb83f 100644 --- a/flutter/lib/mobile/pages/file_manager_page.dart +++ b/flutter/lib/mobile/pages/file_manager_page.dart @@ -203,6 +203,7 @@ class _FileManagerPageState extends State { headTools(), Expanded( child: ListView.builder( + controller: ScrollController(), itemCount: entries.length + 1, itemBuilder: (context, index) { if (index >= entries.length) { diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index dd3742d32..a9a03c04d 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -759,6 +759,7 @@ class _RemotePageState extends State { expand: false, builder: (context, scrollController) { return SingleChildScrollView( + controller: ScrollController(), padding: EdgeInsets.symmetric(vertical: 10), child: GestureHelp( touchMode: gFFI.ffiModel.touchMode, diff --git a/libs/hbb_common/src/lib.rs b/libs/hbb_common/src/lib.rs index 48fbfe23c..50fcc07b2 100644 --- a/libs/hbb_common/src/lib.rs +++ b/libs/hbb_common/src/lib.rs @@ -228,6 +228,14 @@ pub fn get_uuid() -> Vec { Config::get_key_pair().1 } +#[inline] +pub fn get_time() -> i64 { + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map(|d| d.as_millis()) + .unwrap_or(0) as _ +} + #[cfg(test)] mod tests { use super::*; diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index 7431bc952..ee81627d8 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -4,10 +4,11 @@ use crate::{ }; use hbb_common::{ anyhow::{anyhow, Context}, + bytes::Bytes, config::HwCodecConfig, - lazy_static, log, + get_time, lazy_static, log, message_proto::{EncodedVideoFrame, EncodedVideoFrames, Message, VideoFrame}, - ResultType, bytes::Bytes, + ResultType, }; use hwcodec::{ decode::{DecodeContext, DecodeFrame, Decoder}, @@ -105,6 +106,7 @@ impl EncoderApi for HwEncoder { DataFormat::H264 => vf.set_h264s(frames), DataFormat::H265 => vf.set_h265s(frames), } + vf.timestamp = get_time(); msg_out.set_video_frame(vf); Ok(msg_out) } else { diff --git a/libs/scrap/src/common/vpxcodec.rs b/libs/scrap/src/common/vpxcodec.rs index 0fda53fa3..47b3df3a6 100644 --- a/libs/scrap/src/common/vpxcodec.rs +++ b/libs/scrap/src/common/vpxcodec.rs @@ -4,15 +4,15 @@ use hbb_common::anyhow::{anyhow, Context}; use hbb_common::message_proto::{EncodedVideoFrame, EncodedVideoFrames, Message, VideoFrame}; -use hbb_common::ResultType; +use hbb_common::{ResultType, get_time}; use crate::codec::EncoderApi; use crate::STRIDE_ALIGN; use super::vpx::{vp8e_enc_control_id::*, vpx_codec_err_t::*, *}; +use hbb_common::bytes::Bytes; use std::os::raw::{c_int, c_uint}; use std::{ptr, slice}; -use hbb_common::bytes::Bytes; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum VpxVideoCodecId { @@ -285,6 +285,7 @@ impl VpxEncoder { frames: vp9s.into(), ..Default::default() }); + vf.timestamp = get_time(); msg_out.set_video_frame(vf); msg_out } diff --git a/src/common.rs b/src/common.rs index 68656853a..d5ccf0e1b 100644 --- a/src/common.rs +++ b/src/common.rs @@ -426,14 +426,6 @@ pub fn refresh_rendezvous_server() { }); } -#[inline] -pub fn get_time() -> i64 { - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .map(|d| d.as_millis()) - .unwrap_or(0) as _ -} - pub fn run_me>(args: Vec) -> std::io::Result { #[cfg(not(feature = "appimage"))] { @@ -676,4 +668,4 @@ lazy_static::lazy_static! { #[cfg(target_os = "linux")] lazy_static::lazy_static! { pub static ref IS_X11: Mutex = Mutex::new("x11" == hbb_common::platform::linux::get_display_server()); -} \ No newline at end of file +} diff --git a/src/flutter.rs b/src/flutter.rs index b22c0da83..eb66260c9 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -18,7 +18,6 @@ pub(super) const APP_TYPE_MAIN: &str = "main"; pub(super) const APP_TYPE_DESKTOP_REMOTE: &str = "remote"; pub(super) const APP_TYPE_DESKTOP_FILE_TRANSFER: &str = "file transfer"; pub(super) const APP_TYPE_DESKTOP_PORT_FORWARD: &str = "port forward"; -pub(super) const APP_TYPE_DESKTOP_RDP: &str = "rdp"; lazy_static::lazy_static! { pub static ref SESSIONS: RwLock>> = Default::default(); diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 10ab95487..f3c7b3735 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -663,7 +663,6 @@ fn main_broadcast_message(data: &HashMap<&str, &str>) { flutter::APP_TYPE_DESKTOP_REMOTE, flutter::APP_TYPE_DESKTOP_FILE_TRANSFER, flutter::APP_TYPE_DESKTOP_PORT_FORWARD, - flutter::APP_TYPE_DESKTOP_RDP, ]; for app in apps { @@ -703,6 +702,12 @@ pub fn session_remove_port_forward(id: String, local_port: i32) { } } +pub fn session_new_rdp(id: String) { + if let Some(session) = SESSIONS.write().unwrap().get_mut(&id) { + session.new_rdp(); + } +} + pub fn main_get_last_remote_id() -> String { // if !config::APP_DIR.read().unwrap().is_empty() { // res = LocalConfig::get_remote_id(); diff --git a/src/lang/cn.rs b/src/lang/cn.rs index ae55b9fab..abb772d51 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -322,5 +322,27 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Insecure Connection", "非安全连接"), ("Scale original", "原始尺寸"), ("Scale adaptive", "适应窗口"), + ("General", "常规"), + ("Security", "安全"), + ("Acount", "账户"), + ("Theme", "主题"), + ("Dark Theme", "暗黑主题"), + ("Enable hardware codec", "使用硬件编解码"), + ("Unlock Security Settings", "解锁安全设置"), + ("Enable Audio", "允许传输音频"), + ("Temporary Password Length", "临时密码长度"), + ("Unlock Network Settings", "解锁网络设置"), + ("Server", "服务器"), + ("Direct IP Access", "IP直接访问"), + ("Proxy", "代理"), + ("Port", "端口"), + ("Apply", "应用"), + ("Disconnect all devices?", "断开所有远程连接?"), + ("Clear", "清空"), + ("Audio Input Device", "音频输入设备"), + ("Deny remote access", "拒绝远程访问"), + ("Use IP Whitelisting", "只允许白名单上的IP访问"), + ("Network", "网络"), + ("Enable RDP", "允许RDP访问"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 4f49cb113..5061fbb88 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -322,5 +322,27 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Insecure Connection", "Nezabezpečené připojení"), ("Scale original", "Měřítko původní"), ("Scale adaptive", "Měřítko adaptivní"), + ("General", ""), + ("Security", ""), + ("Acount", ""), + ("Theme", ""), + ("Dark Theme", ""), + ("Enable hardware codec", ""), + ("Unlock Security Settings", ""), + ("Enable Audio", ""), + ("Temporary Password Length", ""), + ("Unlock Network Settings", ""), + ("Server", ""), + ("Direct IP Access", ""), + ("Proxy", ""), + ("Port", ""), + ("Apply", ""), + ("Disconnect all devices?", ""), + ("Clear", ""), + ("Audio Input Device", ""), + ("Deny remote access", ""), + ("Use IP Whitelisting", ""), + ("Network", ""), + ("Enable RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 78db47875..ed5e3425b 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -322,5 +322,27 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Insecure Connection", "Usikker forbindelse"), ("Scale original", "Skala original"), ("Scale adaptive", "Skala adaptiv"), + ("General", ""), + ("Security", ""), + ("Acount", ""), + ("Theme", ""), + ("Dark Theme", ""), + ("Enable hardware codec", ""), + ("Unlock Security Settings", ""), + ("Enable Audio", ""), + ("Temporary Password Length", ""), + ("Unlock Network Settings", ""), + ("Server", ""), + ("Direct IP Access", ""), + ("Proxy", ""), + ("Port", ""), + ("Apply", ""), + ("Disconnect all devices?", ""), + ("Clear", ""), + ("Audio Input Device", ""), + ("Deny remote access", ""), + ("Use IP Whitelisting", ""), + ("Network", ""), + ("Enable RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index 60caecdd4..43f0b2f8e 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -322,5 +322,27 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Insecure Connection", "Unsichere Verbindung"), ("Scale original", "Original skalieren"), ("Scale adaptive", "Adaptiv skalieren"), + ("General", ""), + ("Security", ""), + ("Acount", ""), + ("Theme", ""), + ("Dark Theme", ""), + ("Enable hardware codec", ""), + ("Unlock Security Settings", ""), + ("Enable Audio", ""), + ("Temporary Password Length", ""), + ("Unlock Network Settings", ""), + ("Server", ""), + ("Direct IP Access", ""), + ("Proxy", ""), + ("Port", ""), + ("Apply", ""), + ("Disconnect all devices?", ""), + ("Clear", ""), + ("Audio Input Device", ""), + ("Deny remote access", ""), + ("Use IP Whitelisting", ""), + ("Network", ""), + ("Enable RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 95fda5e90..d7f038b41 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -322,5 +322,27 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Insecure Connection", "Nesekura Konekto"), ("Scale original", "Skalo originalo"), ("Scale adaptive", "Skalo adapta"), + ("General", ""), + ("Security", ""), + ("Acount", ""), + ("Theme", ""), + ("Dark Theme", ""), + ("Enable hardware codec", ""), + ("Unlock Security Settings", ""), + ("Enable Audio", ""), + ("Temporary Password Length", ""), + ("Unlock Network Settings", ""), + ("Server", ""), + ("Direct IP Access", ""), + ("Proxy", ""), + ("Port", ""), + ("Apply", ""), + ("Disconnect all devices?", ""), + ("Clear", ""), + ("Audio Input Device", ""), + ("Deny remote access", ""), + ("Use IP Whitelisting", ""), + ("Network", ""), + ("Enable RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 17147a0cf..7c13e849c 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -335,5 +335,27 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Insecure Connection", "Conexión insegura"), ("Scale original", "escala originales"), ("Scale adaptive", "Adaptable a escala"), + ("General", ""), + ("Security", ""), + ("Acount", ""), + ("Theme", ""), + ("Dark Theme", ""), + ("Enable hardware codec", ""), + ("Unlock Security Settings", ""), + ("Enable Audio", ""), + ("Temporary Password Length", ""), + ("Unlock Network Settings", ""), + ("Server", ""), + ("Direct IP Access", ""), + ("Proxy", ""), + ("Port", ""), + ("Apply", ""), + ("Disconnect all devices?", ""), + ("Clear", ""), + ("Audio Input Device", ""), + ("Deny remote access", ""), + ("Use IP Whitelisting", ""), + ("Network", ""), + ("Enable RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 8df98de48..3e46a6b40 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -322,5 +322,27 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Insecure Connection", "Connexion non sécurisée"), ("Scale original", "Échelle d'origine"), ("Scale adaptive", "Échelle adaptative"), + ("General", ""), + ("Security", ""), + ("Acount", ""), + ("Theme", ""), + ("Dark Theme", ""), + ("Enable hardware codec", ""), + ("Unlock Security Settings", ""), + ("Enable Audio", ""), + ("Temporary Password Length", ""), + ("Unlock Network Settings", ""), + ("Server", ""), + ("Direct IP Access", ""), + ("Proxy", ""), + ("Port", ""), + ("Apply", ""), + ("Disconnect all devices?", ""), + ("Clear", ""), + ("Audio Input Device", ""), + ("Deny remote access", ""), + ("Use IP Whitelisting", ""), + ("Network", ""), + ("Enable RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 84d030eb0..78089075c 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -322,5 +322,27 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Insecure Connection", "Nem biztonságos kapcsolat"), ("Scale original", "Eredeti méretarány"), ("Scale adaptive", "Skála adaptív"), + ("General", ""), + ("Security", ""), + ("Acount", ""), + ("Theme", ""), + ("Dark Theme", ""), + ("Enable hardware codec", ""), + ("Unlock Security Settings", ""), + ("Enable Audio", ""), + ("Temporary Password Length", ""), + ("Unlock Network Settings", ""), + ("Server", ""), + ("Direct IP Access", ""), + ("Proxy", ""), + ("Port", ""), + ("Apply", ""), + ("Disconnect all devices?", ""), + ("Clear", ""), + ("Audio Input Device", ""), + ("Deny remote access", ""), + ("Use IP Whitelisting", ""), + ("Network", ""), + ("Enable RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index a80dd6c22..a9cc27767 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -335,5 +335,27 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Insecure Connection", "Koneksi Tidak Aman"), ("Scale original", "Skala asli"), ("Scale adaptive", "Skala adaptif"), + ("General", ""), + ("Security", ""), + ("Acount", ""), + ("Theme", ""), + ("Dark Theme", ""), + ("Enable hardware codec", ""), + ("Unlock Security Settings", ""), + ("Enable Audio", ""), + ("Temporary Password Length", ""), + ("Unlock Network Settings", ""), + ("Server", ""), + ("Direct IP Access", ""), + ("Proxy", ""), + ("Port", ""), + ("Apply", ""), + ("Disconnect all devices?", ""), + ("Clear", ""), + ("Audio Input Device", ""), + ("Deny remote access", ""), + ("Use IP Whitelisting", ""), + ("Network", ""), + ("Enable RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 8bf455d8e..8749e2976 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -321,5 +321,27 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Legacy mode", ""), ("Map mode", ""), ("Translate mode", ""), + ("General", ""), + ("Security", ""), + ("Acount", ""), + ("Theme", ""), + ("Dark Theme", ""), + ("Enable hardware codec", ""), + ("Unlock Security Settings", ""), + ("Enable Audio", ""), + ("Temporary Password Length", ""), + ("Unlock Network Settings", ""), + ("Server", ""), + ("Direct IP Access", ""), + ("Proxy", ""), + ("Port", ""), + ("Apply", ""), + ("Disconnect all devices?", ""), + ("Clear", ""), + ("Audio Input Device", ""), + ("Deny remote access", ""), + ("Use IP Whitelisting", ""), + ("Network", ""), + ("Enable RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 0c9b6c54d..30181b7a5 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -319,5 +319,27 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Insecure Connection", "安全でない接続"), ("Scale original", "オリジナルサイズ"), ("Scale adaptive", "フィットウィンドウ"), + ("General", ""), + ("Security", ""), + ("Acount", ""), + ("Theme", ""), + ("Dark Theme", ""), + ("Enable hardware codec", ""), + ("Unlock Security Settings", ""), + ("Enable Audio", ""), + ("Temporary Password Length", ""), + ("Unlock Network Settings", ""), + ("Server", ""), + ("Direct IP Access", ""), + ("Proxy", ""), + ("Port", ""), + ("Apply", ""), + ("Disconnect all devices?", ""), + ("Clear", ""), + ("Audio Input Device", ""), + ("Deny remote access", ""), + ("Use IP Whitelisting", ""), + ("Network", ""), + ("Enable RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 44ba589f6..7b8377206 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -316,5 +316,27 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Insecure Connection", "안전하지 않은 연결"), ("Scale original", "원래 크기"), ("Scale adaptive", "맞는 창"), + ("General", ""), + ("Security", ""), + ("Acount", ""), + ("Theme", ""), + ("Dark Theme", ""), + ("Enable hardware codec", ""), + ("Unlock Security Settings", ""), + ("Enable Audio", ""), + ("Temporary Password Length", ""), + ("Unlock Network Settings", ""), + ("Server", ""), + ("Direct IP Access", ""), + ("Proxy", ""), + ("Port", ""), + ("Apply", ""), + ("Disconnect all devices?", ""), + ("Clear", ""), + ("Audio Input Device", ""), + ("Deny remote access", ""), + ("Use IP Whitelisting", ""), + ("Network", ""), + ("Enable RDP", ""), ].iter().cloned().collect(); -} \ No newline at end of file +} diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 42bd49bbb..1088cfa72 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -320,5 +320,27 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Insecure Connection", "Niepewne połączenie"), ("Scale original", "Skala oryginalna"), ("Scale adaptive", "Skala adaptacyjna"), + ("General", ""), + ("Security", ""), + ("Acount", ""), + ("Theme", ""), + ("Dark Theme", ""), + ("Enable hardware codec", ""), + ("Unlock Security Settings", ""), + ("Enable Audio", ""), + ("Temporary Password Length", ""), + ("Unlock Network Settings", ""), + ("Server", ""), + ("Direct IP Access", ""), + ("Proxy", ""), + ("Port", ""), + ("Apply", ""), + ("Disconnect all devices?", ""), + ("Clear", ""), + ("Audio Input Device", ""), + ("Deny remote access", ""), + ("Use IP Whitelisting", ""), + ("Network", ""), + ("Enable RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index e8c62d78a..cb6fcf548 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -316,5 +316,27 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Insecure Connection", "Conexão insegura"), ("Scale original", "Escala original"), ("Scale adaptive", "Escala adaptável"), + ("General", ""), + ("Security", ""), + ("Acount", ""), + ("Theme", ""), + ("Dark Theme", ""), + ("Enable hardware codec", ""), + ("Unlock Security Settings", ""), + ("Enable Audio", ""), + ("Temporary Password Length", ""), + ("Unlock Network Settings", ""), + ("Server", ""), + ("Direct IP Access", ""), + ("Proxy", ""), + ("Port", ""), + ("Apply", ""), + ("Disconnect all devices?", ""), + ("Clear", ""), + ("Audio Input Device", ""), + ("Deny remote access", ""), + ("Use IP Whitelisting", ""), + ("Network", ""), + ("Enable RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index b9d3cad70..cba544380 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -322,5 +322,27 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Insecure Connection", ""), ("Scale original", ""), ("Scale adaptive", ""), + ("General", ""), + ("Security", ""), + ("Acount", ""), + ("Theme", ""), + ("Dark Theme", ""), + ("Enable hardware codec", ""), + ("Unlock Security Settings", ""), + ("Enable Audio", ""), + ("Temporary Password Length", ""), + ("Unlock Network Settings", ""), + ("Server", ""), + ("Direct IP Access", ""), + ("Proxy", ""), + ("Port", ""), + ("Apply", ""), + ("Disconnect all devices?", ""), + ("Clear", ""), + ("Audio Input Device", ""), + ("Deny remote access", ""), + ("Use IP Whitelisting", ""), + ("Network", ""), + ("Enable RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 4a0c8413c..6fb7078ac 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -322,5 +322,27 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Insecure Connection", "Небезопасное соединение"), ("Scale original", "Оригинал масштаба"), ("Scale adaptive", "Масштаб адаптивный"), + ("General", ""), + ("Security", ""), + ("Acount", ""), + ("Theme", ""), + ("Dark Theme", ""), + ("Enable hardware codec", ""), + ("Unlock Security Settings", ""), + ("Enable Audio", ""), + ("Temporary Password Length", ""), + ("Unlock Network Settings", ""), + ("Server", ""), + ("Direct IP Access", ""), + ("Proxy", ""), + ("Port", ""), + ("Apply", ""), + ("Disconnect all devices?", ""), + ("Clear", ""), + ("Audio Input Device", ""), + ("Deny remote access", ""), + ("Use IP Whitelisting", ""), + ("Network", ""), + ("Enable RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 1d083d2cc..ebc9571af 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -322,5 +322,27 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Insecure Connection", "Nezabezpečené pripojenie"), ("Scale original", "Pôvodná mierka"), ("Scale adaptive", "Prispôsobivá mierka"), + ("General", ""), + ("Security", ""), + ("Acount", ""), + ("Theme", ""), + ("Dark Theme", ""), + ("Enable hardware codec", ""), + ("Unlock Security Settings", ""), + ("Enable Audio", ""), + ("Temporary Password Length", ""), + ("Unlock Network Settings", ""), + ("Server", ""), + ("Direct IP Access", ""), + ("Proxy", ""), + ("Port", ""), + ("Apply", ""), + ("Disconnect all devices?", ""), + ("Clear", ""), + ("Audio Input Device", ""), + ("Deny remote access", ""), + ("Use IP Whitelisting", ""), + ("Network", ""), + ("Enable RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index dcb2a9566..5d3061a05 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -322,5 +322,27 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Insecure Connection", ""), ("Scale original", ""), ("Scale adaptive", ""), + ("General", ""), + ("Security", ""), + ("Acount", ""), + ("Theme", ""), + ("Dark Theme", ""), + ("Enable hardware codec", ""), + ("Unlock Security Settings", ""), + ("Enable Audio", ""), + ("Temporary Password Length", ""), + ("Unlock Network Settings", ""), + ("Server", ""), + ("Direct IP Access", ""), + ("Proxy", ""), + ("Port", ""), + ("Apply", ""), + ("Disconnect all devices?", ""), + ("Clear", ""), + ("Audio Input Device", ""), + ("Deny remote access", ""), + ("Use IP Whitelisting", ""), + ("Network", ""), + ("Enable RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 01de89909..6cf6c1298 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -335,5 +335,27 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Insecure Connection", "Güvenli Bağlantı"), ("Scale original", "Orijinali ölçeklendir"), ("Scale adaptive", "Ölçek uyarlanabilir"), + ("General", ""), + ("Security", ""), + ("Acount", ""), + ("Theme", ""), + ("Dark Theme", ""), + ("Enable hardware codec", ""), + ("Unlock Security Settings", ""), + ("Enable Audio", ""), + ("Temporary Password Length", ""), + ("Unlock Network Settings", ""), + ("Server", ""), + ("Direct IP Access", ""), + ("Proxy", ""), + ("Port", ""), + ("Apply", ""), + ("Disconnect all devices?", ""), + ("Clear", ""), + ("Audio Input Device", ""), + ("Deny remote access", ""), + ("Use IP Whitelisting", ""), + ("Network", ""), + ("Enable RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 84002b163..f312c7f47 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -322,5 +322,27 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Insecure Connection", "非安全連接"), ("Scale original", "原始尺寸"), ("Scale adaptive", "適應窗口"), + ("General", "常規"), + ("Security", "安全"), + ("Acount", "賬戶"), + ("Theme", "主題"), + ("Dark Theme", "暗黑主題"), + ("Enable hardware codec", "使用硬件編解碼"), + ("Unlock Security Settings", "解鎖安全設置"), + ("Enable Audio", "允許傳輸音頻"), + ("Temporary Password Length", "临时密码长度"), + ("Unlock Network Settings", "臨時密碼長度"), + ("Server", "服務器"), + ("Direct IP Access", "IP直接訪問"), + ("Proxy", "代理"), + ("Port", "端口"), + ("Apply", "應用"), + ("Disconnect all devices?", "斷開所有遠程連接?"), + ("Clear", "清空"), + ("Audio Input Device", "音頻輸入設備"), + ("Deny remote access", "拒絕遠程訪問"), + ("Use IP Whitelisting", "只允許白名單上的IP訪問"), + ("Network", "網絡"), + ("Enable RDP", "允許RDP訪問"), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index a9bc04946..94beafb67 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -322,5 +322,27 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Insecure Connection", "Kết nối không an toàn"), ("Scale original", "Quy mô gốc"), ("Scale adaptive", "Quy mô thích ứng"), + ("General", ""), + ("Security", ""), + ("Acount", ""), + ("Theme", ""), + ("Dark Theme", ""), + ("Enable hardware codec", ""), + ("Unlock Security Settings", ""), + ("Enable Audio", ""), + ("Temporary Password Length", ""), + ("Unlock Network Settings", ""), + ("Server", ""), + ("Direct IP Access", ""), + ("Proxy", ""), + ("Port", ""), + ("Apply", ""), + ("Disconnect all devices?", ""), + ("Clear", ""), + ("Audio Input Device", ""), + ("Deny remote access", ""), + ("Use IP Whitelisting", ""), + ("Network", ""), + ("Enable RDP", ""), ].iter().cloned().collect(); } diff --git a/src/server/audio_service.rs b/src/server/audio_service.rs index addc06644..c7a720ded 100644 --- a/src/server/audio_service.rs +++ b/src/server/audio_service.rs @@ -13,6 +13,7 @@ // https://github.com/krruzic/pulsectl use super::*; +use hbb_common::get_time; use magnum_opus::{Application::*, Channels::*, Encoder}; use std::sync::atomic::{AtomicBool, Ordering}; @@ -348,7 +349,7 @@ fn send_f32(data: &[f32], encoder: &mut Encoder, sp: &GenericService) { let mut msg_out = Message::new(); msg_out.set_audio_frame(AudioFrame { data: data.into(), - timestamp: crate::common::get_time(), + timestamp: get_time(), ..Default::default() }); sp.send(msg_out); @@ -368,7 +369,7 @@ fn send_f32(data: &[f32], encoder: &mut Encoder, sp: &GenericService) { let mut msg_out = Message::new(); msg_out.set_audio_frame(AudioFrame { data: data.into(), - timestamp: crate::common::get_time(), + timestamp: get_time(), ..Default::default() }); sp.send(msg_out); diff --git a/src/server/connection.rs b/src/server/connection.rs index d93d6d775..161c058f8 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -12,7 +12,7 @@ use hbb_common::{ fs, fs::can_enable_overwrite_detection, futures::{SinkExt, StreamExt}, - get_version_number, + get_time, get_version_number, message_proto::{option_message::BoolOption, permission_info::Permission}, password_security as password, sleep, timeout, tokio::{ @@ -397,7 +397,7 @@ impl Connection { conn.on_close("Timeout", true).await; break; } - let time = crate::get_time(); + let time = get_time(); if time > 0 && conn.last_test_delay == 0 { conn.last_test_delay = time; let mut msg_out = Message::new(); @@ -921,17 +921,23 @@ impl Connection { self.file_transfer = Some((ft.dir, ft.show_hidden)); } Some(login_request::Union::PortForward(mut pf)) => { - if !Config::get_option("enable-tunnel").is_empty() { - self.send_login_error("No permission of IP tunneling").await; - sleep(1.).await; - return false; - } let mut is_rdp = false; if pf.host == "RDP" && pf.port == 0 { pf.host = "localhost".to_owned(); pf.port = 3389; is_rdp = true; } + if is_rdp && !Config::get_option("enable-rdp").is_empty() + || !is_rdp && !Config::get_option("enable-tunnel").is_empty() + { + if is_rdp { + self.send_login_error("No permission of RDP").await; + } else { + self.send_login_error("No permission of IP tunneling").await; + } + sleep(1.).await; + return false; + } if pf.host.is_empty() { pf.host = "localhost".to_owned(); } @@ -977,7 +983,7 @@ impl Connection { .get(&self.ip) .map(|x| x.clone()) .unwrap_or((0, 0, 0)); - let time = (crate::get_time() / 60_000) as i32; + let time = (get_time() / 60_000) as i32; if failure.2 > 30 { self.send_login_error("Too many wrong password attempts") .await; @@ -1016,7 +1022,7 @@ impl Connection { self.inner.send(msg_out.into()); } else { self.last_test_delay = 0; - let new_delay = (crate::get_time() - t.time) as u32; + let new_delay = (get_time() - t.time) as u32; video_service::VIDEO_QOS .lock() .unwrap() @@ -1032,9 +1038,9 @@ impl Connection { #[cfg(not(any(target_os = "android", target_os = "ios")))] if self.keyboard { if is_left_up(&me) { - CLICK_TIME.store(crate::get_time(), Ordering::SeqCst); + CLICK_TIME.store(get_time(), Ordering::SeqCst); } else { - MOUSE_MOVE_TIME.store(crate::get_time(), Ordering::SeqCst); + MOUSE_MOVE_TIME.store(get_time(), Ordering::SeqCst); } self.input_mouse(me, self.inner.id()); } @@ -1043,7 +1049,7 @@ impl Connection { #[cfg(not(any(target_os = "android", target_os = "ios")))] if self.keyboard { if is_enter(&me) { - CLICK_TIME.store(crate::get_time(), Ordering::SeqCst); + CLICK_TIME.store(get_time(), Ordering::SeqCst); } // handle all down as press // fix unexpected repeating key on remote linux, seems also fix abnormal alt/shift, which diff --git a/src/server/input_service.rs b/src/server/input_service.rs index d78441a18..f36f2c50e 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -3,7 +3,7 @@ use crate::common::IS_X11; #[cfg(target_os = "macos")] use dispatch::Queue; use enigo::{Enigo, Key, KeyboardControllable, MouseButton, MouseControllable}; -use hbb_common::{config::COMPRESS_LEVEL, protobuf::EnumOrUnknown}; +use hbb_common::{config::COMPRESS_LEVEL, get_time, protobuf::EnumOrUnknown}; use rdev::{simulate, EventType, Key as RdevKey}; use std::{ convert::TryFrom, @@ -111,7 +111,7 @@ fn run_pos(sp: GenericService, state: &mut StatePos) -> ResultType<()> { ..Default::default() }); let exclude = { - let now = crate::get_time(); + let now = get_time(); let lock = LATEST_INPUT.lock().unwrap(); if now - lock.time < 300 { lock.conn @@ -365,7 +365,7 @@ fn handle_mouse_(evt: &MouseEvent, conn: i32) { let buttons = evt.mask >> 3; let evt_type = evt.mask & 0x7; if evt_type == 0 { - let time = crate::get_time(); + let time = get_time(); *LATEST_INPUT.lock().unwrap() = Input { time, conn }; } let mut en = ENIGO.lock().unwrap(); diff --git a/src/server/video_service.rs b/src/server/video_service.rs index b22418398..eee9e4255 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -601,7 +601,7 @@ fn create_msg(vp9s: Vec) -> Message { frames: vp9s.into(), ..Default::default() }); - vf.timestamp = crate::common::get_time(); + vf.timestamp = hbb_common::get_time(); msg_out.set_video_frame(vf); msg_out } diff --git a/src/ui_interface.rs b/src/ui_interface.rs index c7b743ccb..0b95cd588 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -206,7 +206,7 @@ pub fn test_if_valid_server(host: String) -> String { pub fn get_sound_inputs() -> Vec { let mut a = Vec::new(); - #[cfg(windows)] + #[cfg(not(target_os = "linux"))] { // TODO TEST fn get_sound_inputs_() -> Vec {