mirror of
https://github.com/rustdesk/rustdesk.git
synced 2024-11-23 19:49:05 +08:00
Fix/custom client advanced settings (#8066)
* fix: custom client, advanced settings Signed-off-by: fufesou <shuanglongchen@yeah.net> * refact: custom client, default options Signed-off-by: fufesou <shuanglongchen@yeah.net> * fix: cargo test Signed-off-by: fufesou <shuanglongchen@yeah.net> * refact: remove prefix $ and unify option keys Signed-off-by: fufesou <shuanglongchen@yeah.net> * refact: custom client, advanced options Signed-off-by: fufesou <shuanglongchen@yeah.net> * debug custom client, advanced settings Signed-off-by: fufesou <shuanglongchen@yeah.net> * custom client, advanced settings. Add filter-transfer to display settings Signed-off-by: fufesou <shuanglongchen@yeah.net> * custom client, advanced settings Signed-off-by: fufesou <shuanglongchen@yeah.net> * fix: custom client, advanced settings, codec Signed-off-by: fufesou <shuanglongchen@yeah.net> * fix: custom client, advanced settings, whitelist Signed-off-by: fufesou <shuanglongchen@yeah.net> --------- Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
parent
3a4390e0c7
commit
8357d4675a
@ -550,7 +550,8 @@ class MyTheme {
|
||||
Get.changeThemeMode(mode);
|
||||
if (desktopType == DesktopType.main || isAndroid || isIOS) {
|
||||
if (mode == ThemeMode.system) {
|
||||
await bind.mainSetLocalOption(key: kCommConfKeyTheme, value: '');
|
||||
await bind.mainSetLocalOption(
|
||||
key: kCommConfKeyTheme, value: defaultOptionTheme);
|
||||
} else {
|
||||
await bind.mainSetLocalOption(
|
||||
key: kCommConfKeyTheme, value: mode.toShortString());
|
||||
@ -1421,13 +1422,13 @@ bool option2bool(String option, String value) {
|
||||
String bool2option(String option, bool b) {
|
||||
String res;
|
||||
if (option.startsWith('enable-')) {
|
||||
res = b ? '' : 'N';
|
||||
res = b ? defaultOptionYes : 'N';
|
||||
} else if (option.startsWith('allow-') ||
|
||||
option == "stop-service" ||
|
||||
option == "direct-server" ||
|
||||
option == "stop-rendezvous-service" ||
|
||||
option == kOptionForceAlwaysRelay) {
|
||||
res = b ? 'Y' : '';
|
||||
res = b ? 'Y' : defaultOptionNo;
|
||||
} else {
|
||||
assert(false);
|
||||
res = b ? 'Y' : 'N';
|
||||
@ -3281,3 +3282,14 @@ setResizable(bool resizable) {
|
||||
windowManager.setResizable(resizable);
|
||||
}
|
||||
}
|
||||
|
||||
isOptionFixed(String key) => bind.mainIsOptionFixed(key: key);
|
||||
|
||||
final isCustomClient = bind.isCustomClient();
|
||||
get defaultOptionLang => isCustomClient ? 'default' : '';
|
||||
get defaultOptionTheme => isCustomClient ? 'system' : '';
|
||||
get defaultOptionYes => isCustomClient ? 'Y' : '';
|
||||
get defaultOptionNo => isCustomClient ? 'N' : '';
|
||||
get defaultOptionWhitelist => isCustomClient ? ',' : '';
|
||||
get defaultOptionAccessMode => isCustomClient ? 'custom' : '';
|
||||
get defaultOptionApproveMode => isCustomClient ? 'password-click' : '';
|
||||
|
@ -333,6 +333,7 @@ class _AddressBookState extends State<AddressBook> {
|
||||
|
||||
@protected
|
||||
MenuEntryBase<String> syncMenuItem() {
|
||||
final isOptFixed = isOptionFixed(syncAbOption);
|
||||
return MenuEntrySwitch<String>(
|
||||
switchType: SwitchType.scheckbox,
|
||||
text: translate('Sync with recent sessions'),
|
||||
@ -343,11 +344,13 @@ class _AddressBookState extends State<AddressBook> {
|
||||
gFFI.abModel.setShouldAsync(v);
|
||||
},
|
||||
dismissOnClicked: true,
|
||||
enabled: (!isOptFixed).obs,
|
||||
);
|
||||
}
|
||||
|
||||
@protected
|
||||
MenuEntryBase<String> sortMenuItem() {
|
||||
final isOptFixed = isOptionFixed(sortAbTagsOption);
|
||||
return MenuEntrySwitch<String>(
|
||||
switchType: SwitchType.scheckbox,
|
||||
text: translate('Sort tags'),
|
||||
@ -355,15 +358,17 @@ class _AddressBookState extends State<AddressBook> {
|
||||
return shouldSortTags();
|
||||
},
|
||||
setter: (bool v) async {
|
||||
bind.mainSetLocalOption(key: sortAbTagsOption, value: v ? 'Y' : '');
|
||||
bind.mainSetLocalOption(key: sortAbTagsOption, value: v ? 'Y' : defaultOptionNo);
|
||||
gFFI.abModel.sortTags.value = v;
|
||||
},
|
||||
dismissOnClicked: true,
|
||||
enabled: (!isOptFixed).obs,
|
||||
);
|
||||
}
|
||||
|
||||
@protected
|
||||
MenuEntryBase<String> filterMenuItem() {
|
||||
final isOptFixed = isOptionFixed(filterAbTagOption);
|
||||
return MenuEntrySwitch<String>(
|
||||
switchType: SwitchType.scheckbox,
|
||||
text: translate('Filter by intersection'),
|
||||
@ -371,10 +376,11 @@ class _AddressBookState extends State<AddressBook> {
|
||||
return filterAbTagByIntersection();
|
||||
},
|
||||
setter: (bool v) async {
|
||||
bind.mainSetLocalOption(key: filterAbTagOption, value: v ? 'Y' : '');
|
||||
bind.mainSetLocalOption(key: filterAbTagOption, value: v ? 'Y' : defaultOptionNo);
|
||||
gFFI.abModel.filterByIntersection.value = v;
|
||||
},
|
||||
dismissOnClicked: true,
|
||||
enabled: (!isOptFixed).obs,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -177,11 +177,14 @@ void changeIdDialog() {
|
||||
}
|
||||
|
||||
void changeWhiteList({Function()? callback}) async {
|
||||
var newWhiteList = (await bind.mainGetOption(key: 'whitelist')).split(',');
|
||||
var newWhiteListField = newWhiteList.join('\n');
|
||||
final curWhiteList = await bind.mainGetOption(key: kOptionWhitelist);
|
||||
var newWhiteListField = curWhiteList == defaultOptionWhitelist
|
||||
? ''
|
||||
: curWhiteList.split(',').join('\n');
|
||||
var controller = TextEditingController(text: newWhiteListField);
|
||||
var msg = "";
|
||||
var isInProgress = false;
|
||||
final isOptFixed = isOptionFixed(kOptionWhitelist);
|
||||
gFFI.dialogManager.show((setState, close, context) {
|
||||
return CustomAlertDialog(
|
||||
title: Text(translate("IP Whitelisting")),
|
||||
@ -214,14 +217,15 @@ void changeWhiteList({Function()? callback}) async {
|
||||
),
|
||||
actions: [
|
||||
dialogButton("Cancel", onPressed: close, isOutline: true),
|
||||
dialogButton("Clear", onPressed: () async {
|
||||
await bind.mainSetOption(key: 'whitelist', value: '');
|
||||
dialogButton("Clear", onPressed: isOptFixed ? null : () async {
|
||||
await bind.mainSetOption(
|
||||
key: kOptionWhitelist, value: defaultOptionWhitelist);
|
||||
callback?.call();
|
||||
close();
|
||||
}, isOutline: true),
|
||||
dialogButton(
|
||||
"OK",
|
||||
onPressed: () async {
|
||||
onPressed: isOptFixed ? null : () async {
|
||||
setState(() {
|
||||
msg = "";
|
||||
isInProgress = true;
|
||||
@ -248,7 +252,11 @@ void changeWhiteList({Function()? callback}) async {
|
||||
}
|
||||
newWhiteList = ips.join(',');
|
||||
}
|
||||
await bind.mainSetOption(key: 'whitelist', value: newWhiteList);
|
||||
if (newWhiteList.trim().isEmpty) {
|
||||
newWhiteList = defaultOptionWhitelist;
|
||||
}
|
||||
await bind.mainSetOption(
|
||||
key: kOptionWhitelist, value: newWhiteList);
|
||||
callback?.call();
|
||||
close();
|
||||
},
|
||||
@ -298,7 +306,7 @@ Future<String> changeDirectAccessPort(
|
||||
dialogButton("Cancel", onPressed: close, isOutline: true),
|
||||
dialogButton("OK", onPressed: () async {
|
||||
await bind.mainSetOption(
|
||||
key: 'direct-access-port', value: controller.text);
|
||||
key: kOptionDirectAccessPort, value: controller.text);
|
||||
close();
|
||||
}),
|
||||
],
|
||||
@ -345,7 +353,7 @@ Future<String> changeAutoDisconnectTimeout(String old) async {
|
||||
dialogButton("Cancel", onPressed: close, isOutline: true),
|
||||
dialogButton("OK", onPressed: () async {
|
||||
await bind.mainSetOption(
|
||||
key: 'auto-disconnect-timeout', value: controller.text);
|
||||
key: kOptionAutoDisconnectTimeout, value: controller.text);
|
||||
close();
|
||||
}),
|
||||
],
|
||||
|
@ -537,7 +537,7 @@ class _PeerTabPageState extends State<PeerTabPage>
|
||||
),
|
||||
onTap: () async {
|
||||
await bind.mainSetLocalOption(
|
||||
key: "hideAbTagsPanel", value: hideAbTagsPanel.value ? "" : "Y");
|
||||
key: "hideAbTagsPanel", value: hideAbTagsPanel.value ? defaultOptionNo : "Y");
|
||||
hideAbTagsPanel.value = !hideAbTagsPanel.value;
|
||||
});
|
||||
}
|
||||
|
@ -10,8 +10,8 @@ import 'package:get/get.dart';
|
||||
customImageQualityWidget(
|
||||
{required double initQuality,
|
||||
required double initFps,
|
||||
required Function(double) setQuality,
|
||||
required Function(double) setFps,
|
||||
required Function(double)? setQuality,
|
||||
required Function(double)? setFps,
|
||||
required bool showFps,
|
||||
required bool showMoreQuality}) {
|
||||
if (initQuality < kMinQuality ||
|
||||
@ -27,16 +27,12 @@ customImageQualityWidget(
|
||||
final RxBool moreQualityChecked = RxBool(qualityValue.value > kMaxQuality);
|
||||
final debouncerQuality = Debouncer<double>(
|
||||
Duration(milliseconds: 1000),
|
||||
onChanged: (double v) {
|
||||
setQuality(v);
|
||||
},
|
||||
onChanged: setQuality,
|
||||
initialValue: qualityValue.value,
|
||||
);
|
||||
final debouncerFps = Debouncer<double>(
|
||||
Duration(milliseconds: 1000),
|
||||
onChanged: (double v) {
|
||||
setFps(v);
|
||||
},
|
||||
onChanged: setFps,
|
||||
initialValue: fpsValue.value,
|
||||
);
|
||||
|
||||
@ -62,10 +58,12 @@ customImageQualityWidget(
|
||||
divisions: moreQualityChecked.value
|
||||
? ((kMaxMoreQuality - kMinQuality) / 10).round()
|
||||
: ((kMaxQuality - kMinQuality) / 5).round(),
|
||||
onChanged: (double value) async {
|
||||
qualityValue.value = value;
|
||||
debouncerQuality.value = value;
|
||||
},
|
||||
onChanged: setQuality == null
|
||||
? null
|
||||
: (double value) async {
|
||||
qualityValue.value = value;
|
||||
debouncerQuality.value = value;
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
@ -124,10 +122,12 @@ customImageQualityWidget(
|
||||
min: kMinFps,
|
||||
max: kMaxFps,
|
||||
divisions: ((kMaxFps - kMinFps) / 5).round(),
|
||||
onChanged: (double value) async {
|
||||
fpsValue.value = value;
|
||||
debouncerFps.value = value;
|
||||
},
|
||||
onChanged: setFps == null
|
||||
? null
|
||||
: (double value) async {
|
||||
fpsValue.value = value;
|
||||
debouncerFps.value = value;
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
@ -152,21 +152,29 @@ customImageQualitySetting() {
|
||||
final qualityKey = 'custom_image_quality';
|
||||
final fpsKey = 'custom-fps';
|
||||
|
||||
var initQuality =
|
||||
final initQuality =
|
||||
(double.tryParse(bind.mainGetUserDefaultOption(key: qualityKey)) ??
|
||||
kDefaultQuality);
|
||||
var initFps = (double.tryParse(bind.mainGetUserDefaultOption(key: fpsKey)) ??
|
||||
kDefaultFps);
|
||||
final isQuanlityFixed = isOptionFixed(qualityKey);
|
||||
final initFps =
|
||||
(double.tryParse(bind.mainGetUserDefaultOption(key: fpsKey)) ??
|
||||
kDefaultFps);
|
||||
final isFpsFixed = isOptionFixed(fpsKey);
|
||||
|
||||
return customImageQualityWidget(
|
||||
initQuality: initQuality,
|
||||
initFps: initFps,
|
||||
setQuality: (v) {
|
||||
bind.mainSetUserDefaultOption(key: qualityKey, value: v.toString());
|
||||
},
|
||||
setFps: (v) {
|
||||
bind.mainSetUserDefaultOption(key: fpsKey, value: v.toString());
|
||||
},
|
||||
setQuality: isQuanlityFixed
|
||||
? null
|
||||
: (v) {
|
||||
bind.mainSetUserDefaultOption(
|
||||
key: qualityKey, value: v.toString());
|
||||
},
|
||||
setFps: isFpsFixed
|
||||
? null
|
||||
: (v) {
|
||||
bind.mainSetUserDefaultOption(key: fpsKey, value: v.toString());
|
||||
},
|
||||
showFps: true,
|
||||
showMoreQuality: true);
|
||||
}
|
||||
@ -208,23 +216,25 @@ List<Widget> ServerConfigImportExportWidgets(
|
||||
|
||||
List<(String, String)> otherDefaultSettings() {
|
||||
List<(String, String)> v = [
|
||||
('View Mode', 'view_only'),
|
||||
if ((isDesktop || isWebDesktop)) ('show_monitors_tip', kKeyShowMonitorsToolbar),
|
||||
if ((isDesktop || isWebDesktop)) ('Collapse toolbar', 'collapse_toolbar'),
|
||||
('Show remote cursor', 'show_remote_cursor'),
|
||||
('Follow remote cursor', 'follow_remote_cursor'),
|
||||
('Follow remote window focus', 'follow_remote_window'),
|
||||
if ((isDesktop || isWebDesktop)) ('Zoom cursor', 'zoom-cursor'),
|
||||
('Show quality monitor', 'show_quality_monitor'),
|
||||
('Mute', 'disable_audio'),
|
||||
if (isDesktop) ('Enable file copy and paste', 'enable_file_transfer'),
|
||||
('Disable clipboard', 'disable_clipboard'),
|
||||
('Lock after session end', 'lock_after_session_end'),
|
||||
('Privacy mode', 'privacy_mode'),
|
||||
if (isMobile) ('Touch mode', 'touch-mode'),
|
||||
('True color (4:4:4)', 'i444'),
|
||||
('View Mode', kOptionViewOnly),
|
||||
if ((isDesktop || isWebDesktop))
|
||||
('show_monitors_tip', kKeyShowMonitorsToolbar),
|
||||
if ((isDesktop || isWebDesktop))
|
||||
('Collapse toolbar', kOptionCollapseToolbar),
|
||||
('Show remote cursor', kOptionShowRemoteCursor),
|
||||
('Follow remote cursor', kOptionFollowRemoteCursor),
|
||||
('Follow remote window focus', kOptionFollowRemoteWindow),
|
||||
if ((isDesktop || isWebDesktop)) ('Zoom cursor', kOptionZoomCursor),
|
||||
('Show quality monitor', kOptionShowQualityMonitor),
|
||||
('Mute', kOptionDisableAudio),
|
||||
if (isDesktop) ('Enable file copy and paste', kOptionEnableFileTransfer),
|
||||
('Disable clipboard', kOptionDisableClipboard),
|
||||
('Lock after session end', kOptionLockAfterSessionEnd),
|
||||
('Privacy mode', kOptionPrivacyMode),
|
||||
if (isMobile) ('Touch mode', kOptionTouchMode),
|
||||
('True color (4:4:4)', kOptionI444),
|
||||
('Reverse mouse wheel', kKeyReverseMouseWheel),
|
||||
('swap-left-right-mouse', 'swap-left-right-mouse'),
|
||||
('swap-left-right-mouse', kOptionSwapLeftRightMouse),
|
||||
if (isDesktop && useTextureRender)
|
||||
(
|
||||
'Show displays as individual windows',
|
||||
|
@ -327,7 +327,7 @@ Future<List<TRadioMenu<String>>> toolbarCodec(
|
||||
final alternativeCodecs =
|
||||
await bind.sessionAlternativeCodecs(sessionId: sessionId);
|
||||
final groupValue = await bind.sessionGetOption(
|
||||
sessionId: sessionId, arg: 'codec-preference') ??
|
||||
sessionId: sessionId, arg: kOptionCodecPreference) ??
|
||||
'';
|
||||
final List<bool> codecs = [];
|
||||
try {
|
||||
@ -349,7 +349,7 @@ Future<List<TRadioMenu<String>>> toolbarCodec(
|
||||
onChanged(String? value) async {
|
||||
if (value == null) return;
|
||||
await bind.sessionPeerOption(
|
||||
sessionId: sessionId, name: 'codec-preference', value: value);
|
||||
sessionId: sessionId, name: kOptionCodecPreference, value: value);
|
||||
bind.sessionChangePreferCodec(sessionId: sessionId);
|
||||
}
|
||||
|
||||
|
@ -68,11 +68,53 @@ const String kWindowEventMoveTabToNewWindow = "move_tab_to_new_window";
|
||||
const String kWindowEventGetCachedSessionData = "get_cached_session_data";
|
||||
const String kWindowEventOpenMonitorSession = "open_monitor_session";
|
||||
|
||||
const String kOptionViewStyle = "view_style";
|
||||
const String kOptionScrollStyle = "scroll_style";
|
||||
const String kOptionImageQuality = "image_quality";
|
||||
const String kOptionOpenNewConnInTabs = "enable-open-new-connections-in-tabs";
|
||||
const String kOptionOpenInTabs = "allow-open-in-tabs";
|
||||
const String kOptionOpenInWindows = "allow-open-in-windows";
|
||||
const String kOptionForceAlwaysRelay = "force-always-relay";
|
||||
const String kOptionViewOnly = "view-only";
|
||||
const String kOptionViewOnly = "view_only";
|
||||
const String kOptionEnableLanDiscovery = "enable-lan-discovery";
|
||||
const String kOptionWhitelist = "whitelist";
|
||||
const String kOptionEnableAbr = "enable-abr";
|
||||
const String kOptionEnableRecordSession = "enable-record-session";
|
||||
const String kOptionDirectServer = "direct-server";
|
||||
const String kOptionDirectAccessPort = "direct-access-port";
|
||||
const String kOptionAllowAutoDisconnect = "allow-auto-disconnect";
|
||||
const String kOptionAutoDisconnectTimeout = "auto-disconnect-timeout";
|
||||
const String kOptionEnableHwcodec = "enable-hwcodec";
|
||||
const String kOptionAllowAutoRecordIncoming = "allow-auto-record-incoming";
|
||||
const String kOptionVideoSaveDirectory = "video-save-directory";
|
||||
const String kOptionAccessMode = "access-mode";
|
||||
const String kOptionEnableKeyboard = "enable-keyboard";
|
||||
// "Settings -> Security -> Permissions"
|
||||
const String kOptionEnableClipboard = "enable-clipboard";
|
||||
const String kOptionEnableFileTransfer = "enable-file-transfer";
|
||||
const String kOptionEnableAudio = "enable-audio";
|
||||
const String kOptionEnableTunnel = "enable-tunnel";
|
||||
const String kOptionEnableRemoteRestart = "enable-remote-restart";
|
||||
const String kOptionEnableBlockInput = "enable-block-input";
|
||||
const String kOptionAllowRemoteConfigModification =
|
||||
"allow-remote-config-modification";
|
||||
const String kOptionVerificationMethod = "verification-method";
|
||||
const String kOptionApproveMode = "approve-mode";
|
||||
const String kOptionCollapseToolbar = "collapse_toolbar";
|
||||
const String kOptionShowRemoteCursor = "show_remote_cursor";
|
||||
const String kOptionFollowRemoteCursor = "follow_remote_cursor";
|
||||
const String kOptionFollowRemoteWindow = "follow_remote_window";
|
||||
const String kOptionZoomCursor = "zoom-cursor";
|
||||
const String kOptionShowQualityMonitor = "show_quality_monitor";
|
||||
const String kOptionDisableAudio = "disable_audio";
|
||||
// "Settings -> Display -> Other default options"
|
||||
const String kOptionDisableClipboard = "disable_clipboard";
|
||||
const String kOptionLockAfterSessionEnd = "lock_after_session_end";
|
||||
const String kOptionPrivacyMode = "privacy_mode";
|
||||
const String kOptionTouchMode = "touch-mode";
|
||||
const String kOptionI444 = "i444";
|
||||
const String kOptionSwapLeftRightMouse = 'swap-left-right-mouse';
|
||||
const String kOptionCodecPreference = 'codec-preference';
|
||||
|
||||
const String kUrlActionClose = "close";
|
||||
|
||||
@ -208,12 +250,6 @@ const kRemoteImageQualityLow = 'low';
|
||||
/// [kRemoteImageQualityCustom] Custom image quality.
|
||||
const kRemoteImageQualityCustom = 'custom';
|
||||
|
||||
/// [kRemoteAudioGuestToHost] Guest to host audio mode(default).
|
||||
const kRemoteAudioGuestToHost = 'guest-to-host';
|
||||
|
||||
/// [kRemoteAudioDualWay] dual-way audio mode(default).
|
||||
const kRemoteAudioDualWay = 'dual-way';
|
||||
|
||||
const kIgnoreDpi = true;
|
||||
|
||||
// ================================ mobile ================================
|
||||
|
@ -332,22 +332,23 @@ class _GeneralState extends State<_General> {
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
final isOptFixed = isOptionFixed(kCommConfKeyTheme);
|
||||
return _Card(title: 'Theme', children: [
|
||||
_Radio<String>(context,
|
||||
value: 'light',
|
||||
groupValue: current,
|
||||
label: 'Light',
|
||||
onChanged: onChanged),
|
||||
onChanged: isOptFixed ? null : onChanged),
|
||||
_Radio<String>(context,
|
||||
value: 'dark',
|
||||
groupValue: current,
|
||||
label: 'Dark',
|
||||
onChanged: onChanged),
|
||||
onChanged: isOptFixed ? null : onChanged),
|
||||
_Radio<String>(context,
|
||||
value: 'system',
|
||||
groupValue: current,
|
||||
label: 'Follow System',
|
||||
onChanged: onChanged),
|
||||
onChanged: isOptFixed ? null : onChanged),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -547,21 +548,24 @@ class _GeneralState extends State<_General> {
|
||||
)).marginOnly(left: 10),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
String? initialDirectory;
|
||||
if (await Directory.fromUri(Uri.directory(user_dir))
|
||||
.exists()) {
|
||||
initialDirectory = user_dir;
|
||||
}
|
||||
String? selectedDirectory = await FilePicker.platform
|
||||
.getDirectoryPath(initialDirectory: initialDirectory);
|
||||
if (selectedDirectory != null) {
|
||||
await bind.mainSetOption(
|
||||
key: 'video-save-directory',
|
||||
value: selectedDirectory);
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
onPressed: isOptionFixed(kOptionVideoSaveDirectory)
|
||||
? null
|
||||
: () async {
|
||||
String? initialDirectory;
|
||||
if (await Directory.fromUri(Uri.directory(user_dir))
|
||||
.exists()) {
|
||||
initialDirectory = user_dir;
|
||||
}
|
||||
String? selectedDirectory =
|
||||
await FilePicker.platform.getDirectoryPath(
|
||||
initialDirectory: initialDirectory);
|
||||
if (selectedDirectory != null) {
|
||||
await bind.mainSetOption(
|
||||
key: kOptionVideoSaveDirectory,
|
||||
value: selectedDirectory);
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
child: Text(translate('Change')))
|
||||
.marginOnly(left: 5),
|
||||
],
|
||||
@ -580,12 +584,13 @@ class _GeneralState extends State<_General> {
|
||||
Map<String, String> langsMap = {for (var v in langsList) v[0]: v[1]};
|
||||
List<String> keys = langsMap.keys.toList();
|
||||
List<String> values = langsMap.values.toList();
|
||||
keys.insert(0, '');
|
||||
keys.insert(0, defaultOptionLang);
|
||||
values.insert(0, translate('Default'));
|
||||
String currentKey = bind.mainGetLocalOption(key: kCommConfKeyLang);
|
||||
if (!keys.contains(currentKey)) {
|
||||
currentKey = '';
|
||||
currentKey = defaultOptionLang;
|
||||
}
|
||||
final isOptFixed = isOptionFixed(kCommConfKeyLang);
|
||||
return ComboBox(
|
||||
keys: keys,
|
||||
values: values,
|
||||
@ -595,6 +600,7 @@ class _GeneralState extends State<_General> {
|
||||
reloadAllWindows();
|
||||
bind.mainChangeLanguage(lang: key);
|
||||
},
|
||||
enabled: !isOptFixed,
|
||||
).marginOnly(left: _kContentHMargin);
|
||||
});
|
||||
}
|
||||
@ -728,7 +734,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
return _Card(title: 'Permissions', children: [
|
||||
ComboBox(
|
||||
keys: [
|
||||
'',
|
||||
defaultOptionAccessMode,
|
||||
'full',
|
||||
'view',
|
||||
],
|
||||
@ -737,37 +743,39 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
translate('Full Access'),
|
||||
translate('Screen Share'),
|
||||
],
|
||||
enabled: enabled,
|
||||
enabled: enabled && !isOptionFixed(kOptionAccessMode),
|
||||
initialKey: initialKey,
|
||||
onChanged: (mode) async {
|
||||
await bind.mainSetOption(key: 'access-mode', value: mode);
|
||||
await bind.mainSetOption(key: kOptionAccessMode, value: mode);
|
||||
setState(() {});
|
||||
}).marginOnly(left: _kContentHMargin),
|
||||
Column(
|
||||
children: [
|
||||
_OptionCheckBox(context, 'Enable keyboard/mouse', 'enable-keyboard',
|
||||
_OptionCheckBox(
|
||||
context, 'Enable keyboard/mouse', kOptionEnableKeyboard,
|
||||
enabled: enabled, fakeValue: fakeValue),
|
||||
_OptionCheckBox(context, 'Enable clipboard', 'enable-clipboard',
|
||||
_OptionCheckBox(context, 'Enable clipboard', kOptionEnableClipboard,
|
||||
enabled: enabled, fakeValue: fakeValue),
|
||||
_OptionCheckBox(
|
||||
context, 'Enable file transfer', 'enable-file-transfer',
|
||||
context, 'Enable file transfer', kOptionEnableFileTransfer,
|
||||
enabled: enabled, fakeValue: fakeValue),
|
||||
_OptionCheckBox(context, 'Enable audio', 'enable-audio',
|
||||
enabled: enabled, fakeValue: fakeValue),
|
||||
_OptionCheckBox(context, 'Enable TCP tunneling', 'enable-tunnel',
|
||||
_OptionCheckBox(context, 'Enable audio', kOptionEnableAudio,
|
||||
enabled: enabled, fakeValue: fakeValue),
|
||||
_OptionCheckBox(
|
||||
context, 'Enable remote restart', 'enable-remote-restart',
|
||||
context, 'Enable TCP tunneling', kOptionEnableTunnel,
|
||||
enabled: enabled, fakeValue: fakeValue),
|
||||
_OptionCheckBox(
|
||||
context, 'Enable recording session', 'enable-record-session',
|
||||
context, 'Enable remote restart', kOptionEnableRemoteRestart,
|
||||
enabled: enabled, fakeValue: fakeValue),
|
||||
_OptionCheckBox(
|
||||
context, 'Enable recording session', kOptionEnableRecordSession,
|
||||
enabled: enabled, fakeValue: fakeValue),
|
||||
if (isWindows)
|
||||
_OptionCheckBox(
|
||||
context, 'Enable blocking user input', 'enable-block-input',
|
||||
_OptionCheckBox(context, 'Enable blocking user input',
|
||||
kOptionEnableBlockInput,
|
||||
enabled: enabled, fakeValue: fakeValue),
|
||||
_OptionCheckBox(context, 'Enable remote configuration modification',
|
||||
'allow-remote-config-modification',
|
||||
kOptionAllowRemoteConfigModification,
|
||||
enabled: enabled, fakeValue: fakeValue),
|
||||
],
|
||||
),
|
||||
@ -801,14 +809,15 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
value: value,
|
||||
groupValue: currentValue,
|
||||
label: value,
|
||||
onChanged: ((value) {
|
||||
() async {
|
||||
await model.setVerificationMethod(
|
||||
passwordKeys[passwordValues.indexOf(value)]);
|
||||
await model.updatePasswordModel();
|
||||
}();
|
||||
}),
|
||||
enabled: !locked,
|
||||
onChanged: locked
|
||||
? null
|
||||
: ((value) {
|
||||
() async {
|
||||
await model.setVerificationMethod(
|
||||
passwordKeys[passwordValues.indexOf(value)]);
|
||||
await model.updatePasswordModel();
|
||||
}();
|
||||
}),
|
||||
))
|
||||
.toList();
|
||||
|
||||
@ -842,7 +851,11 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
))
|
||||
.toList();
|
||||
|
||||
final modeKeys = ['password', 'click', ''];
|
||||
final modeKeys = <String>[
|
||||
'password',
|
||||
'click',
|
||||
defaultOptionApproveMode
|
||||
];
|
||||
final modeValues = [
|
||||
translate('Accept sessions via password'),
|
||||
translate('Accept sessions via click'),
|
||||
@ -852,9 +865,10 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
if (!modeKeys.contains(modeInitialKey)) modeInitialKey = '';
|
||||
final usePassword = model.approveMode != 'click';
|
||||
|
||||
final isApproveModeFixed = isOptionFixed(kOptionApproveMode);
|
||||
return _Card(title: 'Password', children: [
|
||||
ComboBox(
|
||||
enabled: !locked,
|
||||
enabled: !locked && !isApproveModeFixed,
|
||||
keys: modeKeys,
|
||||
values: modeValues,
|
||||
initialKey: modeInitialKey,
|
||||
@ -930,15 +944,17 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
update() => setState(() {});
|
||||
RxBool applyEnabled = false.obs;
|
||||
return [
|
||||
_OptionCheckBox(context, 'Enable direct IP access', 'direct-server',
|
||||
_OptionCheckBox(context, 'Enable direct IP access', kOptionDirectServer,
|
||||
update: update, enabled: !locked),
|
||||
() {
|
||||
// Simple temp wrapper for PR check
|
||||
tmpWrapper() {
|
||||
bool enabled = option2bool(
|
||||
'direct-server', bind.mainGetOptionSync(key: 'direct-server'));
|
||||
bool enabled = option2bool(kOptionDirectServer,
|
||||
bind.mainGetOptionSync(key: kOptionDirectServer));
|
||||
if (!enabled) applyEnabled.value = false;
|
||||
controller.text = bind.mainGetOptionSync(key: 'direct-access-port');
|
||||
controller.text =
|
||||
bind.mainGetOptionSync(key: kOptionDirectAccessPort);
|
||||
final isOptFixed = isOptionFixed(kOptionDirectAccessPort);
|
||||
return Offstage(
|
||||
offstage: !enabled,
|
||||
child: _SubLabeledWidget(
|
||||
@ -949,7 +965,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
width: 95,
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
enabled: enabled && !locked,
|
||||
enabled: enabled && !locked && !isOptFixed,
|
||||
onChanged: (_) => applyEnabled.value = true,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(
|
||||
@ -963,11 +979,14 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
).marginOnly(right: 15),
|
||||
),
|
||||
Obx(() => ElevatedButton(
|
||||
onPressed: applyEnabled.value && enabled && !locked
|
||||
onPressed: applyEnabled.value &&
|
||||
enabled &&
|
||||
!locked &&
|
||||
!isOptFixed
|
||||
? () async {
|
||||
applyEnabled.value = false;
|
||||
await bind.mainSetOption(
|
||||
key: 'direct-access-port',
|
||||
key: kOptionDirectAccessPort,
|
||||
value: controller.text);
|
||||
}
|
||||
: null,
|
||||
@ -976,7 +995,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
),
|
||||
))
|
||||
]),
|
||||
enabled: enabled && !locked,
|
||||
enabled: enabled && !locked && !isOptFixed,
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -990,17 +1009,19 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
bool enabled = !locked;
|
||||
// Simple temp wrapper for PR check
|
||||
tmpWrapper() {
|
||||
RxBool hasWhitelist =
|
||||
bind.mainGetOptionSync(key: 'whitelist').isNotEmpty.obs;
|
||||
RxBool hasWhitelist = (bind.mainGetOptionSync(key: kOptionWhitelist) !=
|
||||
defaultOptionWhitelist)
|
||||
.obs;
|
||||
update() async {
|
||||
hasWhitelist.value =
|
||||
bind.mainGetOptionSync(key: 'whitelist').isNotEmpty;
|
||||
hasWhitelist.value = bind.mainGetOptionSync(key: kOptionWhitelist) !=
|
||||
defaultOptionWhitelist;
|
||||
}
|
||||
|
||||
onChanged(bool? checked) async {
|
||||
changeWhiteList(callback: update);
|
||||
}
|
||||
|
||||
final isOptFixed = isOptionFixed(kOptionWhitelist);
|
||||
return GestureDetector(
|
||||
child: Tooltip(
|
||||
message: translate('whitelist_tip'),
|
||||
@ -1008,13 +1029,16 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
children: [
|
||||
Checkbox(
|
||||
value: hasWhitelist.value,
|
||||
onChanged: enabled ? onChanged : null)
|
||||
onChanged: enabled && !isOptFixed ? onChanged : null)
|
||||
.marginOnly(right: 5),
|
||||
Offstage(
|
||||
offstage: !hasWhitelist.value,
|
||||
child: const Icon(Icons.warning_amber_rounded,
|
||||
color: Color.fromARGB(255, 255, 204, 0))
|
||||
.marginOnly(right: 5),
|
||||
child: MouseRegion(
|
||||
child: const Icon(Icons.warning_amber_rounded,
|
||||
color: Color.fromARGB(255, 255, 204, 0))
|
||||
.marginOnly(right: 5),
|
||||
cursor: SystemMouseCursors.click,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
@ -1025,9 +1049,11 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
],
|
||||
)),
|
||||
),
|
||||
onTap: () {
|
||||
onChanged(!hasWhitelist.value);
|
||||
},
|
||||
onTap: enabled
|
||||
? () {
|
||||
onChanged(!hasWhitelist.value);
|
||||
}
|
||||
: null,
|
||||
).marginOnly(left: _kCheckBoxLeftMargin);
|
||||
}
|
||||
|
||||
@ -1078,16 +1104,17 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
TextEditingController controller = TextEditingController();
|
||||
update() => setState(() {});
|
||||
RxBool applyEnabled = false.obs;
|
||||
final optionKey = 'allow-auto-disconnect';
|
||||
final timeoutKey = 'auto-disconnect-timeout';
|
||||
return [
|
||||
_OptionCheckBox(context, 'auto_disconnect_option_tip', optionKey,
|
||||
_OptionCheckBox(
|
||||
context, 'auto_disconnect_option_tip', kOptionAllowAutoDisconnect,
|
||||
update: update, enabled: !locked),
|
||||
() {
|
||||
bool enabled =
|
||||
option2bool(optionKey, bind.mainGetOptionSync(key: optionKey));
|
||||
bool enabled = option2bool(kOptionAllowAutoDisconnect,
|
||||
bind.mainGetOptionSync(key: kOptionAllowAutoDisconnect));
|
||||
if (!enabled) applyEnabled.value = false;
|
||||
controller.text = bind.mainGetOptionSync(key: timeoutKey);
|
||||
controller.text =
|
||||
bind.mainGetOptionSync(key: kOptionAutoDisconnectTimeout);
|
||||
final isOptFixed = isOptionFixed(kOptionAutoDisconnectTimeout);
|
||||
return Offstage(
|
||||
offstage: !enabled,
|
||||
child: _SubLabeledWidget(
|
||||
@ -1098,7 +1125,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
width: 95,
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
enabled: enabled && !locked,
|
||||
enabled: enabled && !locked && isOptFixed,
|
||||
onChanged: (_) => applyEnabled.value = true,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(
|
||||
@ -1112,19 +1139,21 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
).marginOnly(right: 15),
|
||||
),
|
||||
Obx(() => ElevatedButton(
|
||||
onPressed: applyEnabled.value && enabled && !locked
|
||||
? () async {
|
||||
applyEnabled.value = false;
|
||||
await bind.mainSetOption(
|
||||
key: timeoutKey, value: controller.text);
|
||||
}
|
||||
: null,
|
||||
onPressed:
|
||||
applyEnabled.value && enabled && !locked && !isOptFixed
|
||||
? () async {
|
||||
applyEnabled.value = false;
|
||||
await bind.mainSetOption(
|
||||
key: kOptionAutoDisconnectTimeout,
|
||||
value: controller.text);
|
||||
}
|
||||
: null,
|
||||
child: Text(
|
||||
translate('Apply'),
|
||||
),
|
||||
))
|
||||
]),
|
||||
enabled: enabled && !locked,
|
||||
enabled: enabled && !locked && !isOptFixed,
|
||||
),
|
||||
);
|
||||
}(),
|
||||
@ -1273,46 +1302,47 @@ class _DisplayState extends State<_Display> {
|
||||
}
|
||||
|
||||
Widget viewStyle(BuildContext context) {
|
||||
final key = 'view_style';
|
||||
final isOptFixed = isOptionFixed(kOptionViewStyle);
|
||||
onChanged(String value) async {
|
||||
await bind.mainSetUserDefaultOption(key: key, value: value);
|
||||
await bind.mainSetUserDefaultOption(key: kOptionViewStyle, value: value);
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
final groupValue = bind.mainGetUserDefaultOption(key: key);
|
||||
final groupValue = bind.mainGetUserDefaultOption(key: kOptionViewStyle);
|
||||
return _Card(title: 'Default View Style', children: [
|
||||
_Radio(context,
|
||||
value: kRemoteViewStyleOriginal,
|
||||
groupValue: groupValue,
|
||||
label: 'Scale original',
|
||||
onChanged: onChanged),
|
||||
onChanged: isOptFixed ? null : onChanged),
|
||||
_Radio(context,
|
||||
value: kRemoteViewStyleAdaptive,
|
||||
groupValue: groupValue,
|
||||
label: 'Scale adaptive',
|
||||
onChanged: onChanged),
|
||||
onChanged: isOptFixed ? null : onChanged),
|
||||
]);
|
||||
}
|
||||
|
||||
Widget scrollStyle(BuildContext context) {
|
||||
final key = 'scroll_style';
|
||||
final isOptFixed = isOptionFixed(kOptionScrollStyle);
|
||||
onChanged(String value) async {
|
||||
await bind.mainSetUserDefaultOption(key: key, value: value);
|
||||
await bind.mainSetUserDefaultOption(
|
||||
key: kOptionScrollStyle, value: value);
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
final groupValue = bind.mainGetUserDefaultOption(key: key);
|
||||
final groupValue = bind.mainGetUserDefaultOption(key: kOptionScrollStyle);
|
||||
return _Card(title: 'Default Scroll Style', children: [
|
||||
_Radio(context,
|
||||
value: kRemoteScrollStyleAuto,
|
||||
groupValue: groupValue,
|
||||
label: 'ScrollAuto',
|
||||
onChanged: onChanged),
|
||||
onChanged: isOptFixed ? null : onChanged),
|
||||
_Radio(context,
|
||||
value: kRemoteScrollStyleBar,
|
||||
groupValue: groupValue,
|
||||
label: 'Scrollbar',
|
||||
onChanged: onChanged),
|
||||
onChanged: isOptFixed ? null : onChanged),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -1323,28 +1353,29 @@ class _DisplayState extends State<_Display> {
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
final isOptFixed = isOptionFixed(key);
|
||||
final groupValue = bind.mainGetUserDefaultOption(key: key);
|
||||
return _Card(title: 'Default Image Quality', children: [
|
||||
_Radio(context,
|
||||
value: kRemoteImageQualityBest,
|
||||
groupValue: groupValue,
|
||||
label: 'Good image quality',
|
||||
onChanged: onChanged),
|
||||
onChanged: isOptFixed ? null : onChanged),
|
||||
_Radio(context,
|
||||
value: kRemoteImageQualityBalanced,
|
||||
groupValue: groupValue,
|
||||
label: 'Balanced',
|
||||
onChanged: onChanged),
|
||||
onChanged: isOptFixed ? null : onChanged),
|
||||
_Radio(context,
|
||||
value: kRemoteImageQualityLow,
|
||||
groupValue: groupValue,
|
||||
label: 'Optimize reaction time',
|
||||
onChanged: onChanged),
|
||||
onChanged: isOptFixed ? null : onChanged),
|
||||
_Radio(context,
|
||||
value: kRemoteImageQualityCustom,
|
||||
groupValue: groupValue,
|
||||
label: 'Custom',
|
||||
onChanged: onChanged),
|
||||
onChanged: isOptFixed ? null : onChanged),
|
||||
Offstage(
|
||||
offstage: groupValue != kRemoteImageQualityCustom,
|
||||
child: customImageQualitySetting(),
|
||||
@ -1353,14 +1384,16 @@ class _DisplayState extends State<_Display> {
|
||||
}
|
||||
|
||||
Widget codec(BuildContext context) {
|
||||
final key = 'codec-preference';
|
||||
onChanged(String value) async {
|
||||
await bind.mainSetUserDefaultOption(key: key, value: value);
|
||||
await bind.mainSetUserDefaultOption(
|
||||
key: kOptionCodecPreference, value: value);
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
final groupValue = bind.mainGetUserDefaultOption(key: key);
|
||||
final groupValue =
|
||||
bind.mainGetUserDefaultOption(key: kOptionCodecPreference);
|
||||
var hwRadios = [];
|
||||
final isOptFixed = isOptionFixed(kOptionCodecPreference);
|
||||
try {
|
||||
final Map codecsJson = jsonDecode(bind.mainSupportedHwdecodings());
|
||||
final h264 = codecsJson['h264'] ?? false;
|
||||
@ -1370,14 +1403,14 @@ class _DisplayState extends State<_Display> {
|
||||
value: 'h264',
|
||||
groupValue: groupValue,
|
||||
label: 'H264',
|
||||
onChanged: onChanged));
|
||||
onChanged: isOptFixed ? null : onChanged));
|
||||
}
|
||||
if (h265) {
|
||||
hwRadios.add(_Radio(context,
|
||||
value: 'h265',
|
||||
groupValue: groupValue,
|
||||
label: 'H265',
|
||||
onChanged: onChanged));
|
||||
onChanged: isOptFixed ? null : onChanged));
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint("failed to parse supported hwdecodings, err=$e");
|
||||
@ -1387,22 +1420,22 @@ class _DisplayState extends State<_Display> {
|
||||
value: 'auto',
|
||||
groupValue: groupValue,
|
||||
label: 'Auto',
|
||||
onChanged: onChanged),
|
||||
onChanged: isOptFixed ? null : onChanged),
|
||||
_Radio(context,
|
||||
value: 'vp8',
|
||||
groupValue: groupValue,
|
||||
label: 'VP8',
|
||||
onChanged: onChanged),
|
||||
onChanged: isOptFixed ? null : onChanged),
|
||||
_Radio(context,
|
||||
value: 'vp9',
|
||||
groupValue: groupValue,
|
||||
label: 'VP9',
|
||||
onChanged: onChanged),
|
||||
onChanged: isOptFixed ? null : onChanged),
|
||||
_Radio(context,
|
||||
value: 'av1',
|
||||
groupValue: groupValue,
|
||||
label: 'AV1',
|
||||
onChanged: onChanged),
|
||||
onChanged: isOptFixed ? null : onChanged),
|
||||
...hwRadios,
|
||||
]);
|
||||
}
|
||||
@ -1445,22 +1478,29 @@ class _DisplayState extends State<_Display> {
|
||||
|
||||
Widget otherRow(String label, String key) {
|
||||
final value = bind.mainGetUserDefaultOption(key: key) == 'Y';
|
||||
final isOptFixed = isOptionFixed(key);
|
||||
onChanged(bool b) async {
|
||||
await bind.mainSetUserDefaultOption(key: key, value: b ? 'Y' : '');
|
||||
await bind.mainSetUserDefaultOption(
|
||||
key: key,
|
||||
value: b
|
||||
? 'Y'
|
||||
: (key == kOptionEnableFileTransfer ? 'N' : defaultOptionNo));
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
return GestureDetector(
|
||||
child: Row(
|
||||
children: [
|
||||
Checkbox(value: value, onChanged: (_) => onChanged(!value))
|
||||
Checkbox(
|
||||
value: value,
|
||||
onChanged: isOptFixed ? null : (_) => onChanged(!value))
|
||||
.marginOnly(right: 5),
|
||||
Expanded(
|
||||
child: Text(translate(label)),
|
||||
)
|
||||
],
|
||||
).marginOnly(left: _kCheckBoxLeftMargin),
|
||||
onTap: () => onChanged(!value));
|
||||
onTap: isOptFixed ? null : () => onChanged(!value));
|
||||
}
|
||||
|
||||
Widget other(BuildContext context) {
|
||||
@ -1772,6 +1812,7 @@ Widget _OptionCheckBox(BuildContext context, String label, String key,
|
||||
bool isServer = true}) {
|
||||
bool value =
|
||||
isServer ? mainGetBoolOptionSync(key) : mainGetLocalBoolOptionSync(key);
|
||||
final isOptFixed = isOptionFixed(key);
|
||||
if (reverse) value = !value;
|
||||
var ref = value.obs;
|
||||
onChanged(option) async {
|
||||
@ -1801,7 +1842,9 @@ Widget _OptionCheckBox(BuildContext context, String label, String key,
|
||||
child: Obx(
|
||||
() => Row(
|
||||
children: [
|
||||
Checkbox(value: ref.value, onChanged: enabled ? onChanged : null)
|
||||
Checkbox(
|
||||
value: ref.value,
|
||||
onChanged: enabled && !isOptFixed ? onChanged : null)
|
||||
.marginOnly(right: 5),
|
||||
Offstage(
|
||||
offstage: !ref.value || checkedIcon == null,
|
||||
@ -1815,7 +1858,7 @@ Widget _OptionCheckBox(BuildContext context, String label, String key,
|
||||
],
|
||||
),
|
||||
).marginOnly(left: _kCheckBoxLeftMargin),
|
||||
onTap: enabled
|
||||
onTap: enabled && !isOptFixed
|
||||
? () {
|
||||
onChanged(!ref.value);
|
||||
}
|
||||
@ -1828,10 +1871,9 @@ Widget _Radio<T>(BuildContext context,
|
||||
{required T value,
|
||||
required T groupValue,
|
||||
required String label,
|
||||
required Function(T value) onChanged,
|
||||
bool autoNewLine = true,
|
||||
bool enabled = true}) {
|
||||
var onChange = enabled
|
||||
required Function(T value)? onChanged,
|
||||
bool autoNewLine = true}) {
|
||||
final onChange2 = onChanged != null
|
||||
? (T? value) {
|
||||
if (value != null) {
|
||||
onChanged(value);
|
||||
@ -1841,18 +1883,18 @@ Widget _Radio<T>(BuildContext context,
|
||||
return GestureDetector(
|
||||
child: Row(
|
||||
children: [
|
||||
Radio<T>(value: value, groupValue: groupValue, onChanged: onChange),
|
||||
Radio<T>(value: value, groupValue: groupValue, onChanged: onChange2),
|
||||
Expanded(
|
||||
child: Text(translate(label),
|
||||
overflow: autoNewLine ? null : TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: _kContentFontSize,
|
||||
color: disabledTextColor(context, enabled)))
|
||||
color: disabledTextColor(context, onChange2 != null)))
|
||||
.marginOnly(left: 5),
|
||||
),
|
||||
],
|
||||
).marginOnly(left: _kRadioLeftMargin),
|
||||
onTap: () => onChange?.call(value),
|
||||
onTap: () => onChange2?.call(value),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -428,7 +428,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
}
|
||||
ConnectionTypeState.init(id);
|
||||
_toolbarState.setShow(
|
||||
bind.mainGetUserDefaultOption(key: 'collapse_toolbar') != 'Y');
|
||||
bind.mainGetUserDefaultOption(key: kOptionCollapseToolbar) != 'Y');
|
||||
tabController.add(TabInfo(
|
||||
key: id,
|
||||
label: id,
|
||||
|
@ -54,7 +54,7 @@ class ToolbarState {
|
||||
_initSet(bool s, bool p) {
|
||||
// Show remubar when connection is established.
|
||||
show =
|
||||
RxBool(bind.mainGetUserDefaultOption(key: 'collapse_toolbar') != 'Y');
|
||||
RxBool(bind.mainGetUserDefaultOption(key: kOptionCollapseToolbar) != 'Y');
|
||||
_pin = RxBool(p);
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,7 @@ class ServerPage extends StatefulWidget implements PageShape {
|
||||
final approveMode = gFFI.serverModel.approveMode;
|
||||
final verificationMethod = gFFI.serverModel.verificationMethod;
|
||||
final showPasswordOption = approveMode != 'click';
|
||||
final isApproveModeFixed = isOptionFixed(kOptionApproveMode);
|
||||
return [
|
||||
PopupMenuItem(
|
||||
enabled: gFFI.serverModel.connectStatus > 0,
|
||||
@ -50,16 +51,19 @@ class ServerPage extends StatefulWidget implements PageShape {
|
||||
value: 'AcceptSessionsViaPassword',
|
||||
child: listTile(
|
||||
'Accept sessions via password', approveMode == 'password'),
|
||||
enabled: !isApproveModeFixed,
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'AcceptSessionsViaClick',
|
||||
child:
|
||||
listTile('Accept sessions via click', approveMode == 'click'),
|
||||
enabled: !isApproveModeFixed,
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: "AcceptSessionsViaBoth",
|
||||
child: listTile("Accept sessions via both",
|
||||
approveMode != 'password' && approveMode != 'click'),
|
||||
enabled: !isApproveModeFixed,
|
||||
),
|
||||
if (showPasswordOption) const PopupMenuDivider(),
|
||||
if (showPasswordOption &&
|
||||
@ -116,7 +120,7 @@ class ServerPage extends StatefulWidget implements PageShape {
|
||||
} else if (value == "Click") {
|
||||
gFFI.serverModel.setApproveMode('click');
|
||||
} else {
|
||||
gFFI.serverModel.setApproveMode('');
|
||||
gFFI.serverModel.setApproveMode(defaultOptionApproveMode);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -100,8 +100,8 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
||||
_denyLANDiscovery = denyLanDiscovery;
|
||||
}
|
||||
|
||||
final onlyWhiteList =
|
||||
(await bind.mainGetOption(key: 'whitelist')).isNotEmpty;
|
||||
final onlyWhiteList = (await bind.mainGetOption(key: kOptionWhitelist)) !=
|
||||
defaultOptionWhitelist;
|
||||
if (onlyWhiteList != _onlyWhiteList) {
|
||||
update = true;
|
||||
_onlyWhiteList = onlyWhiteList;
|
||||
@ -143,7 +143,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
||||
}
|
||||
|
||||
final directAccessPort =
|
||||
await bind.mainGetOption(key: 'direct-access-port');
|
||||
await bind.mainGetOption(key: kOptionDirectAccessPort);
|
||||
if (directAccessPort != _directAccessPort) {
|
||||
update = true;
|
||||
_directAccessPort = directAccessPort;
|
||||
@ -257,16 +257,18 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
||||
SettingsTile.switchTile(
|
||||
title: Text(translate('Deny LAN discovery')),
|
||||
initialValue: _denyLANDiscovery,
|
||||
onToggle: (v) async {
|
||||
await bind.mainSetOption(
|
||||
key: "enable-lan-discovery",
|
||||
value: bool2option("enable-lan-discovery", !v));
|
||||
final newValue = !option2bool('enable-lan-discovery',
|
||||
await bind.mainGetOption(key: 'enable-lan-discovery'));
|
||||
setState(() {
|
||||
_denyLANDiscovery = newValue;
|
||||
});
|
||||
},
|
||||
onToggle: isOptionFixed(kOptionEnableLanDiscovery)
|
||||
? null
|
||||
: (v) async {
|
||||
await bind.mainSetOption(
|
||||
key: kOptionEnableLanDiscovery,
|
||||
value: bool2option(kOptionEnableLanDiscovery, !v));
|
||||
final newValue = !option2bool(kOptionEnableLanDiscovery,
|
||||
await bind.mainGetOption(key: kOptionEnableLanDiscovery));
|
||||
setState(() {
|
||||
_denyLANDiscovery = newValue;
|
||||
});
|
||||
},
|
||||
),
|
||||
SettingsTile.switchTile(
|
||||
title: Row(children: [
|
||||
@ -279,42 +281,51 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
||||
]),
|
||||
initialValue: _onlyWhiteList,
|
||||
onToggle: (_) async {
|
||||
update() async {
|
||||
final onlyWhiteList =
|
||||
(await bind.mainGetOption(key: 'whitelist')).isNotEmpty;
|
||||
if (onlyWhiteList != _onlyWhiteList) {
|
||||
setState(() {
|
||||
_onlyWhiteList = onlyWhiteList;
|
||||
});
|
||||
}
|
||||
}
|
||||
update() async {
|
||||
final onlyWhiteList =
|
||||
(await bind.mainGetOption(key: kOptionWhitelist)) !=
|
||||
defaultOptionWhitelist;
|
||||
if (onlyWhiteList != _onlyWhiteList) {
|
||||
setState(() {
|
||||
_onlyWhiteList = onlyWhiteList;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
changeWhiteList(callback: update);
|
||||
},
|
||||
changeWhiteList(callback: update);
|
||||
},
|
||||
),
|
||||
SettingsTile.switchTile(
|
||||
title: Text('${translate('Adaptive bitrate')} (beta)'),
|
||||
initialValue: _enableAbr,
|
||||
onToggle: (v) async {
|
||||
await bind.mainSetOption(key: "enable-abr", value: v ? "" : "N");
|
||||
final newValue = await bind.mainGetOption(key: "enable-abr") != "N";
|
||||
setState(() {
|
||||
_enableAbr = newValue;
|
||||
});
|
||||
},
|
||||
onToggle: isOptionFixed(kOptionEnableAbr)
|
||||
? null
|
||||
: (v) async {
|
||||
await bind.mainSetOption(
|
||||
key: kOptionEnableAbr, value: v ? defaultOptionYes : "N");
|
||||
final newValue =
|
||||
await bind.mainGetOption(key: kOptionEnableAbr) != "N";
|
||||
setState(() {
|
||||
_enableAbr = newValue;
|
||||
});
|
||||
},
|
||||
),
|
||||
SettingsTile.switchTile(
|
||||
title: Text(translate('Enable recording session')),
|
||||
initialValue: _enableRecordSession,
|
||||
onToggle: (v) async {
|
||||
await bind.mainSetOption(
|
||||
key: "enable-record-session", value: v ? "" : "N");
|
||||
final newValue =
|
||||
await bind.mainGetOption(key: "enable-record-session") != "N";
|
||||
setState(() {
|
||||
_enableRecordSession = newValue;
|
||||
});
|
||||
},
|
||||
onToggle: isOptionFixed(kOptionEnableRecordSession)
|
||||
? null
|
||||
: (v) async {
|
||||
await bind.mainSetOption(
|
||||
key: kOptionEnableRecordSession,
|
||||
value: v ? defaultOptionYes : "N");
|
||||
final newValue =
|
||||
await bind.mainGetOption(key: kOptionEnableRecordSession) !=
|
||||
"N";
|
||||
setState(() {
|
||||
_enableRecordSession = newValue;
|
||||
});
|
||||
},
|
||||
),
|
||||
SettingsTile.switchTile(
|
||||
title: Row(
|
||||
@ -341,21 +352,27 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
||||
Icons.edit,
|
||||
size: 20,
|
||||
),
|
||||
onPressed: () async {
|
||||
final port = await changeDirectAccessPort(
|
||||
_localIP, _directAccessPort);
|
||||
setState(() {
|
||||
_directAccessPort = port;
|
||||
});
|
||||
}))
|
||||
onPressed: isOptionFixed(kOptionDirectAccessPort)
|
||||
? null
|
||||
: () async {
|
||||
final port = await changeDirectAccessPort(
|
||||
_localIP, _directAccessPort);
|
||||
setState(() {
|
||||
_directAccessPort = port;
|
||||
});
|
||||
}))
|
||||
]),
|
||||
initialValue: _enableDirectIPAccess,
|
||||
onToggle: (_) async {
|
||||
_enableDirectIPAccess = !_enableDirectIPAccess;
|
||||
String value = bool2option('direct-server', _enableDirectIPAccess);
|
||||
await bind.mainSetOption(key: 'direct-server', value: value);
|
||||
setState(() {});
|
||||
},
|
||||
onToggle: isOptionFixed(kOptionDirectServer)
|
||||
? null
|
||||
: (_) async {
|
||||
_enableDirectIPAccess = !_enableDirectIPAccess;
|
||||
String value =
|
||||
bool2option(kOptionDirectServer, _enableDirectIPAccess);
|
||||
await bind.mainSetOption(
|
||||
key: kOptionDirectServer, value: value);
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
SettingsTile.switchTile(
|
||||
title: Row(
|
||||
@ -382,22 +399,27 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
||||
Icons.edit,
|
||||
size: 20,
|
||||
),
|
||||
onPressed: () async {
|
||||
final timeout = await changeAutoDisconnectTimeout(
|
||||
_autoDisconnectTimeout);
|
||||
setState(() {
|
||||
_autoDisconnectTimeout = timeout;
|
||||
});
|
||||
}))
|
||||
onPressed: isOptionFixed(kOptionAutoDisconnectTimeout)
|
||||
? null
|
||||
: () async {
|
||||
final timeout = await changeAutoDisconnectTimeout(
|
||||
_autoDisconnectTimeout);
|
||||
setState(() {
|
||||
_autoDisconnectTimeout = timeout;
|
||||
});
|
||||
}))
|
||||
]),
|
||||
initialValue: _allowAutoDisconnect,
|
||||
onToggle: (_) async {
|
||||
_allowAutoDisconnect = !_allowAutoDisconnect;
|
||||
String value =
|
||||
bool2option('allow-auto-disconnect', _allowAutoDisconnect);
|
||||
await bind.mainSetOption(key: 'allow-auto-disconnect', value: value);
|
||||
setState(() {});
|
||||
},
|
||||
onToggle: isOptionFixed(kOptionAllowAutoDisconnect)
|
||||
? null
|
||||
: (_) async {
|
||||
_allowAutoDisconnect = !_allowAutoDisconnect;
|
||||
String value = bool2option(
|
||||
kOptionAllowAutoDisconnect, _allowAutoDisconnect);
|
||||
await bind.mainSetOption(
|
||||
key: kOptionAllowAutoDisconnect, value: value);
|
||||
setState(() {});
|
||||
},
|
||||
)
|
||||
];
|
||||
if (_hasIgnoreBattery) {
|
||||
@ -526,15 +548,19 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
||||
SettingsTile.switchTile(
|
||||
title: Text(translate('Enable hardware codec')),
|
||||
initialValue: _enableHardwareCodec,
|
||||
onToggle: (v) async {
|
||||
await bind.mainSetOption(
|
||||
key: "enable-hwcodec", value: v ? "" : "N");
|
||||
final newValue =
|
||||
await bind.mainGetOption(key: "enable-hwcodec") != "N";
|
||||
setState(() {
|
||||
_enableHardwareCodec = newValue;
|
||||
});
|
||||
},
|
||||
onToggle: isOptionFixed(kOptionEnableHwcodec)
|
||||
? null
|
||||
: (v) async {
|
||||
await bind.mainSetOption(
|
||||
key: kOptionEnableHwcodec,
|
||||
value: v ? defaultOptionYes : "N");
|
||||
final newValue =
|
||||
await bind.mainGetOption(key: kOptionEnableHwcodec) !=
|
||||
"N";
|
||||
setState(() {
|
||||
_enableHardwareCodec = newValue;
|
||||
});
|
||||
},
|
||||
),
|
||||
]),
|
||||
if (isAndroid && !outgoingOnly)
|
||||
@ -551,18 +577,21 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
||||
child: Text("${translate("Directory")}: ${data.data}")),
|
||||
future: bind.mainVideoSaveDirectory(root: false)),
|
||||
initialValue: _autoRecordIncomingSession,
|
||||
onToggle: (v) async {
|
||||
await bind.mainSetOption(
|
||||
key: "allow-auto-record-incoming",
|
||||
value: bool2option("allow-auto-record-incoming", v));
|
||||
final newValue = option2bool(
|
||||
'allow-auto-record-incoming',
|
||||
await bind.mainGetOption(
|
||||
key: 'allow-auto-record-incoming'));
|
||||
setState(() {
|
||||
_autoRecordIncomingSession = newValue;
|
||||
});
|
||||
},
|
||||
onToggle: isOptionFixed(kOptionAllowAutoRecordIncoming)
|
||||
? null
|
||||
: (v) async {
|
||||
await bind.mainSetOption(
|
||||
key: kOptionAllowAutoRecordIncoming,
|
||||
value:
|
||||
bool2option(kOptionAllowAutoRecordIncoming, v));
|
||||
final newValue = option2bool(
|
||||
kOptionAllowAutoRecordIncoming,
|
||||
await bind.mainGetOption(
|
||||
key: kOptionAllowAutoRecordIncoming));
|
||||
setState(() {
|
||||
_autoRecordIncomingSession = newValue;
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -661,29 +690,32 @@ void showServerSettings(OverlayDialogManager dialogManager) async {
|
||||
void showLanguageSettings(OverlayDialogManager dialogManager) async {
|
||||
try {
|
||||
final langs = json.decode(await bind.mainGetLangs()) as List<dynamic>;
|
||||
var lang = bind.mainGetLocalOption(key: "lang");
|
||||
var lang = bind.mainGetLocalOption(key: kCommConfKeyLang);
|
||||
dialogManager.show((setState, close, context) {
|
||||
setLang(v) async {
|
||||
if (lang != v) {
|
||||
setState(() {
|
||||
lang = v;
|
||||
});
|
||||
await bind.mainSetLocalOption(key: "lang", value: v);
|
||||
await bind.mainSetLocalOption(key: kCommConfKeyLang, value: v);
|
||||
HomePage.homeKey.currentState?.refreshPages();
|
||||
Future.delayed(Duration(milliseconds: 200), close);
|
||||
}
|
||||
}
|
||||
|
||||
final isOptFixed = isOptionFixed(kCommConfKeyLang);
|
||||
return CustomAlertDialog(
|
||||
content: Column(
|
||||
children: [
|
||||
getRadio(Text(translate('Default')), '', lang, setLang),
|
||||
getRadio(Text(translate('Default')), defaultOptionLang, lang,
|
||||
isOptFixed ? null : setLang),
|
||||
Divider(color: MyTheme.border),
|
||||
] +
|
||||
langs.map((e) {
|
||||
final key = e[0] as String;
|
||||
final name = e[1] as String;
|
||||
return getRadio(Text(translate(name)), key, lang, setLang);
|
||||
return getRadio(Text(translate(name)), key, lang,
|
||||
isOptFixed ? null : setLang);
|
||||
}).toList(),
|
||||
),
|
||||
);
|
||||
@ -707,13 +739,15 @@ void showThemeSettings(OverlayDialogManager dialogManager) async {
|
||||
}
|
||||
}
|
||||
|
||||
final isOptFixed = isOptionFixed(kCommConfKeyTheme);
|
||||
return CustomAlertDialog(
|
||||
content: Column(children: [
|
||||
getRadio(
|
||||
Text(translate('Light')), ThemeMode.light, themeMode, setTheme),
|
||||
getRadio(Text(translate('Dark')), ThemeMode.dark, themeMode, setTheme),
|
||||
getRadio(Text(translate('Light')), ThemeMode.light, themeMode,
|
||||
isOptFixed ? null : setTheme),
|
||||
getRadio(Text(translate('Dark')), ThemeMode.dark, themeMode,
|
||||
isOptFixed ? null : setTheme),
|
||||
getRadio(Text(translate('Follow System')), ThemeMode.system, themeMode,
|
||||
setTheme)
|
||||
isOptFixed ? null : setTheme)
|
||||
]),
|
||||
);
|
||||
}, backDismiss: true, clickMaskDismiss: true);
|
||||
@ -801,11 +835,14 @@ class __DisplayPageState extends State<_DisplayPage> {
|
||||
_RadioEntry('Scale original', kRemoteViewStyleOriginal),
|
||||
_RadioEntry('Scale adaptive', kRemoteViewStyleAdaptive)
|
||||
],
|
||||
getter: () => bind.mainGetUserDefaultOption(key: 'view_style'),
|
||||
asyncSetter: (value) async {
|
||||
await bind.mainSetUserDefaultOption(
|
||||
key: 'view_style', value: value);
|
||||
},
|
||||
getter: () =>
|
||||
bind.mainGetUserDefaultOption(key: kOptionViewStyle),
|
||||
asyncSetter: isOptionFixed(kOptionViewStyle)
|
||||
? null
|
||||
: (value) async {
|
||||
await bind.mainSetUserDefaultOption(
|
||||
key: kOptionViewStyle, value: value);
|
||||
},
|
||||
),
|
||||
_getPopupDialogRadioEntry(
|
||||
title: 'Default Image Quality',
|
||||
@ -816,16 +853,19 @@ class __DisplayPageState extends State<_DisplayPage> {
|
||||
_RadioEntry('Custom', kRemoteImageQualityCustom),
|
||||
],
|
||||
getter: () {
|
||||
final v = bind.mainGetUserDefaultOption(key: 'image_quality');
|
||||
final v =
|
||||
bind.mainGetUserDefaultOption(key: kOptionImageQuality);
|
||||
showCustomImageQuality.value = v == kRemoteImageQualityCustom;
|
||||
return v;
|
||||
},
|
||||
asyncSetter: (value) async {
|
||||
await bind.mainSetUserDefaultOption(
|
||||
key: 'image_quality', value: value);
|
||||
showCustomImageQuality.value =
|
||||
value == kRemoteImageQualityCustom;
|
||||
},
|
||||
asyncSetter: isOptionFixed(kOptionImageQuality)
|
||||
? null
|
||||
: (value) async {
|
||||
await bind.mainSetUserDefaultOption(
|
||||
key: kOptionImageQuality, value: value);
|
||||
showCustomImageQuality.value =
|
||||
value == kRemoteImageQualityCustom;
|
||||
},
|
||||
tail: customImageQualitySetting(),
|
||||
showTail: showCustomImageQuality,
|
||||
notCloseValue: kRemoteImageQualityCustom,
|
||||
@ -834,11 +874,13 @@ class __DisplayPageState extends State<_DisplayPage> {
|
||||
title: 'Default Codec',
|
||||
list: codecList,
|
||||
getter: () =>
|
||||
bind.mainGetUserDefaultOption(key: 'codec-preference'),
|
||||
asyncSetter: (value) async {
|
||||
await bind.mainSetUserDefaultOption(
|
||||
key: 'codec-preference', value: value);
|
||||
},
|
||||
bind.mainGetUserDefaultOption(key: kOptionCodecPreference),
|
||||
asyncSetter: isOptionFixed(kOptionCodecPreference)
|
||||
? null
|
||||
: (value) async {
|
||||
await bind.mainSetUserDefaultOption(
|
||||
key: kOptionCodecPreference, value: value);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -853,13 +895,17 @@ class __DisplayPageState extends State<_DisplayPage> {
|
||||
|
||||
SettingsTile otherRow(String label, String key) {
|
||||
final value = bind.mainGetUserDefaultOption(key: key) == 'Y';
|
||||
final isOptFixed = isOptionFixed(key);
|
||||
return SettingsTile.switchTile(
|
||||
initialValue: value,
|
||||
title: Text(translate(label)),
|
||||
onToggle: (b) async {
|
||||
await bind.mainSetUserDefaultOption(key: key, value: b ? 'Y' : '');
|
||||
setState(() {});
|
||||
},
|
||||
onToggle: isOptFixed
|
||||
? null
|
||||
: (b) async {
|
||||
await bind.mainSetUserDefaultOption(
|
||||
key: key, value: b ? 'Y' : defaultOptionNo);
|
||||
setState(() {});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -877,7 +923,7 @@ _getPopupDialogRadioEntry({
|
||||
required String title,
|
||||
required List<_RadioEntry> list,
|
||||
required _RadioEntryGetter getter,
|
||||
required _RadioEntrySetter asyncSetter,
|
||||
required _RadioEntrySetter? asyncSetter,
|
||||
Widget? tail,
|
||||
RxBool? showTail,
|
||||
String? notCloseValue,
|
||||
@ -897,21 +943,23 @@ _getPopupDialogRadioEntry({
|
||||
|
||||
void showDialog() async {
|
||||
gFFI.dialogManager.show((setState, close, context) {
|
||||
onChanged(String? value) async {
|
||||
if (value == null) return;
|
||||
await asyncSetter(value);
|
||||
init();
|
||||
if (value != notCloseValue) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
final onChanged = asyncSetter == null
|
||||
? null
|
||||
: (String? value) async {
|
||||
if (value == null) return;
|
||||
await asyncSetter(value);
|
||||
init();
|
||||
if (value != notCloseValue) {
|
||||
close();
|
||||
}
|
||||
};
|
||||
|
||||
return CustomAlertDialog(
|
||||
content: Obx(
|
||||
() => Column(children: [
|
||||
...list
|
||||
.map((e) => getRadio(Text(translate(e.label)), e.value,
|
||||
groupValue.value, (String? value) => onChanged(value)))
|
||||
groupValue.value, onChanged))
|
||||
.toList(),
|
||||
Offstage(
|
||||
offstage:
|
||||
|
@ -509,7 +509,7 @@ class AbModel {
|
||||
}
|
||||
|
||||
void setShouldAsync(bool v) async {
|
||||
await bind.mainSetLocalOption(key: syncAbOption, value: v ? 'Y' : '');
|
||||
await bind.mainSetLocalOption(key: syncAbOption, value: v ? 'Y' : defaultOptionNo);
|
||||
_syncAllFromRecent = true;
|
||||
_timerCounter = 0;
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ class ServerModel with ChangeNotifier {
|
||||
String get approveMode => _approveMode;
|
||||
|
||||
setVerificationMethod(String method) async {
|
||||
await bind.mainSetOption(key: "verification-method", value: method);
|
||||
await bind.mainSetOption(key: kOptionVerificationMethod, value: method);
|
||||
/*
|
||||
if (method != kUsePermanentPassword) {
|
||||
await bind.mainSetOption(
|
||||
@ -99,7 +99,7 @@ class ServerModel with ChangeNotifier {
|
||||
}
|
||||
|
||||
setApproveMode(String mode) async {
|
||||
await bind.mainSetOption(key: 'approve-mode', value: mode);
|
||||
await bind.mainSetOption(key: kOptionApproveMode, value: mode);
|
||||
/*
|
||||
if (mode != 'password') {
|
||||
await bind.mainSetOption(
|
||||
@ -283,7 +283,7 @@ class ServerModel with ChangeNotifier {
|
||||
}
|
||||
|
||||
_audioOk = !_audioOk;
|
||||
bind.mainSetOption(key: "enable-audio", value: _audioOk ? '' : 'N');
|
||||
bind.mainSetOption(key: "enable-audio", value: _audioOk ? defaultOptionYes : 'N');
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@ -302,7 +302,7 @@ class ServerModel with ChangeNotifier {
|
||||
}
|
||||
|
||||
_fileOk = !_fileOk;
|
||||
bind.mainSetOption(key: "enable-file-transfer", value: _fileOk ? '' : 'N');
|
||||
bind.mainSetOption(key: kOptionEnableFileTransfer, value: _fileOk ? defaultOptionYes : 'N');
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@ -312,7 +312,7 @@ class ServerModel with ChangeNotifier {
|
||||
}
|
||||
if (_inputOk) {
|
||||
parent.target?.invokeMethod("stop_input");
|
||||
bind.mainSetOption(key: "enable-keyboard", value: 'N');
|
||||
bind.mainSetOption(key: kOptionEnableKeyboard, value: 'N');
|
||||
} else {
|
||||
if (parent.target != null) {
|
||||
/// the result of toggle-on depends on user actions in the settings page.
|
||||
@ -445,7 +445,7 @@ class ServerModel with ChangeNotifier {
|
||||
break;
|
||||
case "input":
|
||||
if (_inputOk != value) {
|
||||
bind.mainSetOption(key: "enable-keyboard", value: value ? '' : 'N');
|
||||
bind.mainSetOption(key: kOptionEnableKeyboard, value: value ? defaultOptionYes : 'N');
|
||||
}
|
||||
_inputOk = value;
|
||||
break;
|
||||
|
@ -1610,5 +1610,9 @@ class RustdeskImpl {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
bool mainIsOptionFixed({required String key, dynamic hint}) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
void dispose() {}
|
||||
}
|
||||
|
@ -916,7 +916,7 @@ impl Config {
|
||||
|
||||
#[inline]
|
||||
fn purify_options(v: &mut HashMap<String, String>) {
|
||||
v.retain(|k, v| is_option_can_save(&OVERWRITE_SETTINGS, &DEFAULT_SETTINGS, k, v));
|
||||
v.retain(|k, _| is_option_can_save(&OVERWRITE_SETTINGS, k));
|
||||
}
|
||||
|
||||
pub fn set_options(mut v: HashMap<String, String>) {
|
||||
@ -940,7 +940,7 @@ impl Config {
|
||||
}
|
||||
|
||||
pub fn set_option(k: String, v: String) {
|
||||
if !is_option_can_save(&OVERWRITE_SETTINGS, &DEFAULT_SETTINGS, &k, &v) {
|
||||
if !is_option_can_save(&OVERWRITE_SETTINGS, &k) {
|
||||
return;
|
||||
}
|
||||
let mut config = CONFIG2.write().unwrap();
|
||||
@ -1194,36 +1194,36 @@ impl PeerConfig {
|
||||
serde_field_string!(
|
||||
default_view_style,
|
||||
deserialize_view_style,
|
||||
UserDefaultConfig::read("view_style")
|
||||
UserDefaultConfig::read(keys::OPTION_VIEW_STYLE)
|
||||
);
|
||||
serde_field_string!(
|
||||
default_scroll_style,
|
||||
deserialize_scroll_style,
|
||||
UserDefaultConfig::read("scroll_style")
|
||||
UserDefaultConfig::read(keys::OPTION_SCROLL_STYLE)
|
||||
);
|
||||
serde_field_string!(
|
||||
default_image_quality,
|
||||
deserialize_image_quality,
|
||||
UserDefaultConfig::read("image_quality")
|
||||
UserDefaultConfig::read(keys::OPTION_IMAGE_QUALITY)
|
||||
);
|
||||
serde_field_string!(
|
||||
default_reverse_mouse_wheel,
|
||||
deserialize_reverse_mouse_wheel,
|
||||
UserDefaultConfig::read("reverse_mouse_wheel")
|
||||
UserDefaultConfig::read(keys::OPTION_REVERSE_MOUSE_WHEEL)
|
||||
);
|
||||
serde_field_string!(
|
||||
default_displays_as_individual_windows,
|
||||
deserialize_displays_as_individual_windows,
|
||||
UserDefaultConfig::read("displays_as_individual_windows")
|
||||
UserDefaultConfig::read(keys::OPTION_DISPLAYS_AS_INDIVIDUAL_WINDOWS)
|
||||
);
|
||||
serde_field_string!(
|
||||
default_use_all_my_displays_for_the_remote_session,
|
||||
deserialize_use_all_my_displays_for_the_remote_session,
|
||||
UserDefaultConfig::read("use_all_my_displays_for_the_remote_session")
|
||||
UserDefaultConfig::read(keys::OPTION_USE_ALL_MY_DISPLAYS_FOR_THE_REMOTE_SESSION)
|
||||
);
|
||||
|
||||
fn default_custom_image_quality() -> Vec<i32> {
|
||||
let f: f64 = UserDefaultConfig::read("custom_image_quality")
|
||||
let f: f64 = UserDefaultConfig::read(keys::OPTION_CUSTOM_IMAGE_QUALITY)
|
||||
.parse()
|
||||
.unwrap_or(50.0);
|
||||
vec![f as _]
|
||||
@ -1244,12 +1244,12 @@ impl PeerConfig {
|
||||
fn default_options() -> HashMap<String, String> {
|
||||
let mut mp: HashMap<String, String> = Default::default();
|
||||
[
|
||||
"codec-preference",
|
||||
"custom-fps",
|
||||
"zoom-cursor",
|
||||
"touch-mode",
|
||||
"i444",
|
||||
"swap-left-right-mouse",
|
||||
keys::OPTION_CODEC_PREFERENCE,
|
||||
keys::OPTION_CUSTOM_FPS,
|
||||
keys::OPTION_ZOOM_CURSOR,
|
||||
keys::OPTION_TOUCH_MODE,
|
||||
keys::OPTION_I444,
|
||||
keys::OPTION_SWAP_LEFT_RIGHT_MOUSE,
|
||||
]
|
||||
.map(|key| {
|
||||
mp.insert(key.to_owned(), UserDefaultConfig::read(key));
|
||||
@ -1415,10 +1415,17 @@ impl LocalConfig {
|
||||
}
|
||||
|
||||
pub fn set_option(k: String, v: String) {
|
||||
if !is_option_can_save(&OVERWRITE_LOCAL_SETTINGS, &DEFAULT_LOCAL_SETTINGS, &k, &v) {
|
||||
if !is_option_can_save(&OVERWRITE_LOCAL_SETTINGS, &k) {
|
||||
return;
|
||||
}
|
||||
let mut config = LOCAL_CONFIG.write().unwrap();
|
||||
// The custom client will explictly set "default" as the default language.
|
||||
let is_custom_client_default_lang = k == keys::OPTION_LANGUAGE && v == "default";
|
||||
if is_custom_client_default_lang {
|
||||
config.options.insert(k, "".to_owned());
|
||||
config.store();
|
||||
return;
|
||||
}
|
||||
let v2 = if v.is_empty() { None } else { Some(&v) };
|
||||
if v2 != config.options.get(&k) {
|
||||
if v2.is_none() {
|
||||
@ -1572,15 +1579,19 @@ impl UserDefaultConfig {
|
||||
|
||||
pub fn get(&self, key: &str) -> String {
|
||||
match key {
|
||||
"view_style" => self.get_string(key, "original", vec!["adaptive"]),
|
||||
"scroll_style" => self.get_string(key, "scrollauto", vec!["scrollbar"]),
|
||||
"image_quality" => self.get_string(key, "balanced", vec!["best", "low", "custom"]),
|
||||
"codec-preference" => {
|
||||
keys::OPTION_VIEW_STYLE => self.get_string(key, "original", vec!["adaptive"]),
|
||||
keys::OPTION_SCROLL_STYLE => self.get_string(key, "scrollauto", vec!["scrollbar"]),
|
||||
keys::OPTION_IMAGE_QUALITY => {
|
||||
self.get_string(key, "balanced", vec!["best", "low", "custom"])
|
||||
}
|
||||
keys::OPTION_CODEC_PREFERENCE => {
|
||||
self.get_string(key, "auto", vec!["vp8", "vp9", "av1", "h264", "h265"])
|
||||
}
|
||||
"custom_image_quality" => self.get_double_string(key, 50.0, 10.0, 0xFFF as f64),
|
||||
"custom-fps" => self.get_double_string(key, 30.0, 5.0, 120.0),
|
||||
"enable_file_transfer" => self.get_string(key, "Y", vec![""]),
|
||||
keys::OPTION_CUSTOM_IMAGE_QUALITY => {
|
||||
self.get_double_string(key, 50.0, 10.0, 0xFFF as f64)
|
||||
}
|
||||
keys::OPTION_CUSTOM_FPS => self.get_double_string(key, 30.0, 5.0, 120.0),
|
||||
keys::OPTION_ENABLE_FILE_TRANSFER => self.get_string(key, "Y", vec!["", "N"]),
|
||||
_ => self
|
||||
.get_after(key)
|
||||
.map(|v| v.to_string())
|
||||
@ -1589,12 +1600,7 @@ impl UserDefaultConfig {
|
||||
}
|
||||
|
||||
pub fn set(&mut self, key: String, value: String) {
|
||||
if !is_option_can_save(
|
||||
&OVERWRITE_DISPLAY_SETTINGS,
|
||||
&DEFAULT_DISPLAY_SETTINGS,
|
||||
&key,
|
||||
&value,
|
||||
) {
|
||||
if !is_option_can_save(&OVERWRITE_DISPLAY_SETTINGS, &key) {
|
||||
return;
|
||||
}
|
||||
if value.is_empty() {
|
||||
@ -1917,18 +1923,10 @@ fn get_or(
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_option_can_save(
|
||||
overwrite: &RwLock<HashMap<String, String>>,
|
||||
defaults: &RwLock<HashMap<String, String>>,
|
||||
k: &str,
|
||||
v: &str,
|
||||
) -> bool {
|
||||
fn is_option_can_save(overwrite: &RwLock<HashMap<String, String>>, k: &str) -> bool {
|
||||
if overwrite.read().unwrap().contains_key(k) {
|
||||
return false;
|
||||
}
|
||||
if defaults.read().unwrap().get(k).map_or(false, |x| x == v) {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
@ -1984,6 +1982,136 @@ pub fn is_disable_installation() -> bool {
|
||||
is_some_hard_opton("disable-installation")
|
||||
}
|
||||
|
||||
pub mod keys {
|
||||
pub const OPTION_VIEW_ONLY: &str = "view_only";
|
||||
pub const OPTION_SHOW_MONITORS_TOOLBAR: &str = "show_monitors_toolbar";
|
||||
pub const OPTION_COLLAPSE_TOOLBAR: &str = "collapse_toolbar";
|
||||
pub const OPTION_SHOW_REMOTE_CURSOR: &str = "show_remote_cursor";
|
||||
pub const OPTION_FOLLOW_REMOTE_CURSOR: &str = "follow_remote_cursor";
|
||||
pub const OPTION_FOLLOW_REMOTE_WINDOW: &str = "follow_remote_window";
|
||||
pub const OPTION_ZOOM_CURSOR: &str = "zoom-cursor";
|
||||
pub const OPTION_SHOW_QUALITY_MONITOR: &str = "show_quality_monitor";
|
||||
pub const OPTION_DISABLE_AUDIO: &str = "disable_audio";
|
||||
pub const OPTION_DISABLE_CLIPBOARD: &str = "disable_clipboard";
|
||||
pub const OPTION_LOCK_AFTER_SESSION_END: &str = "lock_after_session_end";
|
||||
pub const OPTION_PRIVACY_MODE: &str = "privacy_mode";
|
||||
pub const OPTION_TOUCH_MODE: &str = "touch-mode";
|
||||
pub const OPTION_I444: &str = "i444";
|
||||
pub const OPTION_REVERSE_MOUSE_WHEEL: &str = "reverse_mouse_wheel";
|
||||
pub const OPTION_SWAP_LEFT_RIGHT_MOUSE: &str = "swap-left-right-mouse";
|
||||
pub const OPTION_DISPLAYS_AS_INDIVIDUAL_WINDOWS: &str = "displays_as_individual_windows";
|
||||
pub const OPTION_USE_ALL_MY_DISPLAYS_FOR_THE_REMOTE_SESSION: &str =
|
||||
"use_all_my_displays_for_the_remote_session";
|
||||
pub const OPTION_VIEW_STYLE: &str = "view_style";
|
||||
pub const OPTION_SCROLL_STYLE: &str = "scroll_style";
|
||||
pub const OPTION_IMAGE_QUALITY: &str = "image_quality";
|
||||
pub const OPTION_CUSTOM_IMAGE_QUALITY: &str = "custom_image_quality";
|
||||
pub const OPTION_CUSTOM_FPS: &str = "custom-fps";
|
||||
pub const OPTION_CODEC_PREFERENCE: &str = "codec-preference";
|
||||
pub const OPTION_THEME: &str = "theme";
|
||||
pub const OPTION_LANGUAGE: &str = "lang";
|
||||
pub const OPTION_ENABLE_CONFIRM_CLOSING_TABS: &str = "enable-confirm-closing-tabs";
|
||||
pub const OPTION_ENABLE_OPEN_NEW_CONNECTIONS_IN_TABS: &str =
|
||||
"enable-open-new-connections-in-tabs";
|
||||
pub const OPTION_ENABLE_CHECK_UPDATE: &str = "enable-check-update";
|
||||
pub const OPTION_SYNC_AB_WITH_RECENT_SESSIONS: &str = "sync-ab-with-recent-sessions";
|
||||
pub const OPTION_SYNC_AB_TAGS: &str = "sync-ab-tags";
|
||||
pub const OPTION_FILTER_AB_BY_INTERSECTION: &str = "filter-ab-by-intersection";
|
||||
pub const OPTION_ACCESS_MODE: &str = "access-mode";
|
||||
pub const OPTION_ENABLE_KEYBOARD: &str = "enable-keyboard";
|
||||
pub const OPTION_ENABLE_CLIPBOARD: &str = "enable-clipboard";
|
||||
pub const OPTION_ENABLE_FILE_TRANSFER: &str = "enable-file-transfer";
|
||||
pub const OPTION_ENABLE_AUDIO: &str = "enable-audio";
|
||||
pub const OPTION_ENABLE_TUNNEL: &str = "enable-tunnel";
|
||||
pub const OPTION_ENABLE_REMOTE_RESTART: &str = "enable-remote-restart";
|
||||
pub const OPTION_ENABLE_RECORD_SESSION: &str = "enable-record-session";
|
||||
pub const OPTION_ENABLE_BLOCK_INPUT: &str = "enable-block-input";
|
||||
pub const OPTION_ALLOW_REMOTE_CONFIG_MODIFICATION: &str = "allow-remote-config-modification";
|
||||
pub const OPTION_ENABLE_LAN_DISCOVERY: &str = "enable-lan-discovery";
|
||||
pub const OPTION_DIRECT_SERVER: &str = "direct-server";
|
||||
pub const OPTION_DIRECT_ACCESS_PORT: &str = "direct-access-port";
|
||||
pub const OPTION_WHITELIST: &str = "whitelist";
|
||||
pub const OPTION_ALLOW_AUTO_DISCONNECT: &str = "allow-auto-disconnect";
|
||||
pub const OPTION_AUTO_DISCONNECT_TIMEOUT: &str = "auto-disconnect-timeout";
|
||||
pub const OPTION_ALLOW_ONLY_CONN_WINDOW_OPEN: &str = "allow-only-conn-window-open";
|
||||
pub const OPTION_ALLOW_AUTO_RECORD_INCOMING: &str = "allow-auto-record-incoming";
|
||||
pub const OPTION_VIDEO_SAVE_DIRECTORY: &str = "video-save-directory";
|
||||
pub const OPTION_ENABLE_ABR: &str = "enable-abr";
|
||||
pub const OPTION_ALLOW_REMOVE_WALLPAPER: &str = "allow-remove-wallpaper";
|
||||
pub const OPTION_ALLOW_ALWAYS_SOFTWARE_RENDER: &str = "allow-always-software-render";
|
||||
pub const OPTION_ALLOW_LINUX_HEADLESS: &str = "allow-linux-headless";
|
||||
pub const OPTION_ENABLE_HWCODEC: &str = "enable-hwcodec";
|
||||
pub const OPTION_APPROVE_MODE: &str = "approve-mode";
|
||||
|
||||
// DEFAULT_DISPLAY_SETTINGS, OVERWRITE_DISPLAY_SETTINGS
|
||||
pub const KEYS_DISPLAY_SETTINGS: &[&str] = &[
|
||||
OPTION_VIEW_ONLY,
|
||||
OPTION_SHOW_MONITORS_TOOLBAR,
|
||||
OPTION_COLLAPSE_TOOLBAR,
|
||||
OPTION_SHOW_REMOTE_CURSOR,
|
||||
OPTION_FOLLOW_REMOTE_CURSOR,
|
||||
OPTION_FOLLOW_REMOTE_WINDOW,
|
||||
OPTION_ZOOM_CURSOR,
|
||||
OPTION_SHOW_QUALITY_MONITOR,
|
||||
OPTION_DISABLE_AUDIO,
|
||||
OPTION_ENABLE_FILE_TRANSFER,
|
||||
OPTION_DISABLE_CLIPBOARD,
|
||||
OPTION_LOCK_AFTER_SESSION_END,
|
||||
OPTION_PRIVACY_MODE,
|
||||
OPTION_TOUCH_MODE,
|
||||
OPTION_I444,
|
||||
OPTION_REVERSE_MOUSE_WHEEL,
|
||||
OPTION_SWAP_LEFT_RIGHT_MOUSE,
|
||||
OPTION_DISPLAYS_AS_INDIVIDUAL_WINDOWS,
|
||||
OPTION_USE_ALL_MY_DISPLAYS_FOR_THE_REMOTE_SESSION,
|
||||
OPTION_VIEW_STYLE,
|
||||
OPTION_SCROLL_STYLE,
|
||||
OPTION_IMAGE_QUALITY,
|
||||
OPTION_CUSTOM_IMAGE_QUALITY,
|
||||
OPTION_CUSTOM_FPS,
|
||||
OPTION_CODEC_PREFERENCE,
|
||||
];
|
||||
// DEFAULT_LOCAL_SETTINGS, OVERWRITE_LOCAL_SETTINGS
|
||||
pub const KEYS_LOCAL_SETTINGS: &[&str] = &[
|
||||
OPTION_THEME,
|
||||
OPTION_LANGUAGE,
|
||||
OPTION_ENABLE_CONFIRM_CLOSING_TABS,
|
||||
OPTION_ENABLE_OPEN_NEW_CONNECTIONS_IN_TABS,
|
||||
OPTION_ENABLE_CHECK_UPDATE,
|
||||
OPTION_SYNC_AB_WITH_RECENT_SESSIONS,
|
||||
OPTION_SYNC_AB_TAGS,
|
||||
OPTION_FILTER_AB_BY_INTERSECTION,
|
||||
];
|
||||
// DEFAULT_SETTINGS, OVERWRITE_SETTINGS
|
||||
pub const KEYS_SETTINGS: &[&str] = &[
|
||||
OPTION_ACCESS_MODE,
|
||||
OPTION_ENABLE_KEYBOARD,
|
||||
OPTION_ENABLE_CLIPBOARD,
|
||||
OPTION_ENABLE_FILE_TRANSFER,
|
||||
OPTION_ENABLE_AUDIO,
|
||||
OPTION_ENABLE_TUNNEL,
|
||||
OPTION_ENABLE_REMOTE_RESTART,
|
||||
OPTION_ENABLE_RECORD_SESSION,
|
||||
OPTION_ENABLE_BLOCK_INPUT,
|
||||
OPTION_ALLOW_REMOTE_CONFIG_MODIFICATION,
|
||||
OPTION_ENABLE_LAN_DISCOVERY,
|
||||
OPTION_DIRECT_SERVER,
|
||||
OPTION_DIRECT_ACCESS_PORT,
|
||||
OPTION_WHITELIST,
|
||||
OPTION_ALLOW_AUTO_DISCONNECT,
|
||||
OPTION_AUTO_DISCONNECT_TIMEOUT,
|
||||
OPTION_ALLOW_ONLY_CONN_WINDOW_OPEN,
|
||||
OPTION_ALLOW_AUTO_RECORD_INCOMING,
|
||||
OPTION_VIDEO_SAVE_DIRECTORY,
|
||||
OPTION_ENABLE_ABR,
|
||||
OPTION_ALLOW_REMOVE_WALLPAPER,
|
||||
OPTION_ALLOW_ALWAYS_SOFTWARE_RENDER,
|
||||
OPTION_ALLOW_LINUX_HEADLESS,
|
||||
OPTION_ENABLE_HWCODEC,
|
||||
OPTION_APPROVE_MODE,
|
||||
];
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@ -2022,6 +2150,10 @@ mod tests {
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert("b".to_string(), "c".to_string());
|
||||
OVERWRITE_SETTINGS
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert("c".to_string(), "f".to_string());
|
||||
OVERWRITE_SETTINGS
|
||||
.write()
|
||||
.unwrap()
|
||||
@ -2042,7 +2174,7 @@ mod tests {
|
||||
res.insert("d".to_owned(), "c".to_string());
|
||||
res.insert("c".to_owned(), "a".to_string());
|
||||
res.insert("f".to_owned(), "a".to_string());
|
||||
res.insert("c".to_owned(), "d".to_string());
|
||||
res.insert("e".to_owned(), "d".to_string());
|
||||
Config::purify_options(&mut res);
|
||||
assert!(res.len() == 2);
|
||||
res.insert("b".to_owned(), "c".to_string());
|
||||
@ -2055,11 +2187,11 @@ mod tests {
|
||||
assert!(res.len() == 2);
|
||||
let res = Config::get_options();
|
||||
assert!(res["a"] == "b");
|
||||
assert!(res["c"] == "a");
|
||||
assert!(res["c"] == "f");
|
||||
assert!(res["b"] == "c");
|
||||
assert!(res["d"] == "c");
|
||||
assert!(Config::get_option("a") == "b");
|
||||
assert!(Config::get_option("c") == "a");
|
||||
assert!(Config::get_option("c") == "f");
|
||||
assert!(Config::get_option("b") == "c");
|
||||
assert!(Config::get_option("d") == "c");
|
||||
DEFAULT_SETTINGS.write().unwrap().clear();
|
||||
|
@ -1,5 +1,6 @@
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::HashMap,
|
||||
future::Future,
|
||||
sync::{Arc, Mutex, RwLock},
|
||||
task::Poll,
|
||||
@ -1597,22 +1598,41 @@ pub fn read_custom_client(config: &str) {
|
||||
*config::APP_NAME.write().unwrap() = app_name.to_owned();
|
||||
}
|
||||
}
|
||||
|
||||
let mut map_display_settings = HashMap::new();
|
||||
for s in config::keys::KEYS_DISPLAY_SETTINGS {
|
||||
map_display_settings.insert(s.replace("_", "-"), s);
|
||||
}
|
||||
let mut map_local_settings = HashMap::new();
|
||||
for s in config::keys::KEYS_LOCAL_SETTINGS {
|
||||
map_local_settings.insert(s.replace("_", "-"), s);
|
||||
}
|
||||
let mut map_settings = HashMap::new();
|
||||
for s in config::keys::KEYS_SETTINGS {
|
||||
map_settings.insert(s.replace("_", "-"), s);
|
||||
}
|
||||
|
||||
if let Some(default_settings) = data.remove("default-settings") {
|
||||
if let Some(default_settings) = default_settings.as_object() {
|
||||
for (k, v) in default_settings {
|
||||
let Some(v) = v.as_str() else {
|
||||
continue;
|
||||
};
|
||||
if k.starts_with("$$") {
|
||||
if let Some(k2) = map_display_settings.get(k) {
|
||||
config::DEFAULT_DISPLAY_SETTINGS
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(k.clone(), v[2..].to_owned());
|
||||
} else if k.starts_with("$") {
|
||||
.insert(k2.to_string(), v.to_owned());
|
||||
} else if let Some(k2) = map_local_settings.get(k) {
|
||||
config::DEFAULT_LOCAL_SETTINGS
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(k.clone(), v[1..].to_owned());
|
||||
.insert(k2.to_string(), v.to_owned());
|
||||
} else if let Some(k2) = map_settings.get(k) {
|
||||
config::DEFAULT_SETTINGS
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(k2.to_string(), v.to_owned());
|
||||
} else {
|
||||
config::DEFAULT_SETTINGS
|
||||
.write()
|
||||
@ -1628,16 +1648,21 @@ pub fn read_custom_client(config: &str) {
|
||||
let Some(v) = v.as_str() else {
|
||||
continue;
|
||||
};
|
||||
if k.starts_with("$$") {
|
||||
if let Some(k2) = map_display_settings.get(k) {
|
||||
config::OVERWRITE_DISPLAY_SETTINGS
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(k.clone(), v[2..].to_owned());
|
||||
} else if k.starts_with("$") {
|
||||
.insert(k2.to_string(), v.to_owned());
|
||||
} else if let Some(k2) = map_local_settings.get(k) {
|
||||
config::OVERWRITE_LOCAL_SETTINGS
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(k.clone(), v[1..].to_owned());
|
||||
.insert(k2.to_string(), v.to_owned());
|
||||
} else if let Some(k2) = map_settings.get(k) {
|
||||
config::OVERWRITE_SETTINGS
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(k2.to_string(), v.to_owned());
|
||||
} else {
|
||||
config::OVERWRITE_SETTINGS
|
||||
.write()
|
||||
|
@ -939,7 +939,6 @@ pub fn main_handle_wayland_screencast_restore_token(_key: String, _value: String
|
||||
} else {
|
||||
"".to_owned()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn main_get_input_source() -> SyncReturn<String> {
|
||||
@ -1194,6 +1193,23 @@ pub fn main_handle_relay_id(id: String) -> String {
|
||||
handle_relay_id(&id).to_owned()
|
||||
}
|
||||
|
||||
pub fn main_is_option_fixed(key: String) -> SyncReturn<bool> {
|
||||
SyncReturn(
|
||||
config::OVERWRITE_DISPLAY_SETTINGS
|
||||
.read()
|
||||
.unwrap()
|
||||
.contains_key(&key)
|
||||
|| config::OVERWRITE_LOCAL_SETTINGS
|
||||
.read()
|
||||
.unwrap()
|
||||
.contains_key(&key)
|
||||
|| config::OVERWRITE_SETTINGS
|
||||
.read()
|
||||
.unwrap()
|
||||
.contains_key(&key),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn main_get_main_display() -> SyncReturn<String> {
|
||||
#[cfg(target_os = "ios")]
|
||||
let display_info = "".to_owned();
|
||||
|
Loading…
Reference in New Issue
Block a user