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