| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  | 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/models/model.dart'; | 
					
						
							|  |  |  | import 'package:flutter_hbb/models/platform_model.dart'; | 
					
						
							| 
									
										
										
										
											2023-10-16 07:26:55 +08:00
										 |  |  | import 'package:flutter_hbb/models/desktop_render_texture.dart'; | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  | import 'package:get/get.dart'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-04 19:43:39 +08:00
										 |  |  | bool isEditOsPassword = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  | class TTextMenu { | 
					
						
							|  |  |  |   final Widget child; | 
					
						
							|  |  |  |   final VoidCallback onPressed; | 
					
						
							|  |  |  |   Widget? trailingIcon; | 
					
						
							|  |  |  |   bool divider; | 
					
						
							|  |  |  |   TTextMenu( | 
					
						
							|  |  |  |       {required this.child, | 
					
						
							|  |  |  |       required this.onPressed, | 
					
						
							|  |  |  |       this.trailingIcon, | 
					
						
							|  |  |  |       this.divider = false}); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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}); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-04 20:26:43 +08:00
										 |  |  | handleOsPasswordEditIcon( | 
					
						
							|  |  |  |     SessionID sessionId, OverlayDialogManager dialogManager) { | 
					
						
							|  |  |  |   isEditOsPassword = true; | 
					
						
							| 
									
										
										
										
											2023-09-30 19:47:59 +08:00
										 |  |  |   showSetOSPassword( | 
					
						
							|  |  |  |       sessionId, false, dialogManager, null, () => isEditOsPassword = false); | 
					
						
							| 
									
										
										
										
											2023-07-04 20:26:43 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | handleOsPasswordAction( | 
					
						
							|  |  |  |     SessionID sessionId, OverlayDialogManager dialogManager) async { | 
					
						
							|  |  |  |   if (isEditOsPassword) { | 
					
						
							|  |  |  |     isEditOsPassword = false; | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   final password = | 
					
						
							|  |  |  |       await bind.sessionGetOption(sessionId: sessionId, arg: 'os-password') ?? | 
					
						
							|  |  |  |           ''; | 
					
						
							|  |  |  |   if (password.isEmpty) { | 
					
						
							| 
									
										
										
										
											2023-09-30 19:47:59 +08:00
										 |  |  |     showSetOSPassword(sessionId, true, dialogManager, password, | 
					
						
							|  |  |  |         () => isEditOsPassword = false); | 
					
						
							| 
									
										
										
										
											2023-07-04 20:26:43 +08:00
										 |  |  |   } else { | 
					
						
							|  |  |  |     bind.sessionInputOsPassword(sessionId: sessionId, value: password); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  | List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) { | 
					
						
							|  |  |  |   final ffiModel = ffi.ffiModel; | 
					
						
							|  |  |  |   final pi = ffiModel.pi; | 
					
						
							|  |  |  |   final perms = ffiModel.permissions; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |   final sessionId = ffi.sessionId; | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   List<TTextMenu> v = []; | 
					
						
							|  |  |  |   // elevation
 | 
					
						
							| 
									
										
										
										
											2023-09-30 19:47:59 +08:00
										 |  |  |   if (perms['keyboard'] != false && ffi.elevationModel.showRequestMenu) { | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     v.add( | 
					
						
							|  |  |  |       TTextMenu( | 
					
						
							|  |  |  |           child: Text(translate('Request Elevation')), | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |           onPressed: () => | 
					
						
							|  |  |  |               showRequestElevationDialog(sessionId, ffi.dialogManager)), | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // osAccount / osPassword
 | 
					
						
							| 
									
										
										
										
											2023-11-23 19:17:19 +08:00
										 |  |  |   if (perms['keyboard'] != false) { | 
					
						
							|  |  |  |     v.add( | 
					
						
							|  |  |  |       TTextMenu( | 
					
						
							|  |  |  |         child: Row(children: [ | 
					
						
							|  |  |  |           Text(translate(pi.isHeadless ? 'OS Account' : 'OS Password')), | 
					
						
							|  |  |  |         ]), | 
					
						
							|  |  |  |         trailingIcon: Transform.scale( | 
					
						
							| 
									
										
										
										
											2024-03-28 11:38:11 +08:00
										 |  |  |           scale: (isDesktop || isWebDesktop) ? 0.8 : 1, | 
					
						
							| 
									
										
										
										
											2023-11-23 19:17:19 +08:00
										 |  |  |           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), | 
					
						
							|  |  |  |           ), | 
					
						
							| 
									
										
										
										
											2023-07-04 20:26:43 +08:00
										 |  |  |         ), | 
					
						
							| 
									
										
										
										
											2023-11-23 19:17:19 +08:00
										 |  |  |         onPressed: () => pi.isHeadless | 
					
						
							|  |  |  |             ? showSetOSAccount(sessionId, ffi.dialogManager) | 
					
						
							|  |  |  |             : handleOsPasswordAction(sessionId, ffi.dialogManager), | 
					
						
							| 
									
										
										
										
											2023-07-04 20:26:43 +08:00
										 |  |  |       ), | 
					
						
							| 
									
										
										
										
											2023-11-23 19:17:19 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   // paste
 | 
					
						
							|  |  |  |   if (isMobile && perms['keyboard'] != false && perms['clipboard'] != false) { | 
					
						
							|  |  |  |     v.add(TTextMenu( | 
					
						
							|  |  |  |         child: Text(translate('Paste')), | 
					
						
							|  |  |  |         onPressed: () async { | 
					
						
							|  |  |  |           ClipboardData? data = await Clipboard.getData(Clipboard.kTextPlain); | 
					
						
							|  |  |  |           if (data != null && data.text != null) { | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |             bind.sessionInputString( | 
					
						
							|  |  |  |                 sessionId: sessionId, value: data.text ?? ""); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |           } | 
					
						
							|  |  |  |         })); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // reset canvas
 | 
					
						
							|  |  |  |   if (isMobile) { | 
					
						
							|  |  |  |     v.add(TTextMenu( | 
					
						
							|  |  |  |         child: Text(translate('Reset canvas')), | 
					
						
							|  |  |  |         onPressed: () => ffi.cursorModel.reset())); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // transferFile
 | 
					
						
							|  |  |  |   if (isDesktop) { | 
					
						
							|  |  |  |     v.add( | 
					
						
							|  |  |  |       TTextMenu( | 
					
						
							| 
									
										
										
										
											2023-11-06 20:12:01 +08:00
										 |  |  |           child: Text(translate('Transfer file')), | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |           onPressed: () => connect(context, id, isFileTransfer: true)), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // tcpTunneling
 | 
					
						
							|  |  |  |   if (isDesktop) { | 
					
						
							|  |  |  |     v.add( | 
					
						
							|  |  |  |       TTextMenu( | 
					
						
							| 
									
										
										
										
											2023-11-06 20:12:01 +08:00
										 |  |  |           child: Text(translate('TCP tunneling')), | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |           onPressed: () => connect(context, id, isTcpTunneling: true)), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // note
 | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |   if (bind | 
					
						
							|  |  |  |       .sessionGetAuditServerSync(sessionId: sessionId, typ: "conn") | 
					
						
							|  |  |  |       .isNotEmpty) { | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     v.add( | 
					
						
							|  |  |  |       TTextMenu( | 
					
						
							|  |  |  |           child: Text(translate('Note')), | 
					
						
							| 
									
										
										
										
											2023-07-01 09:33:48 +08:00
										 |  |  |           onPressed: () => showAuditDialog(ffi)), | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // divider
 | 
					
						
							| 
									
										
										
										
											2024-03-28 11:38:11 +08:00
										 |  |  |   if (isDesktop || isWebDesktop) { | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     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'), | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |           onPressed: () => bind.sessionCtrlAltDel(sessionId: sessionId)), | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // restart
 | 
					
						
							|  |  |  |   if (perms['restart'] != false && | 
					
						
							|  |  |  |       (pi.platform == kPeerPlatformLinux || | 
					
						
							|  |  |  |           pi.platform == kPeerPlatformWindows || | 
					
						
							|  |  |  |           pi.platform == kPeerPlatformMacOS)) { | 
					
						
							|  |  |  |     v.add( | 
					
						
							|  |  |  |       TTextMenu( | 
					
						
							| 
									
										
										
										
											2023-11-06 20:12:01 +08:00
										 |  |  |           child: Text(translate('Restart remote device')), | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |           onPressed: () => | 
					
						
							|  |  |  |               showRestartRemoteDevice(pi, id, sessionId, ffi.dialogManager)), | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // insertLock
 | 
					
						
							|  |  |  |   if (!ffiModel.viewOnly && ffi.ffiModel.keyboard) { | 
					
						
							|  |  |  |     v.add( | 
					
						
							|  |  |  |       TTextMenu( | 
					
						
							|  |  |  |           child: Text(translate('Insert Lock')), | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |           onPressed: () => bind.sessionLockScreen(sessionId: sessionId)), | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // blockUserInput
 | 
					
						
							|  |  |  |   if (ffi.ffiModel.keyboard && | 
					
						
							| 
									
										
										
										
											2023-11-05 21:53:21 +08:00
										 |  |  |       ffi.ffiModel.permissions['block_input'] != false && | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |       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( | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |               sessionId: sessionId, | 
					
						
							|  |  |  |               value: '${blockInput.value ? 'un' : ''}block-input'); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |           blockInput.value = !blockInput.value; | 
					
						
							|  |  |  |         })); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // switchSides
 | 
					
						
							|  |  |  |   if (isDesktop && | 
					
						
							|  |  |  |       ffiModel.keyboard && | 
					
						
							|  |  |  |       pi.platform != kPeerPlatformAndroid && | 
					
						
							|  |  |  |       pi.platform != kPeerPlatformMacOS && | 
					
						
							| 
									
										
										
										
											2023-10-08 21:44:54 +08:00
										 |  |  |       versionCmp(pi.version, '1.2.0') >= 0 && | 
					
						
							|  |  |  |       bind.peerGetDefaultSessionsCount(id: id) == 1) { | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     v.add(TTextMenu( | 
					
						
							|  |  |  |         child: Text(translate('Switch Sides')), | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |         onPressed: () => | 
					
						
							|  |  |  |             showConfirmSwitchSidesDialog(sessionId, id, ffi.dialogManager))); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   } | 
					
						
							|  |  |  |   // refresh
 | 
					
						
							|  |  |  |   if (pi.version.isNotEmpty) { | 
					
						
							|  |  |  |     v.add(TTextMenu( | 
					
						
							| 
									
										
										
										
											2023-10-08 21:44:54 +08:00
										 |  |  |       child: Text(translate('Refresh')), | 
					
						
							|  |  |  |       onPressed: () => sessionRefreshVideo(sessionId, pi), | 
					
						
							|  |  |  |     )); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   } | 
					
						
							|  |  |  |   // record
 | 
					
						
							| 
									
										
										
										
											2024-03-28 11:38:11 +08:00
										 |  |  |   if (!(isDesktop || isWeb) && | 
					
						
							| 
									
										
										
										
											2023-10-18 22:39:28 +08:00
										 |  |  |       (ffi.recordingModel.start || (perms["recording"] != false))) { | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     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())); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-04-19 14:39:22 +08:00
										 |  |  |   // fingerprint
 | 
					
						
							| 
									
										
										
										
											2024-03-28 11:38:11 +08:00
										 |  |  |   if (!(isDesktop || isWebDesktop)) { | 
					
						
							| 
									
										
										
										
											2023-04-19 14:39:22 +08:00
										 |  |  |     v.add(TTextMenu( | 
					
						
							|  |  |  |       child: Text(translate('Copy Fingerprint')), | 
					
						
							|  |  |  |       onPressed: () => onCopyFingerprint(FingerprintState.find(id).value), | 
					
						
							|  |  |  |     )); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   return v; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Future<List<TRadioMenu<String>>> toolbarViewStyle( | 
					
						
							|  |  |  |     BuildContext context, String id, FFI ffi) async { | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |   final groupValue = | 
					
						
							|  |  |  |       await bind.sessionGetViewStyle(sessionId: ffi.sessionId) ?? ''; | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   void onChanged(String? value) async { | 
					
						
							|  |  |  |     if (value == null) return; | 
					
						
							|  |  |  |     bind | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |         .sessionSetViewStyle(sessionId: ffi.sessionId, value: value) | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |         .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 { | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |   final groupValue = | 
					
						
							|  |  |  |       await bind.sessionGetImageQuality(sessionId: ffi.sessionId) ?? ''; | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   onChanged(String? value) async { | 
					
						
							|  |  |  |     if (value == null) return; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |     await bind.sessionSetImageQuality(sessionId: ffi.sessionId, value: value); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   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); | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |         customImageQualityDialog(ffi.sessionId, id, ffi); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |       }, | 
					
						
							|  |  |  |     ), | 
					
						
							|  |  |  |   ]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Future<List<TRadioMenu<String>>> toolbarCodec( | 
					
						
							|  |  |  |     BuildContext context, String id, FFI ffi) async { | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |   final sessionId = ffi.sessionId; | 
					
						
							|  |  |  |   final alternativeCodecs = | 
					
						
							|  |  |  |       await bind.sessionAlternativeCodecs(sessionId: sessionId); | 
					
						
							|  |  |  |   final groupValue = await bind.sessionGetOption( | 
					
						
							|  |  |  |           sessionId: sessionId, arg: 'codec-preference') ?? | 
					
						
							|  |  |  |       ''; | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   final List<bool> codecs = []; | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     final Map codecsJson = jsonDecode(alternativeCodecs); | 
					
						
							|  |  |  |     final vp8 = codecsJson['vp8'] ?? false; | 
					
						
							| 
									
										
										
										
											2023-05-08 20:35:24 +08:00
										 |  |  |     final av1 = codecsJson['av1'] ?? false; | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     final h264 = codecsJson['h264'] ?? false; | 
					
						
							|  |  |  |     final h265 = codecsJson['h265'] ?? false; | 
					
						
							|  |  |  |     codecs.add(vp8); | 
					
						
							| 
									
										
										
										
											2023-05-08 20:35:24 +08:00
										 |  |  |     codecs.add(av1); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     codecs.add(h264); | 
					
						
							|  |  |  |     codecs.add(h265); | 
					
						
							|  |  |  |   } catch (e) { | 
					
						
							|  |  |  |     debugPrint("Show Codec Preference err=$e"); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-05-08 20:35:24 +08:00
										 |  |  |   final visible = | 
					
						
							|  |  |  |       codecs.length == 4 && (codecs[0] || codecs[1] || codecs[2] || codecs[3]); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   if (!visible) return []; | 
					
						
							|  |  |  |   onChanged(String? value) async { | 
					
						
							|  |  |  |     if (value == null) return; | 
					
						
							|  |  |  |     await bind.sessionPeerOption( | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |         sessionId: sessionId, name: 'codec-preference', value: value); | 
					
						
							|  |  |  |     bind.sessionChangePreferCodec(sessionId: sessionId); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   TRadioMenu<String> radio(String label, String value, bool enabled) { | 
					
						
							|  |  |  |     return TRadioMenu<String>( | 
					
						
							| 
									
										
										
										
											2024-05-07 20:34:23 +08:00
										 |  |  |         child: Text(label), | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |         value: value, | 
					
						
							|  |  |  |         groupValue: groupValue, | 
					
						
							|  |  |  |         onChanged: enabled ? onChanged : null); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-07 20:34:23 +08:00
										 |  |  |   var autoLabel = translate('Auto'); | 
					
						
							|  |  |  |   if (groupValue == 'auto') { | 
					
						
							|  |  |  |     autoLabel = '$autoLabel (${ffi.qualityMonitorModel.data.codecFormat})'; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   return [ | 
					
						
							| 
									
										
										
										
											2024-05-07 20:34:23 +08:00
										 |  |  |     radio(autoLabel, 'auto', true), | 
					
						
							| 
									
										
										
										
											2023-05-08 20:35:24 +08:00
										 |  |  |     if (codecs[0]) radio('VP8', 'vp8', codecs[0]), | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     radio('VP9', 'vp9', true), | 
					
						
							| 
									
										
										
										
											2023-05-08 20:35:24 +08:00
										 |  |  |     if (codecs[1]) radio('AV1', 'av1', codecs[1]), | 
					
						
							|  |  |  |     if (codecs[2]) radio('H264', 'h264', codecs[2]), | 
					
						
							|  |  |  |     if (codecs[3]) radio('H265', 'h265', codecs[3]), | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   ]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-25 10:56:02 +05:30
										 |  |  | Future<List<TToggleMenu>> toolbarCursor( | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     BuildContext context, String id, FFI ffi) async { | 
					
						
							|  |  |  |   List<TToggleMenu> v = []; | 
					
						
							|  |  |  |   final ffiModel = ffi.ffiModel; | 
					
						
							|  |  |  |   final pi = ffiModel.pi; | 
					
						
							|  |  |  |   final perms = ffiModel.permissions; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |   final sessionId = ffi.sessionId; | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // show remote cursor
 | 
					
						
							|  |  |  |   if (pi.platform != kPeerPlatformAndroid && | 
					
						
							|  |  |  |       !ffi.canvasModel.cursorEmbedded && | 
					
						
							| 
									
										
										
										
											2023-10-08 21:44:54 +08:00
										 |  |  |       !pi.isWayland) { | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     final state = ShowRemoteCursorState.find(id); | 
					
						
							| 
									
										
										
										
											2024-04-25 10:56:02 +05:30
										 |  |  |     final lockState = ShowRemoteCursorLockState.find(id); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     final enabled = !ffiModel.viewOnly; | 
					
						
							|  |  |  |     final option = 'show-remote-cursor'; | 
					
						
							| 
									
										
										
										
											2024-04-25 10:56:02 +05:30
										 |  |  |     if (pi.currentDisplay == kAllDisplayValue || | 
					
						
							|  |  |  |         bind.sessionIsMultiUiSession(sessionId: sessionId)) { | 
					
						
							|  |  |  |       lockState.value = false; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     v.add(TToggleMenu( | 
					
						
							|  |  |  |         child: Text(translate('Show remote cursor')), | 
					
						
							|  |  |  |         value: state.value, | 
					
						
							| 
									
										
										
										
											2024-04-25 10:56:02 +05:30
										 |  |  |         onChanged: enabled && !lockState.value | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |             ? (value) async { | 
					
						
							|  |  |  |                 if (value == null) return; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |                 await bind.sessionToggleOption( | 
					
						
							|  |  |  |                     sessionId: sessionId, value: option); | 
					
						
							|  |  |  |                 state.value = bind.sessionGetToggleOptionSync( | 
					
						
							|  |  |  |                     sessionId: sessionId, arg: option); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |               } | 
					
						
							|  |  |  |             : null)); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2024-04-25 10:56:02 +05:30
										 |  |  |   // 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); | 
					
						
							|  |  |  |         })); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   // zoom cursor
 | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |   final viewStyle = await bind.sessionGetViewStyle(sessionId: sessionId) ?? ''; | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   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; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |         await bind.sessionToggleOption(sessionId: sessionId, value: option); | 
					
						
							|  |  |  |         peerState.value = | 
					
						
							|  |  |  |             bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |       }, | 
					
						
							|  |  |  |     )); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2024-04-25 10:56:02 +05:30
										 |  |  |   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; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   // show quality monitor
 | 
					
						
							|  |  |  |   final option = 'show-quality-monitor'; | 
					
						
							|  |  |  |   v.add(TToggleMenu( | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |       value: bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option), | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |       onChanged: (value) async { | 
					
						
							|  |  |  |         if (value == null) return; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |         await bind.sessionToggleOption(sessionId: sessionId, value: option); | 
					
						
							|  |  |  |         ffi.qualityMonitorModel.checkShowQualityMonitor(sessionId); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |       }, | 
					
						
							|  |  |  |       child: Text(translate('Show quality monitor')))); | 
					
						
							|  |  |  |   // mute
 | 
					
						
							|  |  |  |   if (perms['audio'] != false) { | 
					
						
							|  |  |  |     final option = 'disable-audio'; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |     final value = | 
					
						
							|  |  |  |         bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     v.add(TToggleMenu( | 
					
						
							|  |  |  |         value: value, | 
					
						
							|  |  |  |         onChanged: (value) { | 
					
						
							|  |  |  |           if (value == null) return; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |           bind.sessionToggleOption(sessionId: sessionId, value: option); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |         }, | 
					
						
							|  |  |  |         child: Text(translate('Mute')))); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // file copy and paste
 | 
					
						
							| 
									
										
										
										
											2024-04-14 21:25:26 +08:00
										 |  |  |   // 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); | 
					
						
							| 
									
										
										
										
											2023-12-25 21:49:34 +08:00
										 |  |  |   if (ffiModel.keyboard && | 
					
						
							|  |  |  |       perms['file'] != false && | 
					
						
							| 
									
										
										
										
											2024-04-14 21:25:26 +08:00
										 |  |  |       (isSupportIfPeer_1_2_3 || isSupportIfPeer_1_2_4)) { | 
					
						
							| 
									
										
										
										
											2023-12-25 21:49:34 +08:00
										 |  |  |     final enabled = !ffiModel.viewOnly; | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     final option = 'enable-file-transfer'; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |     final value = | 
					
						
							|  |  |  |         bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     v.add(TToggleMenu( | 
					
						
							|  |  |  |         value: value, | 
					
						
							| 
									
										
										
										
											2023-12-25 21:49:34 +08:00
										 |  |  |         onChanged: enabled | 
					
						
							|  |  |  |             ? (value) { | 
					
						
							|  |  |  |                 if (value == null) return; | 
					
						
							|  |  |  |                 bind.sessionToggleOption(sessionId: sessionId, value: option); | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             : null, | 
					
						
							| 
									
										
										
										
											2023-11-08 18:31:37 +01:00
										 |  |  |         child: Text(translate('Enable file copy and paste')))); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   } | 
					
						
							|  |  |  |   // disable clipboard
 | 
					
						
							|  |  |  |   if (ffiModel.keyboard && perms['clipboard'] != false) { | 
					
						
							|  |  |  |     final enabled = !ffiModel.viewOnly; | 
					
						
							|  |  |  |     final option = 'disable-clipboard'; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |     var value = | 
					
						
							|  |  |  |         bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     if (ffiModel.viewOnly) value = true; | 
					
						
							|  |  |  |     v.add(TToggleMenu( | 
					
						
							|  |  |  |         value: value, | 
					
						
							|  |  |  |         onChanged: enabled | 
					
						
							|  |  |  |             ? (value) { | 
					
						
							|  |  |  |                 if (value == null) return; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |                 bind.sessionToggleOption(sessionId: sessionId, value: option); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |               } | 
					
						
							|  |  |  |             : null, | 
					
						
							|  |  |  |         child: Text(translate('Disable clipboard')))); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // lock after session end
 | 
					
						
							|  |  |  |   if (ffiModel.keyboard) { | 
					
						
							| 
									
										
										
										
											2023-12-25 21:49:34 +08:00
										 |  |  |     final enabled = !ffiModel.viewOnly; | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     final option = 'lock-after-session-end'; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |     final value = | 
					
						
							|  |  |  |         bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     v.add(TToggleMenu( | 
					
						
							|  |  |  |         value: value, | 
					
						
							| 
									
										
										
										
											2023-12-25 21:49:34 +08:00
										 |  |  |         onChanged: enabled | 
					
						
							|  |  |  |             ? (value) { | 
					
						
							|  |  |  |                 if (value == null) return; | 
					
						
							|  |  |  |                 bind.sessionToggleOption(sessionId: sessionId, value: option); | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             : null, | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |         child: Text(translate('Lock after session end')))); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-10-09 17:22:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-16 07:26:55 +08:00
										 |  |  |   if (useTextureRender && | 
					
						
							|  |  |  |       pi.isSupportMultiDisplay && | 
					
						
							| 
									
										
										
										
											2023-11-14 12:11:38 +08:00
										 |  |  |       PrivacyModeState.find(id).isEmpty && | 
					
						
							| 
									
										
										
										
											2023-10-09 17:22:22 +08:00
										 |  |  |       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' : ''); | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         child: Text(translate('Show displays as individual windows')))); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-28 11:38:11 +08:00
										 |  |  |   final isMultiScreens = !isWeb && (await getScreenRectList()).length > 1; | 
					
						
							|  |  |  |   if (useTextureRender && pi.isSupportMultiDisplay && isMultiScreens) { | 
					
						
							| 
									
										
										
										
											2023-10-17 13:57:06 +08:00
										 |  |  |     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' : ''); | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         child: Text(translate('Use all my displays for the remote session')))); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-27 15:44:07 +08:00
										 |  |  |   // 444
 | 
					
						
							|  |  |  |   final codec_format = ffi.qualityMonitorModel.data.codecFormat; | 
					
						
							| 
									
										
										
										
											2023-12-11 23:46:32 +09:00
										 |  |  |   if (versionCmp(pi.version, "1.2.4") >= 0 && | 
					
						
							|  |  |  |       (codec_format == "AV1" || codec_format == "VP9")) { | 
					
						
							| 
									
										
										
										
											2023-10-27 15:44:07 +08:00
										 |  |  |     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); | 
					
						
							|  |  |  |         }, | 
					
						
							| 
									
										
										
										
											2023-11-01 11:05:58 +08:00
										 |  |  |         child: Text(translate('True color (4:4:4)')))); | 
					
						
							| 
									
										
										
										
											2023-10-27 15:44:07 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-11 12:56:26 +08:00
										 |  |  |   if (isMobile) { | 
					
						
							|  |  |  |     v.addAll(toolbarKeyboardToggles(ffi)); | 
					
						
							| 
									
										
										
										
											2023-12-11 11:22:27 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   return v; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-11-14 12:11:38 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 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) { | 
					
						
							| 
									
										
										
										
											2023-12-25 21:49:34 +08:00
										 |  |  |     final enabled = !ffi.ffiModel.viewOnly; | 
					
						
							| 
									
										
										
										
											2023-11-14 12:11:38 +08:00
										 |  |  |     return TToggleMenu( | 
					
						
							|  |  |  |         value: privacyModeState.isNotEmpty, | 
					
						
							| 
									
										
										
										
											2023-12-25 21:49:34 +08:00
										 |  |  |         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, | 
					
						
							| 
									
										
										
										
											2023-11-14 12:11:38 +08:00
										 |  |  |         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 { | 
					
						
							| 
									
										
										
										
											2023-11-14 20:46:06 +08:00
										 |  |  |         bind.sessionTogglePrivacyMode( | 
					
						
							|  |  |  |             sessionId: sid, implKey: implKey, on: privacyModeState.isEmpty); | 
					
						
							| 
									
										
										
										
											2023-11-14 12:11:38 +08:00
										 |  |  |         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( | 
					
						
							| 
									
										
										
										
											2023-11-14 17:35:16 +08:00
										 |  |  |           child: Text(translate(implName)), | 
					
						
							| 
									
										
										
										
											2023-11-14 12:11:38 +08:00
										 |  |  |           value: privacyModeState.value == implKey, | 
					
						
							| 
									
										
										
										
											2023-11-14 17:35:16 +08:00
										 |  |  |           onChanged: (value) { | 
					
						
							|  |  |  |             if (value == null) return; | 
					
						
							|  |  |  |             togglePrivacyModeTime = DateTime.now(); | 
					
						
							|  |  |  |             bind.sessionTogglePrivacyMode( | 
					
						
							| 
									
										
										
										
											2023-11-14 20:46:06 +08:00
										 |  |  |                 sessionId: sessionId, implKey: implKey, on: value); | 
					
						
							| 
									
										
										
										
											2023-11-14 17:35:16 +08:00
										 |  |  |           }); | 
					
						
							| 
									
										
										
										
											2023-11-14 12:11:38 +08:00
										 |  |  |     }).toList(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-12-11 12:56:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 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 && | 
					
						
							| 
									
										
										
										
											2024-03-24 11:23:06 +08:00
										 |  |  |       ((isMacOS && pi.platform != kPeerPlatformMacOS) || | 
					
						
							|  |  |  |           (!isMacOS && pi.platform == kPeerPlatformMacOS))) { | 
					
						
							| 
									
										
										
										
											2023-12-11 12:56:26 +08:00
										 |  |  |     final option = 'allow_swap_key'; | 
					
						
							|  |  |  |     final value = | 
					
						
							|  |  |  |         bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option); | 
					
						
							| 
									
										
										
										
											2023-12-11 15:32:13 +08:00
										 |  |  |     onChanged(bool? value) { | 
					
						
							|  |  |  |       if (value == null) return; | 
					
						
							|  |  |  |       bind.sessionToggleOption(sessionId: sessionId, value: option); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     final enabled = !ffi.ffiModel.viewOnly; | 
					
						
							| 
									
										
										
										
											2023-12-11 12:56:26 +08:00
										 |  |  |     v.add(TToggleMenu( | 
					
						
							|  |  |  |         value: value, | 
					
						
							| 
									
										
										
										
											2023-12-11 15:32:13 +08:00
										 |  |  |         onChanged: enabled ? onChanged : null, | 
					
						
							| 
									
										
										
										
											2023-12-11 12:56:26 +08:00
										 |  |  |         child: Text(translate('Swap control-command key')))); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-11 15:32:13 +08:00
										 |  |  |   // reverse mouse wheel
 | 
					
						
							|  |  |  |   if (ffiModel.keyboard) { | 
					
						
							|  |  |  |     var optionValue = | 
					
						
							|  |  |  |         bind.sessionGetReverseMouseWheelSync(sessionId: sessionId) ?? ''; | 
					
						
							|  |  |  |     if (optionValue == '') { | 
					
						
							| 
									
										
										
										
											2024-02-23 22:49:53 +08:00
										 |  |  |       optionValue = bind.mainGetUserDefaultOption(key: kKeyReverseMouseWheel); | 
					
						
							| 
									
										
										
										
											2023-12-11 15:32:13 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |     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')))); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-11 12:56:26 +08:00
										 |  |  |   // swap left right mouse
 | 
					
						
							| 
									
										
										
										
											2023-12-11 15:32:13 +08:00
										 |  |  |   if (ffiModel.keyboard) { | 
					
						
							| 
									
										
										
										
											2023-12-11 12:56:26 +08:00
										 |  |  |     final option = 'swap-left-right-mouse'; | 
					
						
							|  |  |  |     final value = | 
					
						
							|  |  |  |         bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option); | 
					
						
							| 
									
										
										
										
											2023-12-11 15:32:13 +08:00
										 |  |  |     onChanged(bool? value) { | 
					
						
							|  |  |  |       if (value == null) return; | 
					
						
							|  |  |  |       bind.sessionToggleOption(sessionId: sessionId, value: option); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     final enabled = !ffi.ffiModel.viewOnly; | 
					
						
							| 
									
										
										
										
											2023-12-11 12:56:26 +08:00
										 |  |  |     v.add(TToggleMenu( | 
					
						
							|  |  |  |         value: value, | 
					
						
							| 
									
										
										
										
											2023-12-11 15:32:13 +08:00
										 |  |  |         onChanged: enabled ? onChanged : null, | 
					
						
							| 
									
										
										
										
											2023-12-11 12:56:26 +08:00
										 |  |  |         child: Text(translate('swap-left-right-mouse')))); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return v; | 
					
						
							|  |  |  | } |