123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918 |
- import 'dart:convert';
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- import 'package:flutter_hbb/common.dart';
- import 'package:flutter_hbb/common/shared_state.dart';
- import 'package:flutter_hbb/common/widgets/dialog.dart';
- import 'package:flutter_hbb/consts.dart';
- import 'package:flutter_hbb/desktop/widgets/remote_toolbar.dart';
- import 'package:flutter_hbb/models/model.dart';
- import 'package:flutter_hbb/models/platform_model.dart';
- import 'package:get/get.dart';
- bool isEditOsPassword = false;
- class TTextMenu {
- final Widget child;
- final VoidCallback onPressed;
- Widget? trailingIcon;
- bool divider;
- TTextMenu(
- {required this.child,
- required this.onPressed,
- this.trailingIcon,
- this.divider = false});
- Widget getChild() {
- if (trailingIcon != null) {
- return Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- child,
- trailingIcon!,
- ],
- );
- } else {
- return child;
- }
- }
- }
- class TRadioMenu<T> {
- final Widget child;
- final T value;
- final T groupValue;
- final ValueChanged<T?>? onChanged;
- TRadioMenu(
- {required this.child,
- required this.value,
- required this.groupValue,
- required this.onChanged});
- }
- class TToggleMenu {
- final Widget child;
- final bool value;
- final ValueChanged<bool?>? onChanged;
- TToggleMenu(
- {required this.child, required this.value, required this.onChanged});
- }
- handleOsPasswordEditIcon(
- SessionID sessionId, OverlayDialogManager dialogManager) {
- isEditOsPassword = true;
- showSetOSPassword(
- sessionId, false, dialogManager, null, () => isEditOsPassword = false);
- }
- handleOsPasswordAction(
- SessionID sessionId, OverlayDialogManager dialogManager) async {
- if (isEditOsPassword) {
- isEditOsPassword = false;
- return;
- }
- final password =
- await bind.sessionGetOption(sessionId: sessionId, arg: 'os-password') ??
- '';
- if (password.isEmpty) {
- showSetOSPassword(sessionId, true, dialogManager, password,
- () => isEditOsPassword = false);
- } else {
- bind.sessionInputOsPassword(sessionId: sessionId, value: password);
- }
- }
- List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) {
- final ffiModel = ffi.ffiModel;
- final pi = ffiModel.pi;
- final perms = ffiModel.permissions;
- final sessionId = ffi.sessionId;
- List<TTextMenu> v = [];
- // elevation
- if (perms['keyboard'] != false && ffi.elevationModel.showRequestMenu) {
- v.add(
- TTextMenu(
- child: Text(translate('Request Elevation')),
- onPressed: () =>
- showRequestElevationDialog(sessionId, ffi.dialogManager)),
- );
- }
- // osAccount / osPassword
- if (perms['keyboard'] != false) {
- v.add(
- TTextMenu(
- child: Row(children: [
- Text(translate(pi.isHeadless ? 'OS Account' : 'OS Password')),
- ]),
- trailingIcon: Transform.scale(
- scale: (isDesktop || isWebDesktop) ? 0.8 : 1,
- child: IconButton(
- onPressed: () {
- if (isMobile && Navigator.canPop(context)) {
- Navigator.pop(context);
- }
- if (pi.isHeadless) {
- showSetOSAccount(sessionId, ffi.dialogManager);
- } else {
- handleOsPasswordEditIcon(sessionId, ffi.dialogManager);
- }
- },
- icon: Icon(Icons.edit, color: isMobile ? MyTheme.accent : null),
- ),
- ),
- onPressed: () => pi.isHeadless
- ? showSetOSAccount(sessionId, ffi.dialogManager)
- : handleOsPasswordAction(sessionId, ffi.dialogManager),
- ),
- );
- }
- // paste
- if (pi.platform != kPeerPlatformAndroid && perms['keyboard'] != false) {
- v.add(TTextMenu(
- child: Text(translate('Send clipboard keystrokes')),
- onPressed: () async {
- ClipboardData? data = await Clipboard.getData(Clipboard.kTextPlain);
- if (data != null && data.text != null) {
- bind.sessionInputString(
- sessionId: sessionId, value: data.text ?? "");
- }
- }));
- }
- // reset canvas
- if (isMobile) {
- v.add(TTextMenu(
- child: Text(translate('Reset canvas')),
- onPressed: () => ffi.cursorModel.reset()));
- }
- connectWithToken(
- {required bool isFileTransfer, required bool isTcpTunneling}) {
- final connToken = bind.sessionGetConnToken(sessionId: ffi.sessionId);
- connect(context, id,
- isFileTransfer: isFileTransfer,
- isTcpTunneling: isTcpTunneling,
- connToken: connToken);
- }
- // transferFile
- if (isDesktop) {
- v.add(
- TTextMenu(
- child: Text(translate('Transfer file')),
- onPressed: () =>
- connectWithToken(isFileTransfer: true, isTcpTunneling: false)),
- );
- }
- // tcpTunneling
- if (isDesktop) {
- v.add(
- TTextMenu(
- child: Text(translate('TCP tunneling')),
- onPressed: () =>
- connectWithToken(isFileTransfer: false, isTcpTunneling: true)),
- );
- }
- // note
- if (bind
- .sessionGetAuditServerSync(sessionId: sessionId, typ: "conn")
- .isNotEmpty) {
- v.add(
- TTextMenu(
- child: Text(translate('Note')),
- onPressed: () => showAuditDialog(ffi)),
- );
- }
- // divider
- if (isDesktop || isWebDesktop) {
- v.add(TTextMenu(child: Offstage(), onPressed: () {}, divider: true));
- }
- // ctrlAltDel
- if (!ffiModel.viewOnly &&
- ffiModel.keyboard &&
- (pi.platform == kPeerPlatformLinux || pi.sasEnabled)) {
- v.add(
- TTextMenu(
- child: Text('${translate("Insert Ctrl + Alt + Del")}'),
- onPressed: () => bind.sessionCtrlAltDel(sessionId: sessionId)),
- );
- }
- // restart
- if (perms['restart'] != false &&
- (pi.platform == kPeerPlatformLinux ||
- pi.platform == kPeerPlatformWindows ||
- pi.platform == kPeerPlatformMacOS)) {
- v.add(
- TTextMenu(
- child: Text(translate('Restart remote device')),
- onPressed: () =>
- showRestartRemoteDevice(pi, id, sessionId, ffi.dialogManager)),
- );
- }
- // insertLock
- if (!ffiModel.viewOnly && ffi.ffiModel.keyboard) {
- v.add(
- TTextMenu(
- child: Text(translate('Insert Lock')),
- onPressed: () => bind.sessionLockScreen(sessionId: sessionId)),
- );
- }
- // blockUserInput
- if (ffi.ffiModel.keyboard &&
- ffi.ffiModel.permissions['block_input'] != false &&
- pi.platform == kPeerPlatformWindows) // privacy-mode != true ??
- {
- v.add(TTextMenu(
- child: Obx(() => Text(translate(
- '${BlockInputState.find(id).value ? 'Unb' : 'B'}lock user input'))),
- onPressed: () {
- RxBool blockInput = BlockInputState.find(id);
- bind.sessionToggleOption(
- sessionId: sessionId,
- value: '${blockInput.value ? 'un' : ''}block-input');
- blockInput.value = !blockInput.value;
- }));
- }
- // switchSides
- if (isDesktop &&
- ffiModel.keyboard &&
- pi.platform != kPeerPlatformAndroid &&
- pi.platform != kPeerPlatformMacOS &&
- versionCmp(pi.version, '1.2.0') >= 0 &&
- bind.peerGetDefaultSessionsCount(id: id) == 1) {
- v.add(TTextMenu(
- child: Text(translate('Switch Sides')),
- onPressed: () =>
- showConfirmSwitchSidesDialog(sessionId, id, ffi.dialogManager)));
- }
- // refresh
- if (pi.version.isNotEmpty) {
- v.add(TTextMenu(
- child: Text(translate('Refresh')),
- onPressed: () => sessionRefreshVideo(sessionId, pi),
- ));
- }
- // record
- if (!(isDesktop || isWeb) &&
- (ffi.recordingModel.start || (perms["recording"] != false))) {
- v.add(TTextMenu(
- child: Row(
- children: [
- Text(translate(ffi.recordingModel.start
- ? 'Stop session recording'
- : 'Start session recording')),
- Padding(
- padding: EdgeInsets.only(left: 12),
- child: Icon(
- ffi.recordingModel.start
- ? Icons.pause_circle_filled
- : Icons.videocam_outlined,
- color: MyTheme.accent),
- )
- ],
- ),
- onPressed: () => ffi.recordingModel.toggle()));
- }
- // fingerprint
- if (!(isDesktop || isWebDesktop)) {
- v.add(TTextMenu(
- child: Text(translate('Copy Fingerprint')),
- onPressed: () => onCopyFingerprint(FingerprintState.find(id).value),
- ));
- }
- return v;
- }
- Future<List<TRadioMenu<String>>> toolbarViewStyle(
- BuildContext context, String id, FFI ffi) async {
- final groupValue =
- await bind.sessionGetViewStyle(sessionId: ffi.sessionId) ?? '';
- void onChanged(String? value) async {
- if (value == null) return;
- bind
- .sessionSetViewStyle(sessionId: ffi.sessionId, value: value)
- .then((_) => ffi.canvasModel.updateViewStyle());
- }
- return [
- TRadioMenu<String>(
- child: Text(translate('Scale original')),
- value: kRemoteViewStyleOriginal,
- groupValue: groupValue,
- onChanged: onChanged),
- TRadioMenu<String>(
- child: Text(translate('Scale adaptive')),
- value: kRemoteViewStyleAdaptive,
- groupValue: groupValue,
- onChanged: onChanged)
- ];
- }
- Future<List<TRadioMenu<String>>> toolbarImageQuality(
- BuildContext context, String id, FFI ffi) async {
- final groupValue =
- await bind.sessionGetImageQuality(sessionId: ffi.sessionId) ?? '';
- onChanged(String? value) async {
- if (value == null) return;
- await bind.sessionSetImageQuality(sessionId: ffi.sessionId, value: value);
- }
- return [
- TRadioMenu<String>(
- child: Text(translate('Good image quality')),
- value: kRemoteImageQualityBest,
- groupValue: groupValue,
- onChanged: onChanged),
- TRadioMenu<String>(
- child: Text(translate('Balanced')),
- value: kRemoteImageQualityBalanced,
- groupValue: groupValue,
- onChanged: onChanged),
- TRadioMenu<String>(
- child: Text(translate('Optimize reaction time')),
- value: kRemoteImageQualityLow,
- groupValue: groupValue,
- onChanged: onChanged),
- TRadioMenu<String>(
- child: Text(translate('Custom')),
- value: kRemoteImageQualityCustom,
- groupValue: groupValue,
- onChanged: (value) {
- onChanged(value);
- customImageQualityDialog(ffi.sessionId, id, ffi);
- },
- ),
- ];
- }
- Future<List<TRadioMenu<String>>> toolbarCodec(
- BuildContext context, String id, FFI ffi) async {
- final sessionId = ffi.sessionId;
- final alternativeCodecs =
- await bind.sessionAlternativeCodecs(sessionId: sessionId);
- final groupValue = await bind.sessionGetOption(
- sessionId: sessionId, arg: kOptionCodecPreference) ??
- '';
- final List<bool> codecs = [];
- try {
- final Map codecsJson = jsonDecode(alternativeCodecs);
- final vp8 = codecsJson['vp8'] ?? false;
- final av1 = codecsJson['av1'] ?? false;
- final h264 = codecsJson['h264'] ?? false;
- final h265 = codecsJson['h265'] ?? false;
- codecs.add(vp8);
- codecs.add(av1);
- codecs.add(h264);
- codecs.add(h265);
- } catch (e) {
- debugPrint("Show Codec Preference err=$e");
- }
- final visible =
- codecs.length == 4 && (codecs[0] || codecs[1] || codecs[2] || codecs[3]);
- if (!visible) return [];
- onChanged(String? value) async {
- if (value == null) return;
- await bind.sessionPeerOption(
- sessionId: sessionId, name: kOptionCodecPreference, value: value);
- bind.sessionChangePreferCodec(sessionId: sessionId);
- }
- TRadioMenu<String> radio(String label, String value, bool enabled) {
- return TRadioMenu<String>(
- child: Text(label),
- value: value,
- groupValue: groupValue,
- onChanged: enabled ? onChanged : null);
- }
- var autoLabel = translate('Auto');
- if (groupValue == 'auto' &&
- ffi.qualityMonitorModel.data.codecFormat != null) {
- autoLabel = '$autoLabel (${ffi.qualityMonitorModel.data.codecFormat})';
- }
- return [
- radio(autoLabel, 'auto', true),
- if (codecs[0]) radio('VP8', 'vp8', codecs[0]),
- radio('VP9', 'vp9', true),
- if (codecs[1]) radio('AV1', 'av1', codecs[1]),
- if (codecs[2]) radio('H264', 'h264', codecs[2]),
- if (codecs[3]) radio('H265', 'h265', codecs[3]),
- ];
- }
- Future<List<TToggleMenu>> toolbarCursor(
- BuildContext context, String id, FFI ffi) async {
- List<TToggleMenu> v = [];
- final ffiModel = ffi.ffiModel;
- final pi = ffiModel.pi;
- final sessionId = ffi.sessionId;
- // show remote cursor
- if (pi.platform != kPeerPlatformAndroid &&
- !ffi.canvasModel.cursorEmbedded &&
- !pi.isWayland) {
- final state = ShowRemoteCursorState.find(id);
- final lockState = ShowRemoteCursorLockState.find(id);
- final enabled = !ffiModel.viewOnly;
- final option = 'show-remote-cursor';
- if (pi.currentDisplay == kAllDisplayValue ||
- bind.sessionIsMultiUiSession(sessionId: sessionId)) {
- lockState.value = false;
- }
- v.add(TToggleMenu(
- child: Text(translate('Show remote cursor')),
- value: state.value,
- onChanged: enabled && !lockState.value
- ? (value) async {
- if (value == null) return;
- await bind.sessionToggleOption(
- sessionId: sessionId, value: option);
- state.value = bind.sessionGetToggleOptionSync(
- sessionId: sessionId, arg: option);
- }
- : null));
- }
- // follow remote cursor
- if (pi.platform != kPeerPlatformAndroid &&
- !ffi.canvasModel.cursorEmbedded &&
- !pi.isWayland &&
- versionCmp(pi.version, "1.2.4") >= 0 &&
- pi.displays.length > 1 &&
- pi.currentDisplay != kAllDisplayValue &&
- !bind.sessionIsMultiUiSession(sessionId: sessionId)) {
- final option = 'follow-remote-cursor';
- final value =
- bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
- final showCursorOption = 'show-remote-cursor';
- final showCursorState = ShowRemoteCursorState.find(id);
- final showCursorLockState = ShowRemoteCursorLockState.find(id);
- final showCursorEnabled = bind.sessionGetToggleOptionSync(
- sessionId: sessionId, arg: showCursorOption);
- showCursorLockState.value = value;
- if (value && !showCursorEnabled) {
- await bind.sessionToggleOption(
- sessionId: sessionId, value: showCursorOption);
- showCursorState.value = bind.sessionGetToggleOptionSync(
- sessionId: sessionId, arg: showCursorOption);
- }
- v.add(TToggleMenu(
- child: Text(translate('Follow remote cursor')),
- value: value,
- onChanged: (value) async {
- if (value == null) return;
- await bind.sessionToggleOption(sessionId: sessionId, value: option);
- value = bind.sessionGetToggleOptionSync(
- sessionId: sessionId, arg: option);
- showCursorLockState.value = value;
- if (!showCursorEnabled) {
- await bind.sessionToggleOption(
- sessionId: sessionId, value: showCursorOption);
- showCursorState.value = bind.sessionGetToggleOptionSync(
- sessionId: sessionId, arg: showCursorOption);
- }
- }));
- }
- // follow remote window focus
- if (pi.platform != kPeerPlatformAndroid &&
- !ffi.canvasModel.cursorEmbedded &&
- !pi.isWayland &&
- versionCmp(pi.version, "1.2.4") >= 0 &&
- pi.displays.length > 1 &&
- pi.currentDisplay != kAllDisplayValue &&
- !bind.sessionIsMultiUiSession(sessionId: sessionId)) {
- final option = 'follow-remote-window';
- final value =
- bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
- v.add(TToggleMenu(
- child: Text(translate('Follow remote window focus')),
- value: value,
- onChanged: (value) async {
- if (value == null) return;
- await bind.sessionToggleOption(sessionId: sessionId, value: option);
- value = bind.sessionGetToggleOptionSync(
- sessionId: sessionId, arg: option);
- }));
- }
- // zoom cursor
- final viewStyle = await bind.sessionGetViewStyle(sessionId: sessionId) ?? '';
- if (!isMobile &&
- pi.platform != kPeerPlatformAndroid &&
- viewStyle != kRemoteViewStyleOriginal) {
- final option = 'zoom-cursor';
- final peerState = PeerBoolOption.find(id, option);
- v.add(TToggleMenu(
- child: Text(translate('Zoom cursor')),
- value: peerState.value,
- onChanged: (value) async {
- if (value == null) return;
- await bind.sessionToggleOption(sessionId: sessionId, value: option);
- peerState.value =
- bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
- },
- ));
- }
- return v;
- }
- Future<List<TToggleMenu>> toolbarDisplayToggle(
- BuildContext context, String id, FFI ffi) async {
- List<TToggleMenu> v = [];
- final ffiModel = ffi.ffiModel;
- final pi = ffiModel.pi;
- final perms = ffiModel.permissions;
- final sessionId = ffi.sessionId;
- // show quality monitor
- final option = 'show-quality-monitor';
- v.add(TToggleMenu(
- value: bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option),
- onChanged: (value) async {
- if (value == null) return;
- await bind.sessionToggleOption(sessionId: sessionId, value: option);
- ffi.qualityMonitorModel.checkShowQualityMonitor(sessionId);
- },
- child: Text(translate('Show quality monitor'))));
- // mute
- if (perms['audio'] != false) {
- final option = 'disable-audio';
- final value =
- bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
- v.add(TToggleMenu(
- value: value,
- onChanged: (value) {
- if (value == null) return;
- bind.sessionToggleOption(sessionId: sessionId, value: option);
- },
- child: Text(translate('Mute'))));
- }
- // file copy and paste
- // If the version is less than 1.2.4, file copy and paste is supported on Windows only.
- final isSupportIfPeer_1_2_3 = versionCmp(pi.version, '1.2.4') < 0 &&
- isWindows &&
- pi.platform == kPeerPlatformWindows;
- // If the version is 1.2.4 or later, file copy and paste is supported when kPlatformAdditionsHasFileClipboard is set.
- final isSupportIfPeer_1_2_4 = versionCmp(pi.version, '1.2.4') >= 0 &&
- bind.mainHasFileClipboard() &&
- pi.platformAdditions.containsKey(kPlatformAdditionsHasFileClipboard);
- if (ffiModel.keyboard &&
- perms['file'] != false &&
- (isSupportIfPeer_1_2_3 || isSupportIfPeer_1_2_4)) {
- final enabled = !ffiModel.viewOnly;
- final value = bind.sessionGetToggleOptionSync(
- sessionId: sessionId, arg: kOptionEnableFileCopyPaste);
- v.add(TToggleMenu(
- value: value,
- onChanged: enabled
- ? (value) {
- if (value == null) return;
- bind.sessionToggleOption(
- sessionId: sessionId, value: kOptionEnableFileCopyPaste);
- }
- : null,
- child: Text(translate('Enable file copy and paste'))));
- }
- // disable clipboard
- if (ffiModel.keyboard && perms['clipboard'] != false) {
- final enabled = !ffiModel.viewOnly;
- final option = 'disable-clipboard';
- var value =
- bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
- if (ffiModel.viewOnly) value = true;
- v.add(TToggleMenu(
- value: value,
- onChanged: enabled
- ? (value) {
- if (value == null) return;
- bind.sessionToggleOption(sessionId: sessionId, value: option);
- }
- : null,
- child: Text(translate('Disable clipboard'))));
- }
- // lock after session end
- if (ffiModel.keyboard && !ffiModel.isPeerAndroid) {
- final enabled = !ffiModel.viewOnly;
- final option = 'lock-after-session-end';
- final value =
- bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
- v.add(TToggleMenu(
- value: value,
- onChanged: enabled
- ? (value) {
- if (value == null) return;
- bind.sessionToggleOption(sessionId: sessionId, value: option);
- }
- : null,
- child: Text(translate('Lock after session end'))));
- }
- if (pi.isSupportMultiDisplay &&
- PrivacyModeState.find(id).isEmpty &&
- pi.displaysCount.value > 1 &&
- bind.mainGetUserDefaultOption(key: kKeyShowMonitorsToolbar) == 'Y') {
- final value =
- bind.sessionGetDisplaysAsIndividualWindows(sessionId: ffi.sessionId) ==
- 'Y';
- v.add(TToggleMenu(
- value: value,
- onChanged: (value) {
- if (value == null) return;
- bind.sessionSetDisplaysAsIndividualWindows(
- sessionId: sessionId, value: value ? 'Y' : 'N');
- },
- child: Text(translate('Show displays as individual windows'))));
- }
- final isMultiScreens = !isWeb && (await getScreenRectList()).length > 1;
- if (pi.isSupportMultiDisplay && isMultiScreens) {
- final value = bind.sessionGetUseAllMyDisplaysForTheRemoteSession(
- sessionId: ffi.sessionId) ==
- 'Y';
- v.add(TToggleMenu(
- value: value,
- onChanged: (value) {
- if (value == null) return;
- bind.sessionSetUseAllMyDisplaysForTheRemoteSession(
- sessionId: sessionId, value: value ? 'Y' : 'N');
- },
- child: Text(translate('Use all my displays for the remote session'))));
- }
- // 444
- final codec_format = ffi.qualityMonitorModel.data.codecFormat;
- if (versionCmp(pi.version, "1.2.4") >= 0 &&
- (codec_format == "AV1" || codec_format == "VP9")) {
- final option = 'i444';
- final value =
- bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
- v.add(TToggleMenu(
- value: value,
- onChanged: (value) async {
- if (value == null) return;
- await bind.sessionToggleOption(sessionId: sessionId, value: option);
- bind.sessionChangePreferCodec(sessionId: sessionId);
- },
- child: Text(translate('True color (4:4:4)'))));
- }
- if (isMobile) {
- v.addAll(toolbarKeyboardToggles(ffi));
- }
- // view mode (mobile only, desktop is in keyboard menu)
- if (isMobile && versionCmp(pi.version, '1.2.0') >= 0) {
- v.add(TToggleMenu(
- value: ffiModel.viewOnly,
- onChanged: (value) async {
- if (value == null) return;
- await bind.sessionToggleOption(
- sessionId: ffi.sessionId, value: kOptionToggleViewOnly);
- ffiModel.setViewOnly(id, value);
- },
- child: Text(translate('View Mode'))));
- }
- return v;
- }
- var togglePrivacyModeTime = DateTime.now().subtract(const Duration(hours: 1));
- List<TToggleMenu> toolbarPrivacyMode(
- RxString privacyModeState, BuildContext context, String id, FFI ffi) {
- final ffiModel = ffi.ffiModel;
- final pi = ffiModel.pi;
- final sessionId = ffi.sessionId;
- getDefaultMenu(Future<void> Function(SessionID sid, String opt) toggleFunc) {
- final enabled = !ffi.ffiModel.viewOnly;
- return TToggleMenu(
- value: privacyModeState.isNotEmpty,
- onChanged: enabled
- ? (value) {
- if (value == null) return;
- if (ffiModel.pi.currentDisplay != 0 &&
- ffiModel.pi.currentDisplay != kAllDisplayValue) {
- msgBox(
- sessionId,
- 'custom-nook-nocancel-hasclose',
- 'info',
- 'Please switch to Display 1 first',
- '',
- ffi.dialogManager);
- return;
- }
- final option = 'privacy-mode';
- toggleFunc(sessionId, option);
- }
- : null,
- child: Text(translate('Privacy mode')));
- }
- final privacyModeImpls =
- pi.platformAdditions[kPlatformAdditionsSupportedPrivacyModeImpl]
- as List<dynamic>?;
- if (privacyModeImpls == null) {
- return [
- getDefaultMenu((sid, opt) async {
- bind.sessionToggleOption(sessionId: sid, value: opt);
- togglePrivacyModeTime = DateTime.now();
- })
- ];
- }
- if (privacyModeImpls.isEmpty) {
- return [];
- }
- if (privacyModeImpls.length == 1) {
- final implKey = (privacyModeImpls[0] as List<dynamic>)[0] as String;
- return [
- getDefaultMenu((sid, opt) async {
- bind.sessionTogglePrivacyMode(
- sessionId: sid, implKey: implKey, on: privacyModeState.isEmpty);
- togglePrivacyModeTime = DateTime.now();
- })
- ];
- } else {
- return privacyModeImpls.map((e) {
- final implKey = (e as List<dynamic>)[0] as String;
- final implName = (e)[1] as String;
- return TToggleMenu(
- child: Text(translate(implName)),
- value: privacyModeState.value == implKey,
- onChanged: (value) {
- if (value == null) return;
- togglePrivacyModeTime = DateTime.now();
- bind.sessionTogglePrivacyMode(
- sessionId: sessionId, implKey: implKey, on: value);
- });
- }).toList();
- }
- }
- List<TToggleMenu> toolbarKeyboardToggles(FFI ffi) {
- final ffiModel = ffi.ffiModel;
- final pi = ffiModel.pi;
- final sessionId = ffi.sessionId;
- List<TToggleMenu> v = [];
- // swap key
- if (ffiModel.keyboard &&
- ((isMacOS && pi.platform != kPeerPlatformMacOS) ||
- (!isMacOS && pi.platform == kPeerPlatformMacOS))) {
- final option = 'allow_swap_key';
- final value =
- bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
- onChanged(bool? value) {
- if (value == null) return;
- bind.sessionToggleOption(sessionId: sessionId, value: option);
- }
- final enabled = !ffi.ffiModel.viewOnly;
- v.add(TToggleMenu(
- value: value,
- onChanged: enabled ? onChanged : null,
- child: Text(translate('Swap control-command key'))));
- }
- // reverse mouse wheel
- if (ffiModel.keyboard) {
- var optionValue =
- bind.sessionGetReverseMouseWheelSync(sessionId: sessionId) ?? '';
- if (optionValue == '') {
- optionValue = bind.mainGetUserDefaultOption(key: kKeyReverseMouseWheel);
- }
- onChanged(bool? value) async {
- if (value == null) return;
- await bind.sessionSetReverseMouseWheel(
- sessionId: sessionId, value: value ? 'Y' : 'N');
- }
- final enabled = !ffi.ffiModel.viewOnly;
- v.add(TToggleMenu(
- value: optionValue == 'Y',
- onChanged: enabled ? onChanged : null,
- child: Text(translate('Reverse mouse wheel'))));
- }
- // swap left right mouse
- if (ffiModel.keyboard) {
- final option = 'swap-left-right-mouse';
- final value =
- bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
- onChanged(bool? value) {
- if (value == null) return;
- bind.sessionToggleOption(sessionId: sessionId, value: option);
- }
- final enabled = !ffi.ffiModel.viewOnly;
- v.add(TToggleMenu(
- value: value,
- onChanged: enabled ? onChanged : null,
- child: Text(translate('swap-left-right-mouse'))));
- }
- return v;
- }
- bool showVirtualDisplayMenu(FFI ffi) {
- if (ffi.ffiModel.pi.platform != kPeerPlatformWindows) {
- return false;
- }
- if (!ffi.ffiModel.pi.isInstalled) {
- return false;
- }
- if (ffi.ffiModel.pi.isRustDeskIdd || ffi.ffiModel.pi.isAmyuniIdd) {
- return true;
- }
- return false;
- }
- List<Widget> getVirtualDisplayMenuChildren(
- FFI ffi, String id, VoidCallback? clickCallBack) {
- if (!showVirtualDisplayMenu(ffi)) {
- return [];
- }
- final pi = ffi.ffiModel.pi;
- final privacyModeState = PrivacyModeState.find(id);
- if (pi.isRustDeskIdd) {
- final virtualDisplays = ffi.ffiModel.pi.RustDeskVirtualDisplays;
- final children = <Widget>[];
- for (var i = 0; i < kMaxVirtualDisplayCount; i++) {
- children.add(Obx(() => CkbMenuButton(
- value: virtualDisplays.contains(i + 1),
- onChanged: privacyModeState.isNotEmpty
- ? null
- : (bool? value) async {
- if (value != null) {
- bind.sessionToggleVirtualDisplay(
- sessionId: ffi.sessionId, index: i + 1, on: value);
- clickCallBack?.call();
- }
- },
- child: Text('${translate('Virtual display')} ${i + 1}'),
- ffi: ffi,
- )));
- }
- children.add(Divider());
- children.add(Obx(() => MenuButton(
- onPressed: privacyModeState.isNotEmpty
- ? null
- : () {
- bind.sessionToggleVirtualDisplay(
- sessionId: ffi.sessionId,
- index: kAllVirtualDisplay,
- on: false);
- clickCallBack?.call();
- },
- ffi: ffi,
- child: Text(translate('Plug out all')),
- )));
- return children;
- }
- if (pi.isAmyuniIdd) {
- final count = ffi.ffiModel.pi.amyuniVirtualDisplayCount;
- final children = <Widget>[
- Obx(() => Row(
- children: [
- TextButton(
- onPressed: privacyModeState.isNotEmpty || count == 0
- ? null
- : () {
- bind.sessionToggleVirtualDisplay(
- sessionId: ffi.sessionId, index: 0, on: false);
- clickCallBack?.call();
- },
- child: Icon(Icons.remove),
- ),
- Text(count.toString()),
- TextButton(
- onPressed: privacyModeState.isNotEmpty || count == 4
- ? null
- : () {
- bind.sessionToggleVirtualDisplay(
- sessionId: ffi.sessionId, index: 0, on: true);
- clickCallBack?.call();
- },
- child: Icon(Icons.add),
- ),
- ],
- )),
- Divider(),
- Obx(() => MenuButton(
- onPressed: privacyModeState.isNotEmpty || count == 0
- ? null
- : () {
- bind.sessionToggleVirtualDisplay(
- sessionId: ffi.sessionId,
- index: kAllVirtualDisplay,
- on: false);
- clickCallBack?.call();
- },
- ffi: ffi,
- child: Text(translate('Plug out all')),
- )),
- ];
- return children;
- }
- return [];
- }
|