2022-05-29 17:19:50 +08:00
import ' dart:convert ' ;
2022-09-19 10:14:14 +08:00
import ' dart:io ' ;
2022-11-03 21:58:25 +08:00
import ' dart:ui ' as ui ;
2022-05-29 17:19:50 +08:00
2022-08-09 16:37:11 +08:00
import ' package:desktop_multi_window/desktop_multi_window.dart ' ;
2022-05-29 17:19:50 +08:00
import ' package:flutter/material.dart ' ;
2023-04-19 14:39:22 +08:00
import ' package:flutter/services.dart ' ;
2022-06-28 22:04:10 +08:00
import ' package:flutter_hbb/common.dart ' ;
2022-08-29 18:48:12 +08:00
import ' package:flutter_hbb/common/shared_state.dart ' ;
2022-08-03 15:31:19 +08:00
import ' package:flutter_hbb/consts.dart ' ;
2022-11-01 18:16:52 +08:00
import ' package:flutter_hbb/models/state_model.dart ' ;
2022-05-29 17:19:50 +08:00
import ' package:flutter_hbb/desktop/pages/remote_page.dart ' ;
2023-03-15 18:31:53 +01:00
import ' package:flutter_hbb/desktop/widgets/remote_toolbar.dart ' ;
2022-08-06 17:08:48 +08:00
import ' package:flutter_hbb/desktop/widgets/tabbar_widget.dart ' ;
2022-11-03 21:58:25 +08:00
import ' package:flutter_hbb/desktop/widgets/material_mod_popup_menu.dart '
as mod_menu ;
import ' package:flutter_hbb/desktop/widgets/popup_menu.dart ' ;
2022-05-29 17:19:50 +08:00
import ' package:flutter_hbb/utils/multi_window_manager.dart ' ;
2022-09-27 19:42:05 +08:00
import ' package:flutter_svg/flutter_svg.dart ' ;
2022-06-28 22:04:10 +08:00
import ' package:get/get.dart ' ;
2022-11-03 21:58:25 +08:00
import ' package:bot_toast/bot_toast.dart ' ;
2022-06-28 22:04:10 +08:00
2022-10-13 21:19:05 +09:00
import ' ../../models/platform_model.dart ' ;
2022-09-08 19:26:55 +08:00
2022-11-03 21:58:25 +08:00
class _MenuTheme {
2023-02-15 11:40:17 +01:00
static const Color blueColor = MyTheme . button ;
2022-11-03 21:58:25 +08:00
// kMinInteractiveDimension
static const double height = 20.0 ;
static const double dividerHeight = 12.0 ;
}
2022-05-29 17:19:50 +08:00
class ConnectionTabPage extends StatefulWidget {
final Map < String , dynamic > params ;
const ConnectionTabPage ( { Key ? key , required this . params } ) : super ( key: key ) ;
@ override
State < ConnectionTabPage > createState ( ) = > _ConnectionTabPageState ( params ) ;
}
2022-08-18 10:54:09 +08:00
class _ConnectionTabPageState extends State < ConnectionTabPage > {
2023-01-30 21:42:58 +08:00
final tabController =
Get . put ( DesktopTabController ( tabType: DesktopTabType . remoteScreen ) ) ;
final contentKey = UniqueKey ( ) ;
2022-08-29 18:48:12 +08:00
static const IconData selectedIcon = Icons . desktop_windows_sharp ;
static const IconData unselectedIcon = Icons . desktop_windows_outlined ;
2022-08-05 10:27:06 +08:00
2023-06-11 16:32:22 +08:00
late ToolbarState _toolbarState ;
2022-11-10 14:32:22 +08:00
2022-08-05 10:27:06 +08:00
var connectionMap = RxList < Widget > . empty ( growable: true ) ;
2022-05-29 17:19:50 +08:00
_ConnectionTabPageState ( Map < String , dynamic > params ) {
2023-06-11 16:32:22 +08:00
_toolbarState = ToolbarState ( ) ;
2022-10-08 17:27:30 +08:00
RemoteCountState . init ( ) ;
2022-08-29 22:46:19 +08:00
final peerId = params [ ' id ' ] ;
if ( peerId ! = null ) {
ConnectionTypeState . init ( peerId ) ;
2023-06-07 20:31:54 +08:00
tabController . onSelected = ( id ) {
2023-07-06 09:40:03 +08:00
final remotePage = tabController . widget ( id ) ;
2023-06-07 20:31:54 +08:00
if ( remotePage is RemotePage ) {
final ffi = remotePage . ffi ;
bind . setCurSessionId ( sessionId: ffi . sessionId ) ;
}
2023-01-23 22:07:50 +08:00
WindowController . fromWindowId ( windowId ( ) )
. setTitle ( getWindowNameWithId ( id ) ) ;
2023-07-06 09:40:03 +08:00
UnreadChatCountState . find ( id ) . value = 0 ;
2023-01-23 22:07:50 +08:00
} ;
2022-08-26 12:14:14 +08:00
tabController . add ( TabInfo (
2022-11-01 17:01:43 +08:00
key: peerId ,
label: peerId ,
selectedIcon: selectedIcon ,
unselectedIcon: unselectedIcon ,
onTabCloseButton: ( ) = > tabController . closeBy ( peerId ) ,
page: RemotePage (
key: ValueKey ( peerId ) ,
id: peerId ,
2023-03-20 00:16:06 +08:00
password: params [ ' password ' ] ,
2023-06-11 16:32:22 +08:00
toolbarState: _toolbarState ,
2023-06-07 20:31:54 +08:00
tabController: tabController ,
2023-01-17 13:28:33 +08:00
switchUuid: params [ ' switch_uuid ' ] ,
2023-02-13 16:40:24 +08:00
forceRelay: params [ ' forceRelay ' ] ,
2022-11-01 17:01:43 +08:00
) ,
) ) ;
2022-10-08 17:27:30 +08:00
_update_remote_count ( ) ;
2022-05-31 16:27:54 +08:00
}
2022-05-29 17:19:50 +08:00
}
@ override
void initState ( ) {
super . initState ( ) ;
2022-08-24 20:56:42 +08:00
2022-10-27 10:56:14 +08:00
tabController . onRemoved = ( _ , id ) = > onRemoveId ( id ) ;
2022-08-24 20:56:42 +08:00
2022-05-29 17:19:50 +08:00
rustDeskWinManager . setMethodHandler ( ( call , fromWindowId ) async {
print (
2022-11-05 23:41:22 +08:00
" [Remote Page] call ${ call . method } with args ${ call . arguments } from window $ fromWindowId " ) ;
2022-08-26 23:28:08 +08:00
2022-05-29 17:19:50 +08:00
// for simplify, just replace connectionId
if ( call . method = = " new_remote_desktop " ) {
2022-08-05 10:27:06 +08:00
final args = jsonDecode ( call . arguments ) ;
final id = args [ ' id ' ] ;
2023-01-17 13:28:33 +08:00
final switchUuid = args [ ' switch_uuid ' ] ;
2022-08-09 19:32:19 +08:00
window_on_top ( windowId ( ) ) ;
2022-08-31 18:41:55 +08:00
ConnectionTypeState . init ( id ) ;
2023-06-11 16:32:22 +08:00
_toolbarState . setShow (
2023-06-11 15:28:41 +08:00
bind . mainGetUserDefaultOption ( key: ' collapse_toolbar ' ) ! = ' Y ' ) ;
2022-08-24 20:56:42 +08:00
tabController . add ( TabInfo (
2022-11-01 17:01:43 +08:00
key: id ,
label: id ,
selectedIcon: selectedIcon ,
unselectedIcon: unselectedIcon ,
onTabCloseButton: ( ) = > tabController . closeBy ( id ) ,
2022-11-10 14:32:22 +08:00
page: RemotePage (
key: ValueKey ( id ) ,
id: id ,
2023-03-20 00:16:06 +08:00
password: args [ ' password ' ] ,
2023-06-11 16:32:22 +08:00
toolbarState: _toolbarState ,
2023-06-07 20:31:54 +08:00
tabController: tabController ,
2023-01-17 13:28:33 +08:00
switchUuid: switchUuid ,
2023-02-13 16:40:24 +08:00
forceRelay: args [ ' forceRelay ' ] ,
2022-11-10 14:32:22 +08:00
) ,
2022-11-01 17:01:43 +08:00
) ) ;
2023-02-21 18:43:43 +08:00
} else if ( call . method = = kWindowDisableGrabKeyboard ) {
stateGlobal . grabKeyboard = false ;
2022-06-28 22:04:10 +08:00
} else if ( call . method = = " onDestroy " ) {
2022-08-30 20:48:03 +08:00
tabController . clear ( ) ;
2022-10-26 14:39:13 +08:00
} else if ( call . method = = kWindowActionRebuild ) {
reloadCurrentWindow ( ) ;
2022-05-29 17:19:50 +08:00
}
2022-10-08 17:27:30 +08:00
_update_remote_count ( ) ;
2022-05-29 17:19:50 +08:00
} ) ;
2023-02-01 14:13:53 +08:00
Future . delayed ( Duration . zero , ( ) {
restoreWindowPosition ( WindowType . RemoteDesktop , windowId: windowId ( ) ) ;
} ) ;
2022-05-29 17:19:50 +08:00
}
2022-11-10 14:32:22 +08:00
@ override
void dispose ( ) {
super . dispose ( ) ;
2023-06-11 16:32:22 +08:00
_toolbarState . save ( ) ;
2022-11-10 14:32:22 +08:00
}
2022-05-29 17:19:50 +08:00
@ override
Widget build ( BuildContext context ) {
2022-11-16 19:49:52 +08:00
final tabWidget = Obx (
( ) = > Container (
decoration: BoxDecoration (
2022-10-04 21:19:31 +08:00
border: Border . all (
color: MyTheme . color ( context ) . border ! ,
2022-11-16 19:49:52 +08:00
width: stateGlobal . windowBorderWidth . value ) ,
) ,
child: Scaffold (
2023-02-23 16:49:31 +01:00
backgroundColor: Theme . of ( context ) . colorScheme . background ,
2022-11-01 17:01:43 +08:00
body: DesktopTab (
controller: tabController ,
onWindowCloseButton: handleWindowCloseButton ,
tail: const AddButton ( ) . paddingOnly ( left: 10 ) ,
pageViewBuilder: ( pageView ) = > pageView ,
2022-11-12 22:33:10 +08:00
labelGetter: DesktopTab . labelGetterAlias ,
2022-11-01 17:01:43 +08:00
tabBuilder: ( key , icon , label , themeConf ) = > Obx ( ( ) {
final connectionType = ConnectionTypeState . find ( key ) ;
if ( ! connectionType . isValid ( ) ) {
return Row (
mainAxisAlignment: MainAxisAlignment . center ,
children: [
icon ,
label ,
] ,
) ;
} else {
2023-04-19 14:39:22 +08:00
bool secure =
connectionType . secure . value = = ConnectionType . strSecure ;
bool direct =
connectionType . direct . value = = ConnectionType . strDirect ;
var msgConn ;
if ( secure & & direct ) {
msgConn = translate ( " Direct and encrypted connection " ) ;
} else if ( secure & & ! direct ) {
msgConn = translate ( " Relayed and encrypted connection " ) ;
} else if ( ! secure & & direct ) {
msgConn = translate ( " Direct and unencrypted connection " ) ;
} else {
msgConn = translate ( " Relayed and unencrypted connection " ) ;
}
var msgFingerprint = ' ${ translate ( ' Fingerprint ' ) } : \n ' ;
var fingerprint = FingerprintState . find ( key ) . value ;
if ( fingerprint . length > 5 * 8 ) {
var first = fingerprint . substring ( 0 , 39 ) ;
var second = fingerprint . substring ( 40 ) ;
msgFingerprint + = ' $ first \n $ second ' ;
} else {
msgFingerprint + = fingerprint ;
}
2022-11-03 21:58:25 +08:00
final tab = Row (
2022-11-01 17:01:43 +08:00
mainAxisAlignment: MainAxisAlignment . center ,
children: [
icon ,
Tooltip (
2023-04-19 14:39:22 +08:00
message: ' $ msgConn \n $ msgFingerprint ' ,
2022-11-01 17:01:43 +08:00
child: SvgPicture . asset (
' assets/ ${ connectionType . secure . value } ${ connectionType . direct . value } .svg ' ,
width: themeConf . iconSize ,
height: themeConf . iconSize ,
) . paddingOnly ( right: 5 ) ,
) ,
label ,
2023-07-06 09:40:03 +08:00
unreadMessageCountBuilder ( UnreadChatCountState . find ( key ) ) ,
2022-11-01 17:01:43 +08:00
] ,
) ;
2022-11-03 21:58:25 +08:00
return Listener (
onPointerDown: ( e ) {
if ( e . kind ! = ui . PointerDeviceKind . mouse ) {
return ;
}
if ( e . buttons = = 2 ) {
showRightMenu (
( CancelFunc cancelFunc ) {
return _tabMenuBuilder ( key , cancelFunc ) ;
} ,
target: e . position ,
) ;
}
} ,
child: tab ,
) ;
2022-11-01 17:01:43 +08:00
}
} ) ,
2022-11-16 19:49:52 +08:00
) ,
) ,
) ,
2022-09-19 10:14:14 +08:00
) ;
2023-02-27 15:56:09 +08:00
return Platform . isMacOS | | kUseCompatibleUiMode
2022-09-19 10:14:14 +08:00
? tabWidget
2023-01-30 21:42:58 +08:00
: Obx ( ( ) = > SubWindowDragToResizeArea (
key: contentKey ,
child: tabWidget ,
2023-02-27 12:01:22 +08:00
// Specially configured for a better resize area and remote control.
childPadding: kDragToResizeAreaPadding ,
2023-01-30 21:42:58 +08:00
resizeEdgeSize: stateGlobal . resizeEdgeSize . value ,
windowId: stateGlobal . windowId ,
) ) ;
2022-05-29 17:19:50 +08:00
}
2022-05-31 16:27:54 +08:00
2023-06-11 16:32:22 +08:00
// Note: Some dup code to ../widgets/remote_toolbar
2022-11-03 21:58:25 +08:00
Widget _tabMenuBuilder ( String key , CancelFunc cancelFunc ) {
final List < MenuEntryBase < String > > menu = [ ] ;
const EdgeInsets padding = EdgeInsets . only ( left: 8.0 , right: 5.0 ) ;
final remotePage = tabController . state . value . tabs
. firstWhere ( ( tab ) = > tab . key = = key )
. page as RemotePage ;
final ffi = remotePage . ffi ;
final pi = ffi . ffiModel . pi ;
final perms = ffi . ffiModel . permissions ;
2023-06-06 07:39:44 +08:00
final sessionId = ffi . sessionId ;
2022-11-03 21:58:25 +08:00
menu . addAll ( [
MenuEntryButton < String > (
childBuilder: ( TextStyle ? style ) = > Text (
translate ( ' Close ' ) ,
style: style ,
) ,
proc: ( ) {
tabController . closeBy ( key ) ;
cancelFunc ( ) ;
} ,
padding: padding ,
) ,
MenuEntryButton < String > (
childBuilder: ( TextStyle ? style ) = > Obx ( ( ) = > Text (
2022-11-10 14:32:22 +08:00
translate (
2023-06-11 16:32:22 +08:00
_toolbarState . show . isTrue ? ' Hide Toolbar ' : ' Show Toolbar ' ) ,
2022-11-03 21:58:25 +08:00
style: style ,
) ) ,
proc: ( ) {
2023-06-11 16:32:22 +08:00
_toolbarState . switchShow ( ) ;
2022-11-03 21:58:25 +08:00
cancelFunc ( ) ;
} ,
padding: padding ,
) ,
MenuEntryDivider < String > ( ) ,
2023-02-06 11:27:20 +08:00
RemoteMenuEntry . viewStyle (
key ,
ffi ,
padding ,
dismissFunc: cancelFunc ,
2022-11-03 21:58:25 +08:00
) ,
2022-11-29 16:36:35 +08:00
] ) ;
2023-03-21 11:27:30 +08:00
if ( ! ffi . canvasModel . cursorEmbedded & &
! ffi . ffiModel . viewOnly & &
! pi . is_wayland ) {
2022-11-29 16:36:35 +08:00
menu . add ( MenuEntryDivider < String > ( ) ) ;
2023-02-06 11:27:20 +08:00
menu . add ( RemoteMenuEntry . showRemoteCursor (
key ,
2023-06-06 07:39:44 +08:00
sessionId ,
2023-02-06 11:27:20 +08:00
padding ,
dismissFunc: cancelFunc ,
) ) ;
2022-11-29 16:36:35 +08:00
}
2022-11-03 21:58:25 +08:00
2023-03-16 15:27:44 +08:00
if ( perms [ ' keyboard ' ] ! = false & & ! ffi . ffiModel . viewOnly ) {
2022-11-04 11:09:48 +08:00
if ( perms [ ' clipboard ' ] ! = false ) {
2023-06-06 07:39:44 +08:00
menu . add ( RemoteMenuEntry . disableClipboard ( sessionId , padding ,
2023-02-06 11:27:20 +08:00
dismissFunc: cancelFunc ) ) ;
2022-11-04 11:09:48 +08:00
}
2023-06-06 07:39:44 +08:00
menu . add ( RemoteMenuEntry . insertLock ( sessionId , padding ,
dismissFunc: cancelFunc ) ) ;
2022-11-04 11:09:48 +08:00
2023-01-10 17:13:40 +08:00
if ( pi . platform = = kPeerPlatformLinux | | pi . sasEnabled ) {
2023-06-06 07:39:44 +08:00
menu . add ( RemoteMenuEntry . insertCtrlAltDel ( sessionId , padding ,
2023-02-06 11:27:20 +08:00
dismissFunc: cancelFunc ) ) ;
2022-11-03 21:58:25 +08:00
}
}
2023-04-19 14:39:22 +08:00
menu . add ( MenuEntryButton < String > (
childBuilder: ( TextStyle ? style ) = > Text (
translate ( ' Copy Fingerprint ' ) ,
style: style ,
) ,
proc: ( ) = > onCopyFingerprint ( FingerprintState . find ( key ) . value ) ,
padding: padding ,
dismissOnClicked: true ,
dismissCallback: cancelFunc ,
) ) ;
2022-11-03 21:58:25 +08:00
return mod_menu . PopupMenu < String > (
items: menu
. map ( ( entry ) = > entry . build (
context ,
const MenuConfig (
2023-02-15 11:40:17 +01:00
commonColor: _MenuTheme . blueColor ,
2022-11-03 21:58:25 +08:00
height: _MenuTheme . height ,
dividerHeight: _MenuTheme . dividerHeight ,
) ) )
. expand ( ( i ) = > i )
. toList ( ) ,
) ;
}
2022-11-09 15:14:11 +08:00
void onRemoveId ( String id ) async {
2022-08-30 16:45:47 +08:00
if ( tabController . state . value . tabs . isEmpty ) {
2022-11-09 15:14:11 +08:00
await WindowController . fromWindowId ( windowId ( ) ) . close ( ) ;
2022-08-09 09:01:06 +08:00
}
2022-08-29 22:46:19 +08:00
ConnectionTypeState . delete ( id ) ;
2022-10-08 17:27:30 +08:00
_update_remote_count ( ) ;
2022-05-31 16:27:54 +08:00
}
2022-08-09 16:37:11 +08:00
int windowId ( ) {
return widget . params [ " windowId " ] ;
}
2022-09-08 21:03:20 +08:00
2022-09-09 19:29:19 +08:00
Future < bool > handleWindowCloseButton ( ) async {
2022-10-08 17:27:30 +08:00
final connLength = tabController . length ;
2022-10-13 21:19:05 +09:00
if ( connLength < = 1 ) {
tabController . clear ( ) ;
2022-09-09 19:29:19 +08:00
return true ;
} else {
2022-10-13 21:19:05 +09:00
final opt = " enable-confirm-closing-tabs " ;
final bool res ;
if ( ! option2bool ( opt , await bind . mainGetOption ( key: opt ) ) ) {
res = true ;
} else {
res = await closeConfirmDialog ( ) ;
}
2022-09-09 19:29:19 +08:00
if ( res ) {
tabController . clear ( ) ;
}
return res ;
}
}
2022-10-08 17:27:30 +08:00
_update_remote_count ( ) = >
RemoteCountState . find ( ) . value = tabController . length ;
2022-05-29 17:19:50 +08:00
}