From 302a43d68c7e786f02ba9c2b64b4a2b96ac91c0c Mon Sep 17 00:00:00 2001 From: 21pages <pages21@163.com> Date: Thu, 8 Sep 2022 21:40:43 +0800 Subject: [PATCH] update setting page, add option enable-rdp Signed-off-by: 21pages <pages21@163.com> --- flutter/lib/common.dart | 6 +- .../lib/desktop/pages/connection_page.dart | 8 +- .../lib/desktop/pages/desktop_home_page.dart | 81 +- .../desktop/pages/desktop_setting_page.dart | 760 +++++++++++------- .../lib/desktop/pages/port_forward_page.dart | 4 +- flutter/lib/desktop/pages/server_page.dart | 35 +- src/lang/cn.rs | 22 + src/lang/cs.rs | 22 + src/lang/da.rs | 22 + src/lang/de.rs | 22 + src/lang/eo.rs | 22 + src/lang/es.rs | 22 + src/lang/fr.rs | 22 + src/lang/hu.rs | 22 + src/lang/id.rs | 22 + src/lang/it.rs | 22 + src/lang/ja.rs | 22 + src/lang/ko.rs | 24 +- src/lang/pl.rs | 22 + src/lang/pt_PT.rs | 22 + src/lang/ptbr.rs | 22 + src/lang/ru.rs | 22 + src/lang/sk.rs | 22 + src/lang/template.rs | 22 + src/lang/tr.rs | 22 + src/lang/tw.rs | 22 + src/lang/vn.rs | 22 + src/server/connection.rs | 16 +- src/ui_interface.rs | 2 +- 29 files changed, 956 insertions(+), 420 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 53b23c5cf..59e3bf891 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -195,7 +195,7 @@ class MyTheme { ); static changeTo(bool dark) { - if (Get.isDarkMode != dark) { + if (isDarkTheme() != dark) { Get.find<SharedPreferences>().setString("darkTheme", dark ? "Y" : ""); Get.changeThemeMode(dark ? ThemeMode.dark : ThemeMode.light); if (desktopType == DesktopType.main) { @@ -210,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<SharedPreferences>().getString("darkTheme"); + dark = isDarkTheme(); } else { dark = WidgetsBinding.instance.platformDispatcher.platformBrightness == Brightness.dark; @@ -230,7 +230,7 @@ class MyTheme { } bool isDarkTheme() { - return Get.isDarkMode; + return "Y" == Get.find<SharedPreferences>().getString("darkTheme"); } final ButtonStyle flatButtonStyle = TextButton.styleFrom( diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart index b113d8a56..a39877b64 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<ConnectionPage> { 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"))) ], ); 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<DesktopHomePage> 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<DesktopHomePage> ); } - /// 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..87d12c9c3 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<DesktopSettingPage> createState() => _DesktopSettingPageState(); @@ -40,19 +40,18 @@ class DesktopSettingPage extends StatefulWidget { class _DesktopSettingPageState extends State<DesktopSettingPage> 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<DesktopSettingPage> backgroundColor: MyTheme.color(context).bg, body: Row( children: <Widget>[ - 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<DesktopSettingPage> color: MyTheme.color(context).grayBg, child: PageView( controller: controller, - children: [ - _UserInterface(), + children: const [ + _General(), + _Language(), _Safety(), - _Display(), - _Audio(), - _Connection(), + _Network(), + _Acount(), _About(), ], ), @@ -109,14 +108,14 @@ class _DesktopSettingPageState extends State<DesktopSettingPage> 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(), ], ); } @@ -133,16 +132,16 @@ class _DesktopSettingPageState extends State<DesktopSettingPage> 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 +170,128 @@ class _DesktopSettingPageState extends State<DesktopSettingPage> //#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( + 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<String> 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<String> 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<String> devices = data['devices'] as List<String>; + if (devices.isEmpty) { + return const Offstage(); + } + List<String> keys = devices.toList(); + List<String> 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; @@ -189,7 +302,6 @@ class _UserInterfaceState extends State<_UserInterface> return ListView( children: [ _Card(title: 'Language', children: [language()]), - _Card(title: 'Theme', children: [theme()]), ], ).marginOnly(bottom: _kListViewBottomMargin); } @@ -223,22 +335,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 { @@ -269,7 +365,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin { child: Column(children: [ permissions(context), password(context), - whitelist(), + connection(context), ]), ), ], @@ -379,73 +475,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<Widget> 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 +512,178 @@ 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(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( 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<String> 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<String> devices = (data['devices'] as List<String>).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 +704,13 @@ class _AboutState extends State<_About> { }(), hasData: (data) { final license = data['license'].toString(); final version = data['version'].toString(); - final linkStyle = TextStyle(decoration: TextDecoration.underline); + const linkStyle = TextStyle(decoration: TextDecoration.underline); return ListView(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 +718,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 +726,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 +742,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 +768,11 @@ class _AboutState extends State<_About> { //#region components +// ignore: non_constant_identifier_names Widget _Card({required String title, required List<Widget> children}) { return Row( children: [ - Container( + SizedBox( width: _kCardFixedWidth, child: Card( child: Column( @@ -741,11 +782,11 @@ Widget _Card({required String title, required List<Widget> 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 +803,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 +823,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 +851,14 @@ Widget _OptionCheckBox(BuildContext context, String label, String key, }); } +// ignore: non_constant_identifier_names Widget _Radio<T>(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 +868,7 @@ Widget _Radio<T>(BuildContext context, return GestureDetector( child: Row( children: [ - Radio<T>(value: value, groupValue: groupValue, onChanged: on_change), + Radio<T>(value: value, groupValue: groupValue, onChanged: onChange), Expanded( child: Text(translate(label), style: TextStyle( @@ -827,10 +878,11 @@ Widget _Radio<T>(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 +892,7 @@ Widget _Button(String label, Function() onPressed, translate(label), ).marginSymmetric(horizontal: 15), )); - var child; + StatefulWidget child; if (tip == null) { child = button; } else { @@ -851,6 +903,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 +918,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 +933,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 +969,7 @@ Widget _futureBuilder( return hasData(snapshot.data!); } else { if (snapshot.hasError) { - print(snapshot.error.toString()); + debugPrint(snapshot.error.toString()); } return Container(); } @@ -931,16 +985,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 +1050,7 @@ class _ComboBox extends StatelessWidget { underline: Container( height: 25, ), - icon: Icon( + icon: const Icon( Icons.expand_more_sharp, size: 20, ), @@ -1014,7 +1068,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 +1101,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 +1278,7 @@ void changeServer() async { }); } -void changeWhiteList() async { +void changeWhiteList({Function()? callback}) async { Map<String, dynamic> oldOptions = jsonDecode(await bind.mainGetOptions()); var newWhiteList = ((oldOptions['whitelist'] ?? "") as String).split(','); var newWhiteListField = newWhiteList.join('\n'); @@ -1263,11 +1317,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 +1353,7 @@ void changeWhiteList() async { } oldOptions['whitelist'] = newWhiteList; await bind.mainSetOptions(json: jsonEncode(oldOptions)); + callback?.call(); close(); }, child: Text(translate("OK"))), @@ -1444,4 +1502,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/port_forward_page.dart b/flutter/lib/desktop/pages/port_forward_page.dart index 06ee7bd94..aa108c2f5 100644 --- a/flutter/lib/desktop/pages/port_forward_page.dart +++ b/flutter/lib/desktop/pages/port_forward_page.dart @@ -282,8 +282,8 @@ class _PortForwardPageState extends State<PortForwardPage> } 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, 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<DesktopServerPage> ], child: Consumer<ServerModel>( 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/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/connection.rs b/src/server/connection.rs index d93d6d775..42f4bc940 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -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(); } 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<String> { let mut a = Vec::new(); - #[cfg(windows)] + #[cfg(not(target_os = "linux"))] { // TODO TEST fn get_sound_inputs_() -> Vec<String> {