refact: custom client, more advanced settings (#8085)

* refact: custom client, more advanced settings

Signed-off-by: fufesou <shuanglongchen@yeah.net>

* feat: custom client, more advanced settings

Signed-off-by: fufesou <shuanglongchen@yeah.net>

---------

Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
fufesou 2024-05-18 23:13:54 +08:00 committed by GitHub
parent c2b7810c33
commit 96f41fcc02
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 356 additions and 258 deletions

View File

@ -1408,7 +1408,7 @@ bool option2bool(String option, String value) {
res = value != "N"; res = value != "N";
} else if (option.startsWith("allow-") || } else if (option.startsWith("allow-") ||
option == "stop-service" || option == "stop-service" ||
option == "direct-server" || option == kOptionDirectServer ||
option == "stop-rendezvous-service" || option == "stop-rendezvous-service" ||
option == kOptionForceAlwaysRelay) { option == kOptionForceAlwaysRelay) {
res = value == "Y"; res = value == "Y";
@ -1425,7 +1425,7 @@ String bool2option(String option, bool b) {
res = b ? defaultOptionYes : '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 == kOptionDirectServer ||
option == "stop-rendezvous-service" || option == "stop-rendezvous-service" ||
option == kOptionForceAlwaysRelay) { option == kOptionForceAlwaysRelay) {
res = b ? 'Y' : defaultOptionNo; res = b ? 'Y' : defaultOptionNo;

View File

@ -341,7 +341,7 @@ initSharedStates(String id) {
ShowRemoteCursorLockState.init(id); ShowRemoteCursorLockState.init(id);
RemoteCursorMovedState.init(id); RemoteCursorMovedState.init(id);
FingerprintState.init(id); FingerprintState.init(id);
PeerBoolOption.init(id, 'zoom-cursor', () => false); PeerBoolOption.init(id, kOptionZoomCursor, () => false);
UnreadChatCountState.init(id); UnreadChatCountState.init(id);
if (isMobile) ConnectionTypeState.init(id); // desktop in other places if (isMobile) ConnectionTypeState.init(id); // desktop in other places
} }
@ -355,7 +355,7 @@ removeSharedStates(String id) {
KeyboardEnabledState.delete(id); KeyboardEnabledState.delete(id);
RemoteCursorMovedState.delete(id); RemoteCursorMovedState.delete(id);
FingerprintState.delete(id); FingerprintState.delete(id);
PeerBoolOption.delete(id, 'zoom-cursor'); PeerBoolOption.delete(id, kOptionZoomCursor);
UnreadChatCountState.delete(id); UnreadChatCountState.delete(id);
if (isMobile) ConnectionTypeState.delete(id); if (isMobile) ConnectionTypeState.delete(id);
} }

View File

@ -7,6 +7,7 @@ import 'package:flutter_hbb/common/formatter/id_formatter.dart';
import 'package:flutter_hbb/common/hbbs/hbbs.dart'; import 'package:flutter_hbb/common/hbbs/hbbs.dart';
import 'package:flutter_hbb/common/widgets/peer_card.dart'; import 'package:flutter_hbb/common/widgets/peer_card.dart';
import 'package:flutter_hbb/common/widgets/peers_view.dart'; import 'package:flutter_hbb/common/widgets/peers_view.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/desktop/widgets/popup_menu.dart'; import 'package:flutter_hbb/desktop/widgets/popup_menu.dart';
import 'package:flutter_hbb/models/ab_model.dart'; import 'package:flutter_hbb/models/ab_model.dart';
import 'package:flutter_hbb/models/platform_model.dart'; import 'package:flutter_hbb/models/platform_model.dart';
@ -191,12 +192,15 @@ class _AddressBookState extends State<AddressBook> {
} }
final TextEditingController textEditingController = TextEditingController(); final TextEditingController textEditingController = TextEditingController();
final isOptFixed = isOptionFixed(kOptionCurrentAbName);
return DropdownButton2<String>( return DropdownButton2<String>(
value: gFFI.abModel.currentName.value, value: gFFI.abModel.currentName.value,
onChanged: (value) { onChanged: isOptFixed
? null
: (value) {
if (value != null) { if (value != null) {
gFFI.abModel.setCurrentName(value); gFFI.abModel.setCurrentName(value);
bind.setLocalFlutterOption(k: 'current-ab-name', v: value); bind.setLocalFlutterOption(k: kOptionCurrentAbName, v: value);
} }
}, },
underline: Container( underline: Container(
@ -358,7 +362,8 @@ class _AddressBookState extends State<AddressBook> {
return shouldSortTags(); return shouldSortTags();
}, },
setter: (bool v) async { setter: (bool v) async {
bind.mainSetLocalOption(key: sortAbTagsOption, value: v ? 'Y' : defaultOptionNo); bind.mainSetLocalOption(
key: sortAbTagsOption, value: v ? 'Y' : defaultOptionNo);
gFFI.abModel.sortTags.value = v; gFFI.abModel.sortTags.value = v;
}, },
dismissOnClicked: true, dismissOnClicked: true,
@ -376,7 +381,8 @@ class _AddressBookState extends State<AddressBook> {
return filterAbTagByIntersection(); return filterAbTagByIntersection();
}, },
setter: (bool v) async { setter: (bool v) async {
bind.mainSetLocalOption(key: filterAbTagOption, value: v ? 'Y' : defaultOptionNo); bind.mainSetLocalOption(
key: filterAbTagOption, value: v ? 'Y' : defaultOptionNo);
gFFI.abModel.filterByIntersection.value = v; gFFI.abModel.filterByIntersection.value = v;
}, },
dismissOnClicked: true, dismissOnClicked: true,

View File

@ -204,6 +204,7 @@ void changeWhiteList({Function()? callback}) async {
errorText: msg.isEmpty ? null : translate(msg), errorText: msg.isEmpty ? null : translate(msg),
), ),
controller: controller, controller: controller,
enabled: !isOptFixed,
autofocus: true), autofocus: true),
), ),
], ],
@ -217,15 +218,15 @@ void changeWhiteList({Function()? callback}) async {
), ),
actions: [ actions: [
dialogButton("Cancel", onPressed: close, isOutline: true), dialogButton("Cancel", onPressed: close, isOutline: true),
dialogButton("Clear", onPressed: isOptFixed ? null : () async { if (!isOptFixed)dialogButton("Clear", onPressed: () async {
await bind.mainSetOption( await bind.mainSetOption(
key: kOptionWhitelist, value: defaultOptionWhitelist); key: kOptionWhitelist, value: defaultOptionWhitelist);
callback?.call(); callback?.call();
close(); close();
}, isOutline: true), }, isOutline: true),
dialogButton( if (!isOptFixed) dialogButton(
"OK", "OK",
onPressed: isOptFixed ? null : () async { onPressed: () async {
setState(() { setState(() {
msg = ""; msg = "";
isInProgress = true; isInProgress = true;

View File

@ -74,9 +74,11 @@ class _PeerTabPageState extends State<PeerTabPage>
]; ];
RelativeRect? mobileTabContextMenuPos; RelativeRect? mobileTabContextMenuPos;
final isOptVisiableFixed = isOptionFixed(kOptionPeerTabVisible);
@override @override
void initState() { void initState() {
final uiType = bind.getLocalFlutterOption(k: 'peer-card-ui-type'); final uiType = bind.getLocalFlutterOption(k: kOptionPeerCardUiType);
if (uiType != '') { if (uiType != '') {
peerCardUiType.value = int.parse(uiType) == 0 peerCardUiType.value = int.parse(uiType) == 0
? PeerUiType.grid ? PeerUiType.grid
@ -85,7 +87,7 @@ class _PeerTabPageState extends State<PeerTabPage>
: PeerUiType.list; : PeerUiType.list;
} }
hideAbTagsPanel.value = hideAbTagsPanel.value =
bind.mainGetLocalOption(key: "hideAbTagsPanel").isNotEmpty; bind.mainGetLocalOption(key: kOptionHideAbTagsPanel).isNotEmpty;
super.initState(); super.initState();
} }
@ -173,10 +175,12 @@ class _PeerTabPageState extends State<PeerTabPage>
child: Icon(model.tabIcon(t), color: color) child: Icon(model.tabIcon(t), color: color)
.paddingSymmetric(horizontal: 4), .paddingSymmetric(horizontal: 4),
).paddingSymmetric(horizontal: 4), ).paddingSymmetric(horizontal: 4),
onTap: () async { onTap: isOptionFixed(kOptionPeerTabIndex)
? null
: () async {
await handleTabSelection(t); await handleTabSelection(t);
await bind.setLocalFlutterOption( await bind.setLocalFlutterOption(
k: PeerTabModel.kPeerTabIndex, v: t.toString()); k: kOptionPeerTabIndex, v: t.toString());
}, },
onHover: (value) => hover.value = value, onHover: (value) => hover.value = value,
), ),
@ -265,12 +269,17 @@ class _PeerTabPageState extends State<PeerTabPage>
if (!model.isEnabled[i]) continue; if (!model.isEnabled[i]) continue;
items.add(PopupMenuItem( items.add(PopupMenuItem(
height: kMinInteractiveDimension * 0.8, height: kMinInteractiveDimension * 0.8,
onTap: () => model.setTabVisible(i, !model.isVisibleEnabled[i]), onTap: isOptVisiableFixed
? null
: () => model.setTabVisible(i, !model.isVisibleEnabled[i]),
enabled: !isOptVisiableFixed,
child: Row( child: Row(
children: [ children: [
Checkbox( Checkbox(
value: model.isVisibleEnabled[i], value: model.isVisibleEnabled[i],
onChanged: (_) { onChanged: isOptVisiableFixed
? null
: (_) {
model.setTabVisible(i, !model.isVisibleEnabled[i]); model.setTabVisible(i, !model.isVisibleEnabled[i]);
if (Navigator.canPop(context)) { if (Navigator.canPop(context)) {
Navigator.pop(context); Navigator.pop(context);
@ -333,7 +342,8 @@ class _PeerTabPageState extends State<PeerTabPage>
setter: (show) async { setter: (show) async {
model.setTabVisible(tabIndex, show); model.setTabVisible(tabIndex, show);
cancelFunc(); cancelFunc();
})); },
enabled: (!isOptVisiableFixed).obs));
} }
return mod_menu.PopupMenu( return mod_menu.PopupMenu(
items: menu items: menu
@ -537,7 +547,8 @@ class _PeerTabPageState extends State<PeerTabPage>
), ),
onTap: () async { onTap: () async {
await bind.mainSetLocalOption( await bind.mainSetLocalOption(
key: "hideAbTagsPanel", value: hideAbTagsPanel.value ? defaultOptionNo : "Y"); key: kOptionHideAbTagsPanel,
value: hideAbTagsPanel.value ? defaultOptionNo : "Y");
hideAbTagsPanel.value = !hideAbTagsPanel.value; hideAbTagsPanel.value = !hideAbTagsPanel.value;
}); });
} }
@ -799,12 +810,15 @@ class _PeerViewDropdownState extends State<PeerViewDropdown> {
style: style), style: style),
e, e,
peerCardUiType.value, peerCardUiType.value,
dense: true, (PeerUiType? v) async { dense: true,
isOptionFixed(kOptionPeerCardUiType)
? null
: (PeerUiType? v) async {
if (v != null) { if (v != null) {
peerCardUiType.value = v; peerCardUiType.value = v;
setState(() {}); setState(() {});
await bind.setLocalFlutterOption( await bind.setLocalFlutterOption(
k: "peer-card-ui-type", k: kOptionPeerCardUiType,
v: peerCardUiType.value.index.toString(), v: peerCardUiType.value.index.toString(),
); );
} }
@ -852,7 +866,7 @@ class _PeerSortDropdownState extends State<PeerSortDropdown> {
if (!PeerSortType.values.contains(peerSort.value)) { if (!PeerSortType.values.contains(peerSort.value)) {
peerSort.value = PeerSortType.remoteId; peerSort.value = PeerSortType.remoteId;
bind.setLocalFlutterOption( bind.setLocalFlutterOption(
k: "peer-sorting", k: kOptionPeerSorting,
v: peerSort.value, v: peerSort.value,
); );
} }
@ -882,7 +896,7 @@ class _PeerSortDropdownState extends State<PeerSortDropdown> {
if (v != null) { if (v != null) {
peerSort.value = v; peerSort.value = v;
await bind.setLocalFlutterOption( await bind.setLocalFlutterOption(
k: "peer-sorting", k: kOptionPeerSorting,
v: peerSort.value, v: peerSort.value,
); );
} }

View File

@ -4,12 +4,12 @@ import 'dart:collection';
import 'package:dynamic_layouts/dynamic_layouts.dart'; import 'package:dynamic_layouts/dynamic_layouts.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart'; import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:visibility_detector/visibility_detector.dart'; import 'package:visibility_detector/visibility_detector.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
import 'package:flutter_hbb/models/peer_tab_model.dart';
import '../../common.dart'; import '../../common.dart';
import '../../models/peer_model.dart'; import '../../models/peer_model.dart';
@ -45,7 +45,7 @@ class LoadEvent {
final peerSearchText = "".obs; final peerSearchText = "".obs;
/// for peer sort, global obs value /// for peer sort, global obs value
final peerSort = bind.getLocalFlutterOption(k: 'peer-sorting').obs; final peerSort = bind.getLocalFlutterOption(k: kOptionPeerSorting).obs;
// list for listener // list for listener
final obslist = [peerSearchText, peerSort].obs; final obslist = [peerSearchText, peerSort].obs;
@ -302,7 +302,7 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
if (!PeerSortType.values.contains(sortedBy)) { if (!PeerSortType.values.contains(sortedBy)) {
sortedBy = PeerSortType.remoteId; sortedBy = PeerSortType.remoteId;
bind.setLocalFlutterOption( bind.setLocalFlutterOption(
k: "peer-sorting", k: kOptionPeerSorting,
v: sortedBy, v: sortedBy,
); );
} }

View File

@ -227,7 +227,8 @@ List<(String, String)> otherDefaultSettings() {
if ((isDesktop || isWebDesktop)) ('Zoom cursor', kOptionZoomCursor), if ((isDesktop || isWebDesktop)) ('Zoom cursor', kOptionZoomCursor),
('Show quality monitor', kOptionShowQualityMonitor), ('Show quality monitor', kOptionShowQualityMonitor),
('Mute', kOptionDisableAudio), ('Mute', kOptionDisableAudio),
if (isDesktop) ('Enable file copy and paste', kOptionEnableFileTransfer), if (isDesktop)
('Enable file copy and paste', kOptionEnableFileCopyPaste),
('Disable clipboard', kOptionDisableClipboard), ('Disable clipboard', kOptionDisableClipboard),
('Lock after session end', kOptionLockAfterSessionEnd), ('Lock after session end', kOptionLockAfterSessionEnd),
('Privacy mode', kOptionPrivacyMode), ('Privacy mode', kOptionPrivacyMode),

View File

@ -534,15 +534,15 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
perms['file'] != false && perms['file'] != false &&
(isSupportIfPeer_1_2_3 || isSupportIfPeer_1_2_4)) { (isSupportIfPeer_1_2_3 || isSupportIfPeer_1_2_4)) {
final enabled = !ffiModel.viewOnly; final enabled = !ffiModel.viewOnly;
final option = 'enable-file-transfer'; final value = bind.sessionGetToggleOptionSync(
final value = sessionId: sessionId, arg: kOptionEnableFileCopyPaste);
bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
v.add(TToggleMenu( v.add(TToggleMenu(
value: value, value: value,
onChanged: enabled onChanged: enabled
? (value) { ? (value) {
if (value == null) return; if (value == null) return;
bind.sessionToggleOption(sessionId: sessionId, value: option); bind.sessionToggleOption(
sessionId: sessionId, value: kOptionEnableFileCopyPaste);
} }
: null, : null,
child: Text(translate('Enable file copy and paste')))); child: Text(translate('Enable file copy and paste'))));

View File

@ -107,14 +107,27 @@ const String kOptionFollowRemoteWindow = "follow_remote_window";
const String kOptionZoomCursor = "zoom-cursor"; const String kOptionZoomCursor = "zoom-cursor";
const String kOptionShowQualityMonitor = "show_quality_monitor"; const String kOptionShowQualityMonitor = "show_quality_monitor";
const String kOptionDisableAudio = "disable_audio"; const String kOptionDisableAudio = "disable_audio";
const String kOptionEnableFileCopyPaste = "enable-file-copy-paste";
// "Settings -> Display -> Other default options" // "Settings -> Display -> Other default options"
const String kOptionDisableClipboard = "disable_clipboard"; const String kOptionDisableClipboard = "disable_clipboard";
const String kOptionLockAfterSessionEnd = "lock_after_session_end"; const String kOptionLockAfterSessionEnd = "lock_after_session_end";
const String kOptionPrivacyMode = "privacy_mode"; const String kOptionPrivacyMode = "privacy_mode";
const String kOptionTouchMode = "touch-mode"; const String kOptionTouchMode = "touch-mode";
const String kOptionI444 = "i444"; const String kOptionI444 = "i444";
const String kOptionSwapLeftRightMouse = 'swap-left-right-mouse'; const String kOptionSwapLeftRightMouse = "swap-left-right-mouse";
const String kOptionCodecPreference = 'codec-preference'; const String kOptionCodecPreference = "codec-preference";
const String kOptionRemoteMenubarDragLeft = "remote-menubar-drag-left";
const String kOptionRemoteMenubarDragRight = "remote-menubar-drag-right";
const String kOptionHideAbTagsPanel = "hideAbTagsPanel";
const String kOptionRemoteMenubarState = "remoteMenubarState";
const String kOptionPeerSorting = "peer-sorting";
const String kOptionPeerTabIndex = "peer-tab-index";
const String kOptionPeerTabOrder = "peer-tab-order";
const String kOptionPeerTabVisible = "peer-tab-visible";
const String kOptionPeerCardUiType = "peer-card-ui-type";
const String kOptionCurrentAbName = "current-ab-name";
const String kOptionToggleViewOnly = "view-only";
const String kUrlActionClose = "close"; const String kUrlActionClose = "close";

View File

@ -377,7 +377,7 @@ class _GeneralState extends State<_General> {
_OptionCheckBox(context, 'Confirm before closing multiple tabs', _OptionCheckBox(context, 'Confirm before closing multiple tabs',
'enable-confirm-closing-tabs', 'enable-confirm-closing-tabs',
isServer: false), isServer: false),
_OptionCheckBox(context, 'Adaptive bitrate', 'enable-abr'), _OptionCheckBox(context, 'Adaptive bitrate', kOptionEnableAbr),
wallpaper(), wallpaper(),
if (!bind.isIncomingOnly()) ...[ if (!bind.isIncomingOnly()) ...[
_OptionCheckBox( _OptionCheckBox(
@ -456,9 +456,9 @@ class _GeneralState extends State<_General> {
_OptionCheckBox( _OptionCheckBox(
context, context,
'Enable hardware codec', 'Enable hardware codec',
'enable-hwcodec', kOptionEnableHwcodec,
update: () { update: () {
if (mainGetBoolOptionSync('enable-hwcodec')) { if (mainGetBoolOptionSync(kOptionEnableHwcodec)) {
bind.mainCheckHwcodec(); bind.mainCheckHwcodec();
} }
}, },
@ -510,7 +510,7 @@ class _GeneralState extends State<_General> {
bool user_dir_exists = map['user_dir_exists']!; bool user_dir_exists = map['user_dir_exists']!;
return _Card(title: 'Recording', children: [ return _Card(title: 'Recording', children: [
_OptionCheckBox(context, 'Automatically record incoming sessions', _OptionCheckBox(context, 'Automatically record incoming sessions',
'allow-auto-record-incoming'), kOptionAllowAutoRecordIncoming),
if (showRootDir) if (showRootDir)
Row( Row(
children: [ children: [
@ -705,7 +705,7 @@ 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() {
String accessMode = bind.mainGetOptionSync(key: 'access-mode'); String accessMode = bind.mainGetOptionSync(key: kOptionAccessMode);
_AccessMode mode; _AccessMode mode;
if (accessMode == 'full') { if (accessMode == 'full') {
mode = _AccessMode.full; mode = _AccessMode.full;
@ -1347,14 +1347,14 @@ class _DisplayState extends State<_Display> {
} }
Widget imageQuality(BuildContext context) { Widget imageQuality(BuildContext context) {
final key = 'image_quality';
onChanged(String value) async { onChanged(String value) async {
await bind.mainSetUserDefaultOption(key: key, value: value); await bind.mainSetUserDefaultOption(
key: kOptionImageQuality, value: value);
setState(() {}); setState(() {});
} }
final isOptFixed = isOptionFixed(key); final isOptFixed = isOptionFixed(kOptionImageQuality);
final groupValue = bind.mainGetUserDefaultOption(key: key); final groupValue = bind.mainGetUserDefaultOption(key: kOptionImageQuality);
return _Card(title: 'Default Image Quality', children: [ return _Card(title: 'Default Image Quality', children: [
_Radio(context, _Radio(context,
value: kRemoteImageQualityBest, value: kRemoteImageQualityBest,
@ -1484,7 +1484,7 @@ class _DisplayState extends State<_Display> {
key: key, key: key,
value: b value: b
? 'Y' ? 'Y'
: (key == kOptionEnableFileTransfer ? 'N' : defaultOptionNo)); : (key == kOptionEnableFileCopyPaste ? 'N' : defaultOptionNo));
setState(() {}); setState(() {});
} }

View File

@ -94,7 +94,7 @@ class _RemotePageState extends State<RemotePage>
void _initStates(String id) { void _initStates(String id) {
initSharedStates(id); initSharedStates(id);
_zoomCursor = PeerBoolOption.find(id, 'zoom-cursor'); _zoomCursor = PeerBoolOption.find(id, kOptionZoomCursor);
_showRemoteCursor = ShowRemoteCursorState.find(id); _showRemoteCursor = ShowRemoteCursorState.find(id);
_keyboardEnabled = KeyboardEnabledState.find(id); _keyboardEnabled = KeyboardEnabledState.find(id);
_remoteCursorMoved = RemoteCursorMovedState.find(id); _remoteCursorMoved = RemoteCursorMovedState.find(id);
@ -136,7 +136,7 @@ class _RemotePageState extends State<RemotePage>
_showRemoteCursor.value = bind.sessionGetToggleOptionSync( _showRemoteCursor.value = bind.sessionGetToggleOptionSync(
sessionId: sessionId, arg: 'show-remote-cursor'); sessionId: sessionId, arg: 'show-remote-cursor');
_zoomCursor.value = bind.sessionGetToggleOptionSync( _zoomCursor.value = bind.sessionGetToggleOptionSync(
sessionId: sessionId, arg: 'zoom-cursor'); sessionId: sessionId, arg: kOptionZoomCursor);
DesktopMultiWindow.addListener(this); DesktopMultiWindow.addListener(this);
// if (!_isCustomCursorInited) { // if (!_isCustomCursorInited) {
// customCursorController.registerNeedUpdateCursorCallback( // customCursorController.registerNeedUpdateCursorCallback(

View File

@ -341,9 +341,10 @@ class PopupMenuItemState<T, W extends PopupMenuItem<T>> extends State<W> {
@protected @protected
void handleTap() { void handleTap() {
widget.onTap?.call(); widget.onTap?.call();
if (Navigator.canPop(context)) {
Navigator.pop<T>(context, widget.value); Navigator.pop<T>(context, widget.value);
} }
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -445,6 +445,8 @@ abstract class MenuEntrySwitchBase<T> extends MenuEntryBase<T> {
dismissCallback: dismissCallback, dismissCallback: dismissCallback,
); );
bool get isEnabled => enabled?.value ?? true;
RxBool get curOption; RxBool get curOption;
Future<void> setOption(bool? option); Future<void> setOption(bool? option);
@ -481,7 +483,8 @@ abstract class MenuEntrySwitchBase<T> extends MenuEntryBase<T> {
if (switchType == SwitchType.sswitch) { if (switchType == SwitchType.sswitch) {
return Switch( return Switch(
value: curOption.value, value: curOption.value,
onChanged: (v) { onChanged: isEnabled
? (v) {
if (super.dismissOnClicked && if (super.dismissOnClicked &&
Navigator.canPop(context)) { Navigator.canPop(context)) {
Navigator.pop(context); Navigator.pop(context);
@ -490,12 +493,14 @@ abstract class MenuEntrySwitchBase<T> extends MenuEntryBase<T> {
} }
} }
setOption(v); setOption(v);
}, }
: null,
); );
} else { } else {
return Checkbox( return Checkbox(
value: curOption.value, value: curOption.value,
onChanged: (v) { onChanged: isEnabled
? (v) {
if (super.dismissOnClicked && if (super.dismissOnClicked &&
Navigator.canPop(context)) { Navigator.canPop(context)) {
Navigator.pop(context); Navigator.pop(context);
@ -504,13 +509,15 @@ abstract class MenuEntrySwitchBase<T> extends MenuEntryBase<T> {
} }
} }
setOption(v); setOption(v);
}, }
: null,
); );
} }
})), })),
)) ))
])), ])),
onPressed: () { onPressed: isEnabled
? () {
if (super.dismissOnClicked && Navigator.canPop(context)) { if (super.dismissOnClicked && Navigator.canPop(context)) {
Navigator.pop(context); Navigator.pop(context);
if (super.dismissCallback != null) { if (super.dismissCallback != null) {
@ -518,7 +525,8 @@ abstract class MenuEntrySwitchBase<T> extends MenuEntryBase<T> {
} }
} }
setOption(!curOption.value); setOption(!curOption.value);
}, }
: null,
)), )),
) )
]; ];

View File

@ -27,12 +27,11 @@ import './popup_menu.dart';
import './kb_layout_type_chooser.dart'; import './kb_layout_type_chooser.dart';
class ToolbarState { class ToolbarState {
final kStoreKey = 'remoteMenubarState';
late RxBool show; late RxBool show;
late RxBool _pin; late RxBool _pin;
ToolbarState() { ToolbarState() {
final s = bind.getLocalFlutterOption(k: kStoreKey); final s = bind.getLocalFlutterOption(k: kOptionRemoteMenubarState);
if (s.isEmpty) { if (s.isEmpty) {
_initSet(false, false); _initSet(false, false);
return; return;
@ -53,8 +52,8 @@ 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(
RxBool(bind.mainGetUserDefaultOption(key: kOptionCollapseToolbar) != 'Y'); bind.mainGetUserDefaultOption(key: kOptionCollapseToolbar) != 'Y');
_pin = RxBool(p); _pin = RxBool(p);
} }
@ -86,7 +85,7 @@ class ToolbarState {
_savePin() async { _savePin() async {
bind.setLocalFlutterOption( bind.setLocalFlutterOption(
k: kStoreKey, v: jsonEncode({'pin': _pin.value})); k: kOptionRemoteMenubarState, v: jsonEncode({'pin': _pin.value}));
} }
save() async { save() async {
@ -1875,7 +1874,7 @@ class _KeyboardMenu extends StatelessWidget {
? (value) async { ? (value) async {
if (value == null) return; if (value == null) return;
await bind.sessionToggleOption( await bind.sessionToggleOption(
sessionId: ffi.sessionId, value: kOptionViewOnly); sessionId: ffi.sessionId, value: kOptionToggleViewOnly);
ffiModel.setViewOnly(id, value); ffiModel.setViewOnly(id, value);
} }
: null, : null,
@ -2019,6 +2018,7 @@ class _VoiceCallMenu extends StatelessWidget {
); );
} }
} }
class _RecordMenu extends StatelessWidget { class _RecordMenu extends StatelessWidget {
const _RecordMenu({Key? key}) : super(key: key); const _RecordMenu({Key? key}) : super(key: key);
@ -2372,18 +2372,18 @@ class _DraggableShowHideState extends State<_DraggableShowHide> {
super.initState(); super.initState();
final confLeft = double.tryParse( final confLeft = double.tryParse(
bind.mainGetLocalOption(key: 'remote-menubar-drag-left')); bind.mainGetLocalOption(key: kOptionRemoteMenubarDragLeft));
if (confLeft == null) { if (confLeft == null) {
bind.mainSetLocalOption( bind.mainSetLocalOption(
key: 'remote-menubar-drag-left', value: left.toString()); key: kOptionRemoteMenubarDragLeft, value: left.toString());
} else { } else {
left = confLeft; left = confLeft;
} }
final confRight = double.tryParse( final confRight = double.tryParse(
bind.mainGetLocalOption(key: 'remote-menubar-drag-right')); bind.mainGetLocalOption(key: kOptionRemoteMenubarDragRight));
if (confRight == null) { if (confRight == null) {
bind.mainSetLocalOption( bind.mainSetLocalOption(
key: 'remote-menubar-drag-right', value: right.toString()); key: kOptionRemoteMenubarDragRight, value: right.toString());
} else { } else {
right = confRight; right = confRight;
} }

View File

@ -323,11 +323,11 @@ class DesktopTab extends StatelessWidget {
return buildRemoteBlock( return buildRemoteBlock(
child: child, child: child,
use: () async { use: () async {
var access_mode = await bind.mainGetOption(key: 'access-mode'); var access_mode = await bind.mainGetOption(key: kOptionAccessMode);
var option = option2bool( var option = option2bool(
'allow-remote-config-modification', kOptionAllowRemoteConfigModification,
await bind.mainGetOption( await bind.mainGetOption(
key: 'allow-remote-config-modification')); key: kOptionAllowRemoteConfigModification));
return access_mode == 'view' || (access_mode.isEmpty && !option); return access_mode == 'view' || (access_mode.isEmpty && !option);
}); });
} }

View File

@ -638,7 +638,7 @@ class _RemotePageState extends State<RemotePage> {
gFFI.ffiModel.toggleTouchMode(); gFFI.ffiModel.toggleTouchMode();
final v = gFFI.ffiModel.touchMode ? 'Y' : ''; final v = gFFI.ffiModel.touchMode ? 'Y' : '';
bind.sessionPeerOption( bind.sessionPeerOption(
sessionId: sessionId, name: "touch-mode", value: v); sessionId: sessionId, name: kOptionTouchMode, value: v);
}))); })));
} }

View File

@ -111,7 +111,7 @@ class ServerPage extends StatefulWidget implements PageShape {
} else if (value == kUsePermanentPassword || } else if (value == kUsePermanentPassword ||
value == kUseTemporaryPassword || value == kUseTemporaryPassword ||
value == kUseBothPasswords) { value == kUseBothPasswords) {
bind.mainSetOption(key: "verification-method", value: value); bind.mainSetOption(key: kOptionVerificationMethod, value: value);
gFFI.serverModel.updatePasswordModel(); gFFI.serverModel.updatePasswordModel();
} else if (value.startsWith("AcceptSessionsVia")) { } else if (value.startsWith("AcceptSessionsVia")) {
value = value.substring("AcceptSessionsVia".length); value = value.substring("AcceptSessionsVia".length);

View File

@ -87,7 +87,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
} }
final enableAbrRes = option2bool( final enableAbrRes = option2bool(
"enable-abr", await bind.mainGetOption(key: "enable-abr")); kOptionEnableAbr, await bind.mainGetOption(key: kOptionEnableAbr));
if (enableAbrRes != _enableAbr) { if (enableAbrRes != _enableAbr) {
update = true; update = true;
_enableAbr = enableAbrRes; _enableAbr = enableAbrRes;
@ -107,30 +107,30 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
_onlyWhiteList = onlyWhiteList; _onlyWhiteList = onlyWhiteList;
} }
final enableDirectIPAccess = option2bool( final enableDirectIPAccess = option2bool(kOptionDirectServer,
'direct-server', await bind.mainGetOption(key: 'direct-server')); await bind.mainGetOption(key: kOptionDirectServer));
if (enableDirectIPAccess != _enableDirectIPAccess) { if (enableDirectIPAccess != _enableDirectIPAccess) {
update = true; update = true;
_enableDirectIPAccess = enableDirectIPAccess; _enableDirectIPAccess = enableDirectIPAccess;
} }
final enableRecordSession = option2bool('enable-record-session', final enableRecordSession = option2bool(kOptionEnableRecordSession,
await bind.mainGetOption(key: 'enable-record-session')); await bind.mainGetOption(key: kOptionEnableRecordSession));
if (enableRecordSession != _enableRecordSession) { if (enableRecordSession != _enableRecordSession) {
update = true; update = true;
_enableRecordSession = enableRecordSession; _enableRecordSession = enableRecordSession;
} }
final enableHardwareCodec = option2bool( final enableHardwareCodec = option2bool(kOptionEnableHwcodec,
'enable-hwcodec', await bind.mainGetOption(key: 'enable-hwcodec')); await bind.mainGetOption(key: kOptionEnableHwcodec));
if (_enableHardwareCodec != enableHardwareCodec) { if (_enableHardwareCodec != enableHardwareCodec) {
update = true; update = true;
_enableHardwareCodec = enableHardwareCodec; _enableHardwareCodec = enableHardwareCodec;
} }
final autoRecordIncomingSession = option2bool( final autoRecordIncomingSession = option2bool(
'allow-auto-record-incoming', kOptionAllowAutoRecordIncoming,
await bind.mainGetOption(key: 'allow-auto-record-incoming')); await bind.mainGetOption(key: kOptionAllowAutoRecordIncoming));
if (autoRecordIncomingSession != _autoRecordIncomingSession) { if (autoRecordIncomingSession != _autoRecordIncomingSession) {
update = true; update = true;
_autoRecordIncomingSession = autoRecordIncomingSession; _autoRecordIncomingSession = autoRecordIncomingSession;
@ -161,15 +161,15 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
_buildDate = buildDate; _buildDate = buildDate;
} }
final allowAutoDisconnect = option2bool('allow-auto-disconnect', final allowAutoDisconnect = option2bool(kOptionAllowAutoDisconnect,
await bind.mainGetOption(key: 'allow-auto-disconnect')); await bind.mainGetOption(key: kOptionAllowAutoDisconnect));
if (allowAutoDisconnect != _allowAutoDisconnect) { if (allowAutoDisconnect != _allowAutoDisconnect) {
update = true; update = true;
_allowAutoDisconnect = allowAutoDisconnect; _allowAutoDisconnect = allowAutoDisconnect;
} }
final autoDisconnectTimeout = final autoDisconnectTimeout =
await bind.mainGetOption(key: 'auto-disconnect-timeout'); await bind.mainGetOption(key: kOptionAutoDisconnectTimeout);
if (autoDisconnectTimeout != _autoDisconnectTimeout) { if (autoDisconnectTimeout != _autoDisconnectTimeout) {
update = true; update = true;
_autoDisconnectTimeout = autoDisconnectTimeout; _autoDisconnectTimeout = autoDisconnectTimeout;

View File

@ -5,6 +5,7 @@ import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hbb/common/hbbs/hbbs.dart'; import 'package:flutter_hbb/common/hbbs/hbbs.dart';
import 'package:flutter_hbb/common/widgets/peers_view.dart'; import 'package:flutter_hbb/common/widgets/peers_view.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/models/model.dart'; import 'package:flutter_hbb/models/model.dart';
import 'package:flutter_hbb/models/peer_model.dart'; import 'package:flutter_hbb/models/peer_model.dart';
import 'package:flutter_hbb/models/platform_model.dart'; import 'package:flutter_hbb/models/platform_model.dart';
@ -548,7 +549,7 @@ class AbModel {
} }
trySetCurrentToLast() { trySetCurrentToLast() {
final name = bind.getLocalFlutterOption(k: 'current-ab-name'); final name = bind.getLocalFlutterOption(k: kOptionCurrentAbName);
if (addressbooks.containsKey(name)) { if (addressbooks.containsKey(name)) {
_currentName.value = name; _currentName.value = name;
} }

View File

@ -389,7 +389,7 @@ class FfiModel with ChangeNotifier {
_handleSyncPeerOption(Map<String, dynamic> evt, String peer) { _handleSyncPeerOption(Map<String, dynamic> evt, String peer) {
final k = evt['k']; final k = evt['k'];
final v = evt['v']; final v = evt['v'];
if (k == kOptionViewOnly) { if (k == kOptionToggleViewOnly) {
setViewOnly(peer, v as bool); setViewOnly(peer, v as bool);
} else if (k == 'keyboard_mode') { } else if (k == 'keyboard_mode') {
parent.target?.inputModel.updateKeyboardMode(); parent.target?.inputModel.updateKeyboardMode();
@ -765,7 +765,7 @@ class FfiModel with ChangeNotifier {
_touchMode = true; _touchMode = true;
} else { } else {
_touchMode = await bind.sessionGetOption( _touchMode = await bind.sessionGetOption(
sessionId: sessionId, arg: 'touch-mode') != sessionId: sessionId, arg: kOptionTouchMode) !=
''; '';
} }
if (connType == ConnType.fileTransfer) { if (connType == ConnType.fileTransfer) {
@ -797,7 +797,7 @@ class FfiModel with ChangeNotifier {
setViewOnly( setViewOnly(
peerId, peerId,
bind.sessionGetToggleOptionSync( bind.sessionGetToggleOptionSync(
sessionId: sessionId, arg: kOptionViewOnly)); sessionId: sessionId, arg: kOptionToggleViewOnly));
} }
if (connType == ConnType.defaultConn) { if (connType == ConnType.defaultConn) {
final platformAdditions = evt['platform_additions']; final platformAdditions = evt['platform_additions'];

View File

@ -2,6 +2,7 @@ import 'dart:convert';
import 'dart:math'; import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/models/peer_model.dart'; import 'package:flutter_hbb/models/peer_model.dart';
import 'package:flutter_hbb/models/platform_model.dart'; import 'package:flutter_hbb/models/platform_model.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@ -22,9 +23,6 @@ class PeerTabModel with ChangeNotifier {
int get currentTab => _currentTab; int get currentTab => _currentTab;
int _currentTab = 0; // index in tabNames int _currentTab = 0; // index in tabNames
static const int maxTabCount = 5; static const int maxTabCount = 5;
static const String kPeerTabIndex = 'peer-tab-index';
static const String kPeerTabOrder = 'peer-tab-order';
static const String kPeerTabVisible = 'peer-tab-visible';
static const List<String> tabNames = [ static const List<String> tabNames = [
'Recent sessions', 'Recent sessions',
'Favorites', 'Favorites',
@ -72,7 +70,7 @@ class PeerTabModel with ChangeNotifier {
PeerTabModel(this.parent) { PeerTabModel(this.parent) {
// visible // visible
try { try {
final option = bind.getLocalFlutterOption(k: kPeerTabVisible); final option = bind.getLocalFlutterOption(k: kOptionPeerTabVisible);
if (option.isNotEmpty) { if (option.isNotEmpty) {
List<dynamic> decodeList = jsonDecode(option); List<dynamic> decodeList = jsonDecode(option);
if (decodeList.length == _isVisible.length) { if (decodeList.length == _isVisible.length) {
@ -88,7 +86,7 @@ class PeerTabModel with ChangeNotifier {
} }
// order // order
try { try {
final option = bind.getLocalFlutterOption(k: kPeerTabOrder); final option = bind.getLocalFlutterOption(k: kOptionPeerTabOrder);
if (option.isNotEmpty) { if (option.isNotEmpty) {
List<dynamic> decodeList = jsonDecode(option); List<dynamic> decodeList = jsonDecode(option);
if (decodeList.length == maxTabCount) { if (decodeList.length == maxTabCount) {
@ -112,7 +110,7 @@ class PeerTabModel with ChangeNotifier {
} }
// init currentTab // init currentTab
_currentTab = _currentTab =
int.tryParse(bind.getLocalFlutterOption(k: kPeerTabIndex)) ?? 0; int.tryParse(bind.getLocalFlutterOption(k: kOptionPeerTabIndex)) ?? 0;
if (_currentTab < 0 || _currentTab >= maxTabCount) { if (_currentTab < 0 || _currentTab >= maxTabCount) {
_currentTab = 0; _currentTab = 0;
} }
@ -222,7 +220,7 @@ class PeerTabModel with ChangeNotifier {
} }
try { try {
bind.setLocalFlutterOption( bind.setLocalFlutterOption(
k: kPeerTabVisible, v: jsonEncode(_isVisible)); k: kOptionPeerTabVisible, v: jsonEncode(_isVisible));
} catch (_) {} } catch (_) {}
notifyListeners(); notifyListeners();
} }
@ -258,7 +256,7 @@ class PeerTabModel with ChangeNotifier {
for (int i = 0; i < list.length; i++) { for (int i = 0; i < list.length; i++) {
orders[i] = list[i]; orders[i] = list[i];
} }
bind.setLocalFlutterOption(k: kPeerTabOrder, v: jsonEncode(orders)); bind.setLocalFlutterOption(k: kOptionPeerTabOrder, v: jsonEncode(orders));
notifyListeners(); notifyListeners();
} }
} }

View File

@ -125,8 +125,8 @@ class ServerModel with ChangeNotifier {
/* /*
// initital _hideCm at startup // initital _hideCm at startup
final verificationMethod = final verificationMethod =
bind.mainGetOptionSync(key: "verification-method"); bind.mainGetOptionSync(key: kOptionVerificationMethod);
final approveMode = bind.mainGetOptionSync(key: 'approve-mode'); final approveMode = bind.mainGetOptionSync(key: kOptionApproveMode);
_hideCm = option2bool( _hideCm = option2bool(
'allow-hide-cm', bind.mainGetOptionSync(key: 'allow-hide-cm')); 'allow-hide-cm', bind.mainGetOptionSync(key: 'allow-hide-cm'));
if (!(approveMode == 'password' && if (!(approveMode == 'password' &&
@ -187,18 +187,19 @@ class ServerModel with ChangeNotifier {
if (androidVersion < 30 || if (androidVersion < 30 ||
!await AndroidPermissionManager.check(kRecordAudio)) { !await AndroidPermissionManager.check(kRecordAudio)) {
_audioOk = false; _audioOk = false;
bind.mainSetOption(key: "enable-audio", value: "N"); bind.mainSetOption(key: kOptionEnableAudio, value: "N");
} else { } else {
final audioOption = await bind.mainGetOption(key: 'enable-audio'); final audioOption = await bind.mainGetOption(key: kOptionEnableAudio);
_audioOk = audioOption.isEmpty; _audioOk = audioOption.isEmpty;
} }
// file // file
if (!await AndroidPermissionManager.check(kManageExternalStorage)) { if (!await AndroidPermissionManager.check(kManageExternalStorage)) {
_fileOk = false; _fileOk = false;
bind.mainSetOption(key: "enable-file-transfer", value: "N"); bind.mainSetOption(key: kOptionEnableFileTransfer, value: "N");
} else { } else {
final fileOption = await bind.mainGetOption(key: 'enable-file-transfer'); final fileOption =
await bind.mainGetOption(key: kOptionEnableFileTransfer);
_fileOk = fileOption.isEmpty; _fileOk = fileOption.isEmpty;
} }
@ -209,10 +210,10 @@ class ServerModel with ChangeNotifier {
var update = false; var update = false;
final temporaryPassword = await bind.mainGetTemporaryPassword(); final temporaryPassword = await bind.mainGetTemporaryPassword();
final verificationMethod = final verificationMethod =
await bind.mainGetOption(key: "verification-method"); await bind.mainGetOption(key: kOptionVerificationMethod);
final temporaryPasswordLength = final temporaryPasswordLength =
await bind.mainGetOption(key: "temporary-password-length"); await bind.mainGetOption(key: "temporary-password-length");
final approveMode = await bind.mainGetOption(key: 'approve-mode'); final approveMode = await bind.mainGetOption(key: kOptionApproveMode);
/* /*
var hideCm = option2bool( var hideCm = option2bool(
'allow-hide-cm', await bind.mainGetOption(key: 'allow-hide-cm')); 'allow-hide-cm', await bind.mainGetOption(key: 'allow-hide-cm'));
@ -283,7 +284,8 @@ class ServerModel with ChangeNotifier {
} }
_audioOk = !_audioOk; _audioOk = !_audioOk;
bind.mainSetOption(key: "enable-audio", value: _audioOk ? defaultOptionYes : 'N'); bind.mainSetOption(
key: kOptionEnableAudio, value: _audioOk ? defaultOptionYes : 'N');
notifyListeners(); notifyListeners();
} }
@ -302,7 +304,9 @@ class ServerModel with ChangeNotifier {
} }
_fileOk = !_fileOk; _fileOk = !_fileOk;
bind.mainSetOption(key: kOptionEnableFileTransfer, value: _fileOk ? defaultOptionYes : 'N'); bind.mainSetOption(
key: kOptionEnableFileTransfer,
value: _fileOk ? defaultOptionYes : 'N');
notifyListeners(); notifyListeners();
} }
@ -445,7 +449,9 @@ class ServerModel with ChangeNotifier {
break; break;
case "input": case "input":
if (_inputOk != value) { if (_inputOk != value) {
bind.mainSetOption(key: kOptionEnableKeyboard, value: value ? defaultOptionYes : 'N'); bind.mainSetOption(
key: kOptionEnableKeyboard,
value: value ? defaultOptionYes : 'N');
} }
_inputOk = value; _inputOk = value;
break; break;

View File

@ -660,7 +660,7 @@ export default class Connection {
const defaultToggleTrue = [ const defaultToggleTrue = [
'show-remote-cursor', 'show-remote-cursor',
'privacy-mode', 'privacy-mode',
'enable-file-transfer', 'enable-file-copy-paste',
'allow_swap_key', 'allow_swap_key',
]; ];
return this._options[name] || (defaultToggleTrue.includes(name) ? true : false); return this._options[name] || (defaultToggleTrue.includes(name) ? true : false);
@ -906,7 +906,7 @@ export default class Connection {
case "privacy-mode": case "privacy-mode":
option.privacy_mode = v2; option.privacy_mode = v2;
break; break;
case "enable-file-transfer": case "enable-file-copy-paste":
option.enable_file_transfer = v2; option.enable_file_transfer = v2;
break; break;
case "block-input": case "block-input":
@ -933,7 +933,7 @@ export default class Connection {
option.show_remote_cursor = this.getToggleOption("show-remote-cursor") option.show_remote_cursor = this.getToggleOption("show-remote-cursor")
? message.OptionMessage_BoolOption.Yes ? message.OptionMessage_BoolOption.Yes
: message.OptionMessage_BoolOption.No; : message.OptionMessage_BoolOption.No;
option.enable_file_transfer = this.getToggleOption("enable-file-transfer") option.enable_file_transfer = this.getToggleOption("enable-file-copy-paste")
? message.OptionMessage_BoolOption.Yes ? message.OptionMessage_BoolOption.Yes
: message.OptionMessage_BoolOption.No; : message.OptionMessage_BoolOption.No;
option.lock_after_session_end = this.getToggleOption("lock-after-session-end") option.lock_after_session_end = this.getToggleOption("lock-after-session-end")

View File

@ -40,10 +40,6 @@ const SERIAL: i32 = 3;
const PASSWORD_ENC_VERSION: &str = "00"; const PASSWORD_ENC_VERSION: &str = "00";
const ENCRYPT_MAX_LEN: usize = 128; const ENCRYPT_MAX_LEN: usize = 128;
// config2 options
#[cfg(target_os = "linux")]
pub const CONFIG_OPTION_ALLOW_LINUX_HEADLESS: &str = "allow-linux-headless";
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
lazy_static::lazy_static! { lazy_static::lazy_static! {
pub static ref ORG: RwLock<String> = RwLock::new("com.carriez".to_owned()); pub static ref ORG: RwLock<String> = RwLock::new("com.carriez".to_owned());
@ -278,7 +274,7 @@ pub struct PeerConfig {
#[serde(flatten)] #[serde(flatten)]
pub disable_clipboard: DisableClipboard, pub disable_clipboard: DisableClipboard,
#[serde(flatten)] #[serde(flatten)]
pub enable_file_transfer: EnableFileTransfer, pub enable_file_copy_paste: EnableFileCopyPaste,
#[serde(flatten)] #[serde(flatten)]
pub show_quality_monitor: ShowQualityMonitor, pub show_quality_monitor: ShowQualityMonitor,
#[serde(flatten)] #[serde(flatten)]
@ -355,7 +351,7 @@ impl Default for PeerConfig {
direct_failures: Default::default(), direct_failures: Default::default(),
disable_audio: Default::default(), disable_audio: Default::default(),
disable_clipboard: Default::default(), disable_clipboard: Default::default(),
enable_file_transfer: Default::default(), enable_file_copy_paste: Default::default(),
show_quality_monitor: Default::default(), show_quality_monitor: Default::default(),
follow_remote_cursor: Default::default(), follow_remote_cursor: Default::default(),
follow_remote_window: Default::default(), follow_remote_window: Default::default(),
@ -1289,11 +1285,13 @@ serde_field_bool!(
default_disable_audio, default_disable_audio,
"DisableAudio::default_disable_audio" "DisableAudio::default_disable_audio"
); );
// The key is enable_file_transfer, but no need to keep the old name.
// This option should always be true.
serde_field_bool!( serde_field_bool!(
EnableFileTransfer, EnableFileCopyPaste,
"enable_file_transfer", "enable_file_copy_paste",
default_enable_file_transfer, default_enable_file_copy_paste,
"EnableFileTransfer::default_enable_file_transfer" "EnableFileCopyPaste::default_enable_file_copy_paste"
); );
serde_field_bool!( serde_field_bool!(
DisableClipboard, DisableClipboard,
@ -1438,11 +1436,13 @@ impl LocalConfig {
} }
pub fn get_flutter_option(k: &str) -> String { pub fn get_flutter_option(k: &str) -> String {
if let Some(v) = LOCAL_CONFIG.read().unwrap().ui_flutter.get(k) { get_or(
v.clone() &OVERWRITE_LOCAL_SETTINGS,
} else { &LOCAL_CONFIG.read().unwrap().ui_flutter,
"".to_owned() &DEFAULT_LOCAL_SETTINGS,
} k,
)
.unwrap_or_default()
} }
pub fn set_flutter_option(k: String, v: String) { pub fn set_flutter_option(k: String, v: String) {
@ -1591,7 +1591,7 @@ impl UserDefaultConfig {
self.get_double_string(key, 50.0, 10.0, 0xFFF as f64) self.get_double_string(key, 50.0, 10.0, 0xFFF as f64)
} }
keys::OPTION_CUSTOM_FPS => self.get_double_string(key, 30.0, 5.0, 120.0), keys::OPTION_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"]), keys::OPTION_ENABLE_FILE_COPY_PASTE => 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())
@ -1992,6 +1992,7 @@ pub mod keys {
pub const OPTION_ZOOM_CURSOR: &str = "zoom-cursor"; pub const OPTION_ZOOM_CURSOR: &str = "zoom-cursor";
pub const OPTION_SHOW_QUALITY_MONITOR: &str = "show_quality_monitor"; pub const OPTION_SHOW_QUALITY_MONITOR: &str = "show_quality_monitor";
pub const OPTION_DISABLE_AUDIO: &str = "disable_audio"; pub const OPTION_DISABLE_AUDIO: &str = "disable_audio";
pub const OPTION_ENABLE_FILE_COPY_PASTE: &str = "enable-file-copy-paste";
pub const OPTION_DISABLE_CLIPBOARD: &str = "disable_clipboard"; pub const OPTION_DISABLE_CLIPBOARD: &str = "disable_clipboard";
pub const OPTION_LOCK_AFTER_SESSION_END: &str = "lock_after_session_end"; pub const OPTION_LOCK_AFTER_SESSION_END: &str = "lock_after_session_end";
pub const OPTION_PRIVACY_MODE: &str = "privacy_mode"; pub const OPTION_PRIVACY_MODE: &str = "privacy_mode";
@ -2010,6 +2011,9 @@ pub mod keys {
pub const OPTION_CODEC_PREFERENCE: &str = "codec-preference"; pub const OPTION_CODEC_PREFERENCE: &str = "codec-preference";
pub const OPTION_THEME: &str = "theme"; pub const OPTION_THEME: &str = "theme";
pub const OPTION_LANGUAGE: &str = "lang"; pub const OPTION_LANGUAGE: &str = "lang";
pub const OPTION_REMOTE_MENUBAR_DRAG_LEFT: &str = "remote-menubar-drag-left";
pub const OPTION_REMOTE_MENUBAR_DRAG_RIGHT: &str = "remote-menubar-drag-right";
pub const OPTION_HIDE_AB_TAGS_PANEL: &str = "hideAbTagsPanel";
pub const OPTION_ENABLE_CONFIRM_CLOSING_TABS: &str = "enable-confirm-closing-tabs"; pub const OPTION_ENABLE_CONFIRM_CLOSING_TABS: &str = "enable-confirm-closing-tabs";
pub const OPTION_ENABLE_OPEN_NEW_CONNECTIONS_IN_TABS: &str = pub const OPTION_ENABLE_OPEN_NEW_CONNECTIONS_IN_TABS: &str =
"enable-open-new-connections-in-tabs"; "enable-open-new-connections-in-tabs";
@ -2043,6 +2047,15 @@ pub mod keys {
pub const OPTION_ENABLE_HWCODEC: &str = "enable-hwcodec"; pub const OPTION_ENABLE_HWCODEC: &str = "enable-hwcodec";
pub const OPTION_APPROVE_MODE: &str = "approve-mode"; pub const OPTION_APPROVE_MODE: &str = "approve-mode";
// flutter local options
pub const OPTION_FLUTTER_REMOTE_MENUBAR_STATE: &str = "remoteMenubarState";
pub const OPTION_FLUTTER_PEER_SORTING: &str = "peer-sorting";
pub const OPTION_FLUTTER_PEER_TAB_INDEX: &str = "peer-tab-index";
pub const OPTION_FLUTTER_PEER_TAB_ORDER: &str = "peer-tab-order";
pub const OPTION_FLUTTER_PEER_TAB_VISIBLE: &str = "peer-tab-visible";
pub const OPTION_FLUTTER_PEER_CARD_UI_TYLE: &str = "peer-card-ui-type";
pub const OPTION_FLUTTER_CURRENT_AB_NAME: &str = "current-ab-name";
// DEFAULT_DISPLAY_SETTINGS, OVERWRITE_DISPLAY_SETTINGS // DEFAULT_DISPLAY_SETTINGS, OVERWRITE_DISPLAY_SETTINGS
pub const KEYS_DISPLAY_SETTINGS: &[&str] = &[ pub const KEYS_DISPLAY_SETTINGS: &[&str] = &[
OPTION_VIEW_ONLY, OPTION_VIEW_ONLY,
@ -2054,7 +2067,7 @@ pub mod keys {
OPTION_ZOOM_CURSOR, OPTION_ZOOM_CURSOR,
OPTION_SHOW_QUALITY_MONITOR, OPTION_SHOW_QUALITY_MONITOR,
OPTION_DISABLE_AUDIO, OPTION_DISABLE_AUDIO,
OPTION_ENABLE_FILE_TRANSFER, OPTION_ENABLE_FILE_COPY_PASTE,
OPTION_DISABLE_CLIPBOARD, OPTION_DISABLE_CLIPBOARD,
OPTION_LOCK_AFTER_SESSION_END, OPTION_LOCK_AFTER_SESSION_END,
OPTION_PRIVACY_MODE, OPTION_PRIVACY_MODE,
@ -2081,6 +2094,16 @@ pub mod keys {
OPTION_SYNC_AB_WITH_RECENT_SESSIONS, OPTION_SYNC_AB_WITH_RECENT_SESSIONS,
OPTION_SYNC_AB_TAGS, OPTION_SYNC_AB_TAGS,
OPTION_FILTER_AB_BY_INTERSECTION, OPTION_FILTER_AB_BY_INTERSECTION,
OPTION_REMOTE_MENUBAR_DRAG_LEFT,
OPTION_REMOTE_MENUBAR_DRAG_RIGHT,
OPTION_HIDE_AB_TAGS_PANEL,
OPTION_FLUTTER_REMOTE_MENUBAR_STATE,
OPTION_FLUTTER_PEER_SORTING,
OPTION_FLUTTER_PEER_TAB_INDEX,
OPTION_FLUTTER_PEER_TAB_ORDER,
OPTION_FLUTTER_PEER_TAB_VISIBLE,
OPTION_FLUTTER_PEER_CARD_UI_TYLE,
OPTION_FLUTTER_CURRENT_AB_NAME,
]; ];
// DEFAULT_SETTINGS, OVERWRITE_SETTINGS // DEFAULT_SETTINGS, OVERWRITE_SETTINGS
pub const KEYS_SETTINGS: &[&str] = &[ pub const KEYS_SETTINGS: &[&str] = &[

View File

@ -1503,9 +1503,9 @@ impl LoginConfigHandler {
BoolOption::Yes BoolOption::Yes
}) })
.into(); .into();
} else if name == "enable-file-transfer" { } else if name == "enable-file-copy-paste" {
config.enable_file_transfer.v = !config.enable_file_transfer.v; config.enable_file_copy_paste.v = !config.enable_file_copy_paste.v;
option.enable_file_transfer = (if config.enable_file_transfer.v { option.enable_file_transfer = (if config.enable_file_copy_paste.v {
BoolOption::Yes BoolOption::Yes
} else { } else {
BoolOption::No BoolOption::No
@ -1538,7 +1538,7 @@ impl LoginConfigHandler {
option.disable_keyboard = f(false); option.disable_keyboard = f(false);
option.disable_clipboard = f(self.get_toggle_option("disable-clipboard")); option.disable_clipboard = f(self.get_toggle_option("disable-clipboard"));
option.show_remote_cursor = f(self.get_toggle_option("show-remote-cursor")); option.show_remote_cursor = f(self.get_toggle_option("show-remote-cursor"));
option.enable_file_transfer = f(self.config.enable_file_transfer.v); option.enable_file_transfer = f(self.config.enable_file_copy_paste.v);
option.lock_after_session_end = f(self.config.lock_after_session_end.v); option.lock_after_session_end = f(self.config.lock_after_session_end.v);
} }
} else { } else {
@ -1631,7 +1631,7 @@ impl LoginConfigHandler {
if self.get_toggle_option("disable-audio") { if self.get_toggle_option("disable-audio") {
msg.disable_audio = BoolOption::Yes.into(); msg.disable_audio = BoolOption::Yes.into();
} }
if !view_only && self.get_toggle_option("enable-file-transfer") { if !view_only && self.get_toggle_option(config::keys::OPTION_ENABLE_FILE_COPY_PASTE) {
msg.enable_file_transfer = BoolOption::Yes.into(); msg.enable_file_transfer = BoolOption::Yes.into();
} }
if view_only || self.get_toggle_option("disable-clipboard") { if view_only || self.get_toggle_option("disable-clipboard") {
@ -1704,8 +1704,8 @@ impl LoginConfigHandler {
self.config.lock_after_session_end.v self.config.lock_after_session_end.v
} else if name == "privacy-mode" { } else if name == "privacy-mode" {
self.config.privacy_mode.v self.config.privacy_mode.v
} else if name == "enable-file-transfer" { } else if name == config::keys::OPTION_ENABLE_FILE_COPY_PASTE {
self.config.enable_file_transfer.v self.config.enable_file_copy_paste.v
} else if name == "disable-audio" { } else if name == "disable-audio" {
self.config.disable_audio.v self.config.disable_audio.v
} else if name == "disable-clipboard" { } else if name == "disable-clipboard" {

View File

@ -319,8 +319,13 @@ impl<T: InvokeUiSession> Remote<T> {
let is_stopping_allowed = clip.is_stopping_allowed(); let is_stopping_allowed = clip.is_stopping_allowed();
let server_file_transfer_enabled = let server_file_transfer_enabled =
*self.handler.server_file_transfer_enabled.read().unwrap(); *self.handler.server_file_transfer_enabled.read().unwrap();
let file_transfer_enabled = let file_transfer_enabled = self
self.handler.lc.read().unwrap().enable_file_transfer.v; .handler
.lc
.read()
.unwrap()
.enable_file_copy_paste
.v;
let view_only = self.handler.lc.read().unwrap().view_only.v; let view_only = self.handler.lc.read().unwrap().view_only.v;
let stop = is_stopping_allowed let stop = is_stopping_allowed
&& (view_only && (view_only
@ -1760,7 +1765,13 @@ impl<T: InvokeUiSession> Remote<T> {
))] ))]
{ {
let enabled = *self.handler.server_file_transfer_enabled.read().unwrap() let enabled = *self.handler.server_file_transfer_enabled.read().unwrap()
&& self.handler.lc.read().unwrap().enable_file_transfer.v; && self
.handler
.lc
.read()
.unwrap()
.enable_file_copy_paste
.v;
ContextSend::enable(enabled); ContextSend::enable(enabled);
} }
} }
@ -1783,7 +1794,13 @@ impl<T: InvokeUiSession> Remote<T> {
}; };
let is_stopping_allowed = clip.is_stopping_allowed_from_peer(); let is_stopping_allowed = clip.is_stopping_allowed_from_peer();
let file_transfer_enabled = self.handler.lc.read().unwrap().enable_file_transfer.v; let file_transfer_enabled = self
.handler
.lc
.read()
.unwrap()
.enable_file_copy_paste
.v;
let stop = is_stopping_allowed && !file_transfer_enabled; let stop = is_stopping_allowed && !file_transfer_enabled;
log::debug!( log::debug!(
"Process clipboard message from server peer, stop: {}, is_stopping_allowed: {}, file_transfer_enabled: {}", "Process clipboard message from server peer, stop: {}, is_stopping_allowed: {}, file_transfer_enabled: {}",

View File

@ -1572,6 +1572,56 @@ pub fn load_custom_client() {
} }
} }
fn read_custom_client_advanced_settings(
settings: serde_json::Value,
map_display_settings: &HashMap<String, &&str>,
map_local_settings: &HashMap<String, &&str>,
map_settings: &HashMap<String, &&str>,
is_override: bool,
) {
let mut display_settings = if is_override {
config::OVERWRITE_DISPLAY_SETTINGS.write().unwrap()
} else {
config::DEFAULT_DISPLAY_SETTINGS.write().unwrap()
};
let mut local_settings = if is_override {
config::OVERWRITE_LOCAL_SETTINGS.write().unwrap()
} else {
config::DEFAULT_LOCAL_SETTINGS.write().unwrap()
};
let mut server_settings = if is_override {
config::OVERWRITE_SETTINGS.write().unwrap()
} else {
config::DEFAULT_SETTINGS.write().unwrap()
};
if let Some(settings) = settings.as_object() {
for (k, v) in settings {
let Some(v) = v.as_str() else {
continue;
};
if let Some(k2) = map_display_settings.get(k) {
display_settings.insert(k2.to_string(), v.to_owned());
} else if let Some(k2) = map_local_settings.get(k) {
local_settings.insert(k2.to_string(), v.to_owned());
} else if let Some(k2) = map_settings.get(k) {
server_settings.insert(k2.to_string(), v.to_owned());
} else {
let k2 = k.replace("_", "-");
let k = k2.replace("-", "_");
// display
display_settings.insert(k.clone(), v.to_owned());
display_settings.insert(k2.clone(), v.to_owned());
// local
local_settings.insert(k.clone(), v.to_owned());
local_settings.insert(k2.clone(), v.to_owned());
// server
server_settings.insert(k.clone(), v.to_owned());
server_settings.insert(k2.clone(), v.to_owned());
}
}
}
}
pub fn read_custom_client(config: &str) { pub fn read_custom_client(config: &str) {
let Ok(data) = decode64(config) else { let Ok(data) = decode64(config) else {
log::error!("Failed to decode custom client config"); log::error!("Failed to decode custom client config");
@ -1611,66 +1661,23 @@ pub fn read_custom_client(config: &str) {
for s in config::keys::KEYS_SETTINGS { for s in config::keys::KEYS_SETTINGS {
map_settings.insert(s.replace("_", "-"), s); 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() { read_custom_client_advanced_settings(
for (k, v) in default_settings { default_settings,
let Some(v) = v.as_str() else { &map_display_settings,
continue; &map_local_settings,
}; &map_settings,
if let Some(k2) = map_display_settings.get(k) { false,
config::DEFAULT_DISPLAY_SETTINGS );
.write()
.unwrap()
.insert(k2.to_string(), v.to_owned());
} else if let Some(k2) = map_local_settings.get(k) {
config::DEFAULT_LOCAL_SETTINGS
.write()
.unwrap()
.insert(k2.to_string(), v.to_owned());
} else if let Some(k2) = map_settings.get(k) {
config::DEFAULT_SETTINGS
.write()
.unwrap()
.insert(k2.to_string(), v.to_owned());
} else {
config::DEFAULT_SETTINGS
.write()
.unwrap()
.insert(k.clone(), v.to_owned());
}
}
}
} }
if let Some(overwrite_settings) = data.remove("override-settings") { if let Some(overwrite_settings) = data.remove("override-settings") {
if let Some(overwrite_settings) = overwrite_settings.as_object() { read_custom_client_advanced_settings(
for (k, v) in overwrite_settings { overwrite_settings,
let Some(v) = v.as_str() else { &map_display_settings,
continue; &map_local_settings,
}; &map_settings,
if let Some(k2) = map_display_settings.get(k) { true,
config::OVERWRITE_DISPLAY_SETTINGS );
.write()
.unwrap()
.insert(k2.to_string(), v.to_owned());
} else if let Some(k2) = map_local_settings.get(k) {
config::OVERWRITE_LOCAL_SETTINGS
.write()
.unwrap()
.insert(k2.to_string(), v.to_owned());
} else if let Some(k2) = map_settings.get(k) {
config::OVERWRITE_SETTINGS
.write()
.unwrap()
.insert(k2.to_string(), v.to_owned());
} else {
config::OVERWRITE_SETTINGS
.write()
.unwrap()
.insert(k.clone(), v.to_owned());
}
}
}
} }
for (k, v) in data { for (k, v) in data {
if let Some(v) = v.as_str() { if let Some(v) = v.as_str() {

View File

@ -775,7 +775,7 @@ pub fn main_get_error() -> String {
pub fn main_show_option(_key: String) -> SyncReturn<bool> { pub fn main_show_option(_key: String) -> SyncReturn<bool> {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
if _key.eq(config::CONFIG_OPTION_ALLOW_LINUX_HEADLESS) { if _key.eq(config::keys::OPTION_ALLOW_LINUX_HEADLESS) {
return SyncReturn(true); return SyncReturn(true);
} }
SyncReturn(false) SyncReturn(false)

View File

@ -1,6 +1,6 @@
use super::{CursorData, ResultType}; use super::{CursorData, ResultType};
use desktop::Desktop; use desktop::Desktop;
use hbb_common::config::CONFIG_OPTION_ALLOW_LINUX_HEADLESS; use hbb_common::config::keys::OPTION_ALLOW_LINUX_HEADLESS;
pub use hbb_common::platform::linux::*; pub use hbb_common::platform::linux::*;
use hbb_common::{ use hbb_common::{
allow_err, allow_err,
@ -95,7 +95,7 @@ pub struct xcb_xfixes_get_cursor_image {
#[inline] #[inline]
pub fn is_headless_allowed() -> bool { pub fn is_headless_allowed() -> bool {
Config::get_option(CONFIG_OPTION_ALLOW_LINUX_HEADLESS) == "Y" Config::get_option(OPTION_ALLOW_LINUX_HEADLESS) == "Y"
} }
#[inline] #[inline]

View File

@ -28,7 +28,7 @@ use hbb_common::platform::linux::run_cmds;
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
use hbb_common::protobuf::EnumOrUnknown; use hbb_common::protobuf::EnumOrUnknown;
use hbb_common::{ use hbb_common::{
config::Config, config::{self, Config},
fs::{self, can_enable_overwrite_detection}, fs::{self, can_enable_overwrite_detection},
futures::{SinkExt, StreamExt}, futures::{SinkExt, StreamExt},
get_time, get_version_number, get_time, get_version_number,
@ -337,7 +337,7 @@ impl Connection {
clipboard: Connection::permission("enable-clipboard"), clipboard: Connection::permission("enable-clipboard"),
audio: Connection::permission("enable-audio"), audio: Connection::permission("enable-audio"),
// to-do: make sure is the option correct here // to-do: make sure is the option correct here
file: Connection::permission("enable-file-transfer"), file: Connection::permission(config::keys::OPTION_ENABLE_FILE_TRANSFER),
restart: Connection::permission("enable-remote-restart"), restart: Connection::permission("enable-remote-restart"),
recording: Connection::permission("enable-record-session"), recording: Connection::permission("enable-record-session"),
block_input: Connection::permission("enable-block-input"), block_input: Connection::permission("enable-block-input"),
@ -1614,7 +1614,7 @@ impl Connection {
} }
match lr.union { match lr.union {
Some(login_request::Union::FileTransfer(ft)) => { Some(login_request::Union::FileTransfer(ft)) => {
if !Connection::permission("enable-file-transfer") { if !Connection::permission(config::keys::OPTION_ENABLE_FILE_TRANSFER) {
self.send_login_error("No permission of file transfer") self.send_login_error("No permission of file transfer")
.await; .await;
sleep(1.).await; sleep(1.).await;

View File

@ -198,7 +198,7 @@ class Header: Reactor.Component {
{<li #follow-remote-window .toggle-option><span>{svg_checkmark}</span>{translate('Follow remote window focus')}</li>} {<li #follow-remote-window .toggle-option><span>{svg_checkmark}</span>{translate('Follow remote window focus')}</li>}
<li #show-quality-monitor .toggle-option><span>{svg_checkmark}</span>{translate('Show quality monitor')}</li> <li #show-quality-monitor .toggle-option><span>{svg_checkmark}</span>{translate('Show quality monitor')}</li>
{audio_enabled ? <li #disable-audio .toggle-option><span>{svg_checkmark}</span>{translate('Mute')}</li> : ""} {audio_enabled ? <li #disable-audio .toggle-option><span>{svg_checkmark}</span>{translate('Mute')}</li> : ""}
{(is_win && pi.platform == "Windows") && file_enabled ? <li #enable-file-transfer .toggle-option><span>{svg_checkmark}</span>{translate('Enable file copy and paste')}</li> : ""} {(is_win && pi.platform == "Windows") && file_enabled ? <li #enable-file-copy-paste .toggle-option><span>{svg_checkmark}</span>{translate('Enable file copy and paste')}</li> : ""}
{keyboard_enabled && clipboard_enabled ? <li #disable-clipboard .toggle-option><span>{svg_checkmark}</span>{translate('Disable clipboard')}</li> : ""} {keyboard_enabled && clipboard_enabled ? <li #disable-clipboard .toggle-option><span>{svg_checkmark}</span>{translate('Disable clipboard')}</li> : ""}
{keyboard_enabled ? <li #lock-after-session-end .toggle-option><span>{svg_checkmark}</span>{translate('Lock after session end')}</li> : ""} {keyboard_enabled ? <li #lock-after-session-end .toggle-option><span>{svg_checkmark}</span>{translate('Lock after session end')}</li> : ""}
{keyboard_enabled && pi.platform == "Windows" ? <li #privacy-mode><span>{svg_checkmark}</span>{translate('Privacy mode')}</li> : ""} {keyboard_enabled && pi.platform == "Windows" ? <li #privacy-mode><span>{svg_checkmark}</span>{translate('Privacy mode')}</li> : ""}
@ -481,7 +481,7 @@ function toggleMenuState() {
for (var el in $$(menu#keyboard-options>li)) { for (var el in $$(menu#keyboard-options>li)) {
el.attributes.toggleClass("selected", values.indexOf(el.id) >= 0); el.attributes.toggleClass("selected", values.indexOf(el.id) >= 0);
} }
for (var id in ["show-remote-cursor", "follow-remote-cursor", "follow-remote-window", "show-quality-monitor", "disable-audio", "enable-file-transfer", "disable-clipboard", "lock-after-session-end", "allow_swap_key", "i444"]) { for (var id in ["show-remote-cursor", "follow-remote-cursor", "follow-remote-window", "show-quality-monitor", "disable-audio", "enable-file-copy-paste", "disable-clipboard", "lock-after-session-end", "allow_swap_key", "i444"]) {
var el = self.select('#' + id); var el = self.select('#' + id);
if (el) { if (el) {
var value = handler.get_toggle_option(id); var value = handler.get_toggle_option(id);

View File

@ -574,7 +574,9 @@ pub async fn start_ipc<T: InvokeUiCM>(cm: ConnectionManager<T>) {
feature = "unix-file-copy-paste" feature = "unix-file-copy-paste"
), ),
))] ))]
ContextSend::enable(Config::get_option("enable-file-transfer").is_empty()); ContextSend::enable(
Config::get_option(hbb_common::config::keys::OPTION_ENABLE_FILE_TRANSFER).is_empty(),
);
match ipc::new_listener("_cm").await { match ipc::new_listener("_cm").await {
Ok(mut incoming) => { Ok(mut incoming) => {

View File

@ -1120,7 +1120,7 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver<ipc:
) )
))] ))]
{ {
let b = OPTIONS.lock().unwrap().get("enable-file-transfer").map(|x| x.to_string()).unwrap_or_default(); let b = OPTIONS.lock().unwrap().get(config::keys::OPTION_ENABLE_FILE_TRANSFER).map(|x| x.to_string()).unwrap_or_default();
if b != enable_file_transfer { if b != enable_file_transfer {
clipboard::ContextSend::enable(b.is_empty()); clipboard::ContextSend::enable(b.is_empty());
enable_file_transfer = b; enable_file_transfer = b;

View File

@ -312,7 +312,7 @@ impl<T: InvokeUiSession> Session<T> {
pub fn toggle_option(&self, name: String) { pub fn toggle_option(&self, name: String) {
let msg = self.lc.write().unwrap().toggle_option(name.clone()); let msg = self.lc.write().unwrap().toggle_option(name.clone());
#[cfg(not(feature = "flutter"))] #[cfg(not(feature = "flutter"))]
if name == "enable-file-transfer" { if name == hbb_common::config::keys::OPTION_ENABLE_FILE_COPY_PASTE {
self.send(Data::ToggleClipboardFile); self.send(Data::ToggleClipboardFile);
} }
if let Some(msg) = msg { if let Some(msg) = msg {