2022-05-29 17:19:50 +08:00
import ' dart:async ' ;
import ' dart:convert ' ;
2022-10-10 10:53:10 +08:00
import ' dart:io ' ;
2022-05-29 17:19:50 +08:00
import ' dart:math ' ;
2023-10-08 21:44:54 +08:00
import ' dart:typed_data ' ;
2022-05-29 17:19:50 +08:00
import ' dart:ui ' as ui ;
2023-08-14 20:40:58 +08:00
import ' package:desktop_multi_window/desktop_multi_window.dart ' ;
2022-05-29 17:19:50 +08:00
import ' package:flutter/material.dart ' ;
2020-11-28 13:22:19 +08:00
import ' package:flutter/services.dart ' ;
2022-08-31 18:41:55 +08:00
import ' package:flutter_hbb/consts.dart ' ;
2022-05-31 14:44:06 +08:00
import ' package:flutter_hbb/generated_bridge.dart ' ;
2022-07-25 16:23:45 +08:00
import ' package:flutter_hbb/models/ab_model.dart ' ;
2022-03-03 14:58:57 +08:00
import ' package:flutter_hbb/models/chat_model.dart ' ;
2023-09-06 16:56:39 +08:00
import ' package:flutter_hbb/models/cm_file_model.dart ' ;
2022-03-07 22:54:34 +08:00
import ' package:flutter_hbb/models/file_model.dart ' ;
2022-12-11 21:40:35 +08:00
import ' package:flutter_hbb/models/group_model.dart ' ;
2023-02-03 15:07:45 +08:00
import ' package:flutter_hbb/models/peer_tab_model.dart ' ;
2022-03-19 23:28:29 +08:00
import ' package:flutter_hbb/models/server_model.dart ' ;
2022-07-27 14:29:47 +08:00
import ' package:flutter_hbb/models/user_model.dart ' ;
2022-11-01 17:01:43 +08:00
import ' package:flutter_hbb/models/state_model.dart ' ;
2023-10-16 07:26:55 +08:00
import ' package:flutter_hbb/models/desktop_render_texture.dart ' ;
2023-04-21 21:40:34 +08:00
import ' package:flutter_hbb/plugin/event.dart ' ;
2023-05-09 19:47:26 +08:00
import ' package:flutter_hbb/plugin/manager.dart ' ;
2023-05-10 18:58:45 +08:00
import ' package:flutter_hbb/plugin/widgets/desc_ui.dart ' ;
2022-11-12 22:33:10 +08:00
import ' package:flutter_hbb/common/shared_state.dart ' ;
2023-05-02 23:48:49 +08:00
import ' package:flutter_hbb/utils/multi_window_manager.dart ' ;
2020-11-23 23:18:42 +08:00
import ' package:tuple/tuple.dart ' ;
2022-10-27 18:40:45 +08:00
import ' package:image/image.dart ' as img2 ;
2022-12-11 14:17:29 +08:00
import ' package:flutter_custom_cursor/cursor_manager.dart ' ;
2022-09-27 18:34:05 +08:00
import ' package:flutter_svg/flutter_svg.dart ' ;
2023-01-06 20:25:18 +08:00
import ' package:get/get.dart ' ;
2023-06-06 07:39:44 +08:00
import ' package:uuid/uuid.dart ' ;
2023-05-02 23:48:49 +08:00
import ' package:window_manager/window_manager.dart ' ;
2022-05-29 17:19:50 +08:00
2022-02-28 18:29:25 +08:00
import ' ../common.dart ' ;
2022-09-11 10:50:48 +08:00
import ' ../utils/image.dart ' as img ;
2023-03-24 15:21:14 +08:00
import ' ../common/widgets/dialog.dart ' ;
2022-09-27 20:35:02 +08:00
import ' input_model.dart ' ;
2022-08-03 22:03:31 +08:00
import ' platform_model.dart ' ;
2020-11-19 00:32:46 +08:00
2022-09-12 16:35:56 +08:00
typedef HandleMsgBox = Function ( Map < String , dynamic > evt , String id ) ;
2023-06-06 07:39:44 +08:00
typedef ReconnectHandle = Function ( OverlayDialogManager , SessionID , bool ) ;
2023-06-09 19:57:37 +08:00
final _constSessionId = Uuid ( ) . v4obj ( ) ;
2022-03-19 23:28:29 +08:00
2023-08-14 18:28:31 +08:00
class CachedPeerData {
Map < String , dynamic > updatePrivacyMode = { } ;
Map < String , dynamic > peerInfo = { } ;
List < Map < String , dynamic > > cursorDataList = [ ] ;
Map < String , dynamic > lastCursorId = { } ;
bool secure = false ;
bool direct = false ;
CachedPeerData ( ) ;
2023-08-14 20:40:58 +08:00
@ override
String toString ( ) {
return jsonEncode ( {
' updatePrivacyMode ' : updatePrivacyMode ,
' peerInfo ' : peerInfo ,
' cursorDataList ' : cursorDataList ,
' lastCursorId ' : lastCursorId ,
' secure ' : secure ,
' direct ' : direct ,
} ) ;
}
static CachedPeerData ? fromString ( String s ) {
try {
final map = jsonDecode ( s ) ;
final data = CachedPeerData ( ) ;
data . updatePrivacyMode = map [ ' updatePrivacyMode ' ] ;
data . peerInfo = map [ ' peerInfo ' ] ;
for ( final cursorData in map [ ' cursorDataList ' ] ) {
data . cursorDataList . add ( cursorData ) ;
}
data . lastCursorId = map [ ' lastCursorId ' ] ;
data . secure = map [ ' secure ' ] ;
data . direct = map [ ' direct ' ] ;
return data ;
} catch ( e ) {
debugPrint ( ' Failed to parse CachedPeerData: $ e ' ) ;
return null ;
}
}
2023-08-14 18:28:31 +08:00
}
2020-11-19 00:32:46 +08:00
class FfiModel with ChangeNotifier {
2023-08-14 18:28:31 +08:00
CachedPeerData cachedPeerData = CachedPeerData ( ) ;
2022-02-17 15:22:14 +08:00
PeerInfo _pi = PeerInfo ( ) ;
2023-10-08 21:44:54 +08:00
Rect ? _rect ;
2022-06-13 21:07:26 +08:00
2022-02-02 00:46:21 +08:00
var _inputBlocked = false ;
2022-09-03 10:39:33 +08:00
final _permissions = < String , bool > { } ;
2022-02-17 15:22:14 +08:00
bool ? _secure ;
bool ? _direct ;
2022-04-28 22:44:54 +08:00
bool _touchMode = false ;
2022-03-28 17:24:52 +08:00
Timer ? _timer ;
var _reconnects = 1 ;
2023-03-16 09:37:35 +08:00
bool _viewOnly = false ;
2022-06-13 21:07:26 +08:00
WeakReference < FFI > parent ;
2023-06-06 07:39:44 +08:00
late final SessionID sessionId ;
2020-11-22 21:08:19 +08:00
2023-08-23 23:57:09 +08:00
RxBool waitForImageDialogShow = true . obs ;
2023-08-24 12:03:29 +08:00
Timer ? waitForImageTimer ;
2023-08-23 23:57:09 +08:00
RxBool waitForFirstImage = true . obs ;
2023-10-08 21:44:54 +08:00
Rect ? get rect = > _rect ;
bool get isOriginalResolutionSet = >
_pi . tryGetDisplayIfNotAllDisplay ( ) ? . isOriginalResolutionSet ? ? false ;
bool get isVirtualDisplayResolution = >
_pi . tryGetDisplayIfNotAllDisplay ( ) ? . isVirtualDisplayResolution ? ? false ;
bool get isOriginalResolution = >
_pi . tryGetDisplayIfNotAllDisplay ( ) ? . isOriginalResolution ? ? false ;
2022-02-02 17:25:56 +08:00
2023-10-08 21:44:54 +08:00
Map < String , bool > get permissions = > _permissions ;
2022-02-02 17:25:56 +08:00
2022-04-28 22:44:54 +08:00
bool ? get secure = > _secure ;
2022-02-02 17:25:56 +08:00
2022-04-28 22:44:54 +08:00
bool ? get direct = > _direct ;
2022-02-02 17:25:56 +08:00
2022-04-28 22:44:54 +08:00
PeerInfo get pi = > _pi ;
2022-02-17 15:22:14 +08:00
2022-04-28 22:44:54 +08:00
bool get inputBlocked = > _inputBlocked ;
bool get touchMode = > _touchMode ;
2023-01-10 17:13:40 +08:00
bool get isPeerAndroid = > _pi . platform = = kPeerPlatformAndroid ;
2022-02-02 00:46:21 +08:00
2023-03-16 09:37:35 +08:00
bool get viewOnly = > _viewOnly ;
2022-02-02 00:46:21 +08:00
set inputBlocked ( v ) {
_inputBlocked = v ;
}
2020-11-19 00:32:46 +08:00
2022-06-13 21:07:26 +08:00
FfiModel ( this . parent ) {
2020-11-22 21:08:19 +08:00
clear ( ) ;
2023-06-06 07:39:44 +08:00
sessionId = parent . target ! . sessionId ;
2022-04-17 00:44:05 +08:00
}
2023-10-08 21:44:54 +08:00
Rect ? displaysRect ( ) {
final displays = _pi . getCurDisplays ( ) ;
if ( displays . isEmpty ) {
return null ;
}
double l = displays [ 0 ] . x ;
double t = displays [ 0 ] . y ;
double r = displays [ 0 ] . x + displays [ 0 ] . width ;
double b = displays [ 0 ] . y + displays [ 0 ] . height ;
for ( var display in displays . sublist ( 1 ) ) {
l = min ( l , display . x ) ;
t = min ( t , display . y ) ;
r = max ( r , display . x + display . width ) ;
b = max ( b , display . y + display . height ) ;
}
return Rect . fromLTRB ( l , t , r , b ) ;
}
2022-09-12 16:35:56 +08:00
toggleTouchMode ( ) {
2022-04-28 22:44:54 +08:00
if ( ! isPeerAndroid ) {
_touchMode = ! _touchMode ;
notifyListeners ( ) ;
}
}
2022-09-12 16:35:56 +08:00
updatePermission ( Map < String , dynamic > evt , String id ) {
2020-11-22 21:08:19 +08:00
evt . forEach ( ( k , v ) {
2022-08-04 17:24:02 +08:00
if ( k = = ' name ' | | k . isEmpty ) return ;
2020-11-22 21:08:19 +08:00
_permissions [ k ] = v = = ' true ' ;
} ) ;
2022-10-16 12:29:51 +08:00
// Only inited at remote page
if ( desktopType = = DesktopType . remote ) {
KeyboardEnabledState . find ( id ) . value = _permissions [ ' keyboard ' ] ! = false ;
}
2022-09-03 10:39:33 +08:00
debugPrint ( ' $ _permissions ' ) ;
2022-02-06 16:29:56 +08:00
notifyListeners ( ) ;
2020-11-19 00:32:46 +08:00
}
2023-03-28 10:36:59 +08:00
bool get keyboard = > _permissions [ ' keyboard ' ] ! = false ;
2020-11-23 23:18:42 +08:00
2022-09-12 16:35:56 +08:00
clear ( ) {
2020-11-22 21:08:19 +08:00
_pi = PeerInfo ( ) ;
2020-11-29 00:13:55 +08:00
_secure = null ;
_direct = null ;
2022-02-02 00:46:21 +08:00
_inputBlocked = false ;
2022-03-28 17:24:52 +08:00
_timer ? . cancel ( ) ;
_timer = null ;
2020-11-28 13:22:19 +08:00
clearPermissions ( ) ;
2023-08-24 12:03:29 +08:00
waitForImageTimer ? . cancel ( ) ;
2020-11-28 13:22:19 +08:00
}
2022-09-12 16:35:56 +08:00
setConnectionType ( String peerId , bool secure , bool direct ) {
2023-08-14 18:28:31 +08:00
cachedPeerData . secure = secure ;
cachedPeerData . direct = direct ;
2020-11-29 00:13:55 +08:00
_secure = secure ;
_direct = direct ;
2022-08-29 18:48:12 +08:00
try {
var connectionType = ConnectionTypeState . find ( peerId ) ;
connectionType . setSecure ( secure ) ;
connectionType . setDirect ( direct ) ;
} catch ( e ) {
//
}
2020-11-29 00:13:55 +08:00
}
2022-09-27 18:34:05 +08:00
Widget ? getConnectionImage ( ) {
2022-08-29 18:48:12 +08:00
if ( secure = = null | | direct = = null ) {
return null ;
} else {
final icon =
2022-09-07 12:20:53 +08:00
' ${ secure = = true ? ' secure ' : ' insecure ' } ${ direct = = true ? ' ' : ' _relay ' } ' ;
2022-09-29 14:08:15 +08:00
return SvgPicture . asset ( ' assets/ $ icon .svg ' , width: 48 , height: 48 ) ;
2020-11-29 00:13:55 +08:00
}
}
2022-09-12 16:35:56 +08:00
clearPermissions ( ) {
2022-02-02 00:46:21 +08:00
_inputBlocked = false ;
2020-11-22 21:08:19 +08:00
_permissions . clear ( ) ;
2020-11-19 00:32:46 +08:00
}
2022-03-07 22:54:34 +08:00
2023-08-14 18:28:31 +08:00
handleCachedPeerData ( CachedPeerData data , String peerId ) async {
handleMsgBox ( {
' type ' : ' success ' ,
' title ' : ' Successful ' ,
' text ' : ' Connected, waiting for image... ' ,
' link ' : ' ' ,
} , sessionId , peerId ) ;
updatePrivacyMode ( data . updatePrivacyMode , sessionId , peerId ) ;
setConnectionType ( peerId , data . secure , data . direct ) ;
2023-10-17 00:30:34 +08:00
await handlePeerInfo ( data . peerInfo , peerId , true ) ;
2023-10-03 22:15:58 +08:00
for ( final element in data . cursorDataList ) {
updateLastCursorId ( element ) ;
2023-09-27 09:16:51 +08:00
await handleCursorData ( element ) ;
2023-08-14 18:28:31 +08:00
}
2023-10-08 21:44:54 +08:00
if ( data . lastCursorId . isNotEmpty ) {
updateLastCursorId ( data . lastCursorId ) ;
handleCursorId ( data . lastCursorId ) ;
}
2023-08-14 18:28:31 +08:00
}
2023-06-06 07:39:44 +08:00
// todo: why called by two position
StreamEventHandler startEventListener ( SessionID sessionId , String peerId ) {
2022-09-11 10:50:48 +08:00
return ( evt ) async {
2022-05-31 17:36:36 +08:00
var name = evt [ ' name ' ] ;
if ( name = = ' msgbox ' ) {
2023-06-06 07:39:44 +08:00
handleMsgBox ( evt , sessionId , peerId ) ;
2022-05-31 17:36:36 +08:00
} else if ( name = = ' peer_info ' ) {
2023-10-17 00:30:34 +08:00
handlePeerInfo ( evt , peerId , false ) ;
2023-02-17 13:32:17 +08:00
} else if ( name = = ' sync_peer_info ' ) {
2023-10-08 21:44:54 +08:00
handleSyncPeerInfo ( evt , sessionId , peerId ) ;
2022-05-31 17:36:36 +08:00
} else if ( name = = ' connection_ready ' ) {
2022-08-29 18:48:12 +08:00
setConnectionType (
peerId , evt [ ' secure ' ] = = ' true ' , evt [ ' direct ' ] = = ' true ' ) ;
2022-05-31 17:36:36 +08:00
} else if ( name = = ' switch_display ' ) {
2023-10-08 21:44:54 +08:00
// switch display is kept for backward compatibility
2023-06-06 07:39:44 +08:00
handleSwitchDisplay ( evt , sessionId , peerId ) ;
2022-05-31 17:36:36 +08:00
} else if ( name = = ' cursor_data ' ) {
2023-10-03 22:15:58 +08:00
updateLastCursorId ( evt ) ;
2023-08-14 18:28:31 +08:00
await handleCursorData ( evt ) ;
2022-05-31 17:36:36 +08:00
} else if ( name = = ' cursor_id ' ) {
2023-10-03 22:15:58 +08:00
updateLastCursorId ( evt ) ;
handleCursorId ( evt ) ;
2022-05-31 17:36:36 +08:00
} else if ( name = = ' cursor_position ' ) {
2022-09-11 10:50:48 +08:00
await parent . target ? . cursorModel . updateCursorPosition ( evt , peerId ) ;
2022-05-31 17:36:36 +08:00
} else if ( name = = ' clipboard ' ) {
Clipboard . setData ( ClipboardData ( text: evt [ ' content ' ] ) ) ;
} else if ( name = = ' permission ' ) {
2023-02-25 22:47:22 +08:00
updatePermission ( evt , peerId ) ;
2022-05-31 17:36:36 +08:00
} else if ( name = = ' chat_client_mode ' ) {
2022-06-13 21:07:26 +08:00
parent . target ? . chatModel
2022-09-07 12:20:53 +08:00
. receive ( ChatModel . clientModeID , evt [ ' text ' ] ? ? ' ' ) ;
2022-05-31 17:36:36 +08:00
} else if ( name = = ' chat_server_mode ' ) {
2022-06-13 21:07:26 +08:00
parent . target ? . chatModel
2022-09-07 12:20:53 +08:00
. receive ( int . parse ( evt [ ' id ' ] as String ) , evt [ ' text ' ] ? ? ' ' ) ;
2022-05-31 17:36:36 +08:00
} else if ( name = = ' file_dir ' ) {
2022-06-13 21:07:26 +08:00
parent . target ? . fileModel . receiveFileDir ( evt ) ;
2022-05-31 17:36:36 +08:00
} else if ( name = = ' job_progress ' ) {
2023-03-08 20:05:55 +08:00
parent . target ? . fileModel . jobController . tryUpdateJobProgress ( evt ) ;
2022-05-31 17:36:36 +08:00
} else if ( name = = ' job_done ' ) {
2023-03-08 20:05:55 +08:00
parent . target ? . fileModel . jobController . jobDone ( evt ) ;
parent . target ? . fileModel . refreshAll ( ) ;
2022-05-31 17:36:36 +08:00
} else if ( name = = ' job_error ' ) {
2023-03-08 20:05:55 +08:00
parent . target ? . fileModel . jobController . jobError ( evt ) ;
2022-05-31 17:36:36 +08:00
} else if ( name = = ' override_file_confirm ' ) {
2023-03-15 15:19:40 +08:00
parent . target ? . fileModel . postOverrideFileConfirm ( evt ) ;
2022-07-11 18:23:58 +08:00
} else if ( name = = ' load_last_job ' ) {
2023-03-08 20:05:55 +08:00
parent . target ? . fileModel . jobController . loadLastJob ( evt ) ;
2022-07-11 10:30:45 +08:00
} else if ( name = = ' update_folder_files ' ) {
2023-03-08 20:05:55 +08:00
parent . target ? . fileModel . jobController . updateFolderFiles ( evt ) ;
2022-09-05 19:41:09 +08:00
} else if ( name = = ' add_connection ' ) {
parent . target ? . serverModel . addConnection ( evt ) ;
2022-05-31 17:36:36 +08:00
} else if ( name = = ' on_client_remove ' ) {
2022-06-13 21:07:26 +08:00
parent . target ? . serverModel . onClientRemove ( evt ) ;
2022-08-05 20:29:43 +08:00
} else if ( name = = ' update_quality_status ' ) {
parent . target ? . qualityMonitorModel . updateQualityStatus ( evt ) ;
2022-08-08 22:00:01 +08:00
} else if ( name = = ' update_block_input_state ' ) {
2022-08-26 23:28:08 +08:00
updateBlockInputState ( evt , peerId ) ;
2022-08-08 22:00:01 +08:00
} else if ( name = = ' update_privacy_mode ' ) {
2023-06-06 07:39:44 +08:00
updatePrivacyMode ( evt , sessionId , peerId ) ;
2022-11-10 10:27:13 +08:00
} else if ( name = = ' show_elevation ' ) {
final show = evt [ ' show ' ] . toString ( ) = = ' true ' ;
parent . target ? . serverModel . setShowElevation ( show ) ;
2022-11-15 16:49:55 +08:00
} else if ( name = = ' cancel_msgbox ' ) {
2023-06-06 07:39:44 +08:00
cancelMsgBox ( evt , sessionId ) ;
2023-01-17 13:28:33 +08:00
} else if ( name = = ' switch_back ' ) {
final peer_id = evt [ ' peer_id ' ] . toString ( ) ;
2023-06-06 07:39:44 +08:00
await bind . sessionSwitchSides ( sessionId: sessionId ) ;
2023-01-17 13:28:33 +08:00
closeConnection ( id: peer_id ) ;
2023-02-24 15:51:13 +08:00
} else if ( name = = ' portable_service_running ' ) {
2023-10-08 21:44:54 +08:00
_handlePortableServiceRunning ( peerId , evt ) ;
2023-04-21 21:40:34 +08:00
} else if ( name = = ' on_url_scheme_received ' ) {
2023-06-10 18:24:03 +08:00
// currently comes from "_url" ipc of mac and dbus of linux
2023-05-02 23:48:49 +08:00
onUrlSchemeReceived ( evt ) ;
2023-04-21 21:40:34 +08:00
} else if ( name = = ' on_voice_call_waiting ' ) {
2023-02-06 20:10:39 +08:00
// Waiting for the response from the peer.
parent . target ? . chatModel . onVoiceCallWaiting ( ) ;
2023-04-21 21:40:34 +08:00
} else if ( name = = ' on_voice_call_started ' ) {
2023-02-06 20:10:39 +08:00
// Voice call is connected.
parent . target ? . chatModel . onVoiceCallStarted ( ) ;
2023-04-21 21:40:34 +08:00
} else if ( name = = ' on_voice_call_closed ' ) {
2023-02-06 20:10:39 +08:00
// Voice call is closed with reason.
final reason = evt [ ' reason ' ] . toString ( ) ;
parent . target ? . chatModel . onVoiceCallClosed ( reason ) ;
2023-04-21 21:40:34 +08:00
} else if ( name = = ' on_voice_call_incoming ' ) {
2023-02-06 20:10:39 +08:00
// Voice call is requested by the peer.
parent . target ? . chatModel . onVoiceCallIncoming ( ) ;
2023-04-21 21:40:34 +08:00
} else if ( name = = ' update_voice_call_state ' ) {
2023-02-07 16:11:55 +08:00
parent . target ? . serverModel . updateVoiceCallState ( evt ) ;
2023-04-21 21:40:34 +08:00
} else if ( name = = ' fingerprint ' ) {
2023-04-19 14:39:22 +08:00
FingerprintState . find ( peerId ) . value = evt [ ' fingerprint ' ] ? ? ' ' ;
2023-05-09 19:47:26 +08:00
} else if ( name = = ' plugin_manager ' ) {
pluginManager . handleEvent ( evt ) ;
2023-04-21 21:40:34 +08:00
} else if ( name = = ' plugin_event ' ) {
2023-06-06 07:39:44 +08:00
handlePluginEvent ( evt ,
( Map < String , dynamic > e ) = > handleMsgBox ( e , sessionId , peerId ) ) ;
2023-04-21 21:40:34 +08:00
} else if ( name = = ' plugin_reload ' ) {
2023-06-06 07:39:44 +08:00
handleReloading ( evt ) ;
2023-04-21 21:40:34 +08:00
} else if ( name = = ' plugin_option ' ) {
2023-06-06 07:39:44 +08:00
handleOption ( evt ) ;
2023-08-21 08:39:47 +08:00
} else if ( name = = " sync_peer_password_to_ab " ) {
if ( desktopType = = DesktopType . main ) {
final id = evt [ ' id ' ] ;
final password = evt [ ' password ' ] ;
if ( id ! = null & & password ! = null ) {
if ( gFFI . abModel
. changePassword ( id . toString ( ) , password . toString ( ) ) ) {
gFFI . abModel . pushAb ( toastIfFail: false , toastIfSucc: false ) ;
}
}
}
2023-09-06 16:56:39 +08:00
} else if ( name = = " cm_file_transfer_log " ) {
if ( isDesktop ) {
gFFI . cmFileModel . onFileTransferLog ( evt [ ' log ' ] ) ;
}
2023-02-06 20:10:39 +08:00
} else {
2023-04-21 21:40:34 +08:00
debugPrint ( ' Unknown event name: $ name ' ) ;
2022-05-31 17:36:36 +08:00
}
} ;
}
2023-05-02 23:48:49 +08:00
onUrlSchemeReceived ( Map < String , dynamic > evt ) {
final url = evt [ ' url ' ] . toString ( ) . trim ( ) ;
2023-07-07 12:22:39 +08:00
if ( url . startsWith ( kUniLinksPrefix ) & & handleUriLink ( uriString: url ) ) {
2023-06-10 18:24:03 +08:00
return ;
}
switch ( url ) {
case kUrlActionClose:
debugPrint ( " closing all instances " ) ;
Future . microtask ( ( ) async {
await rustDeskWinManager . closeAllSubWindows ( ) ;
windowManager . close ( ) ;
} ) ;
break ;
default :
2023-08-02 20:38:09 +08:00
windowOnTop ( null ) ;
2023-06-10 18:24:03 +08:00
break ;
2023-05-02 23:48:49 +08:00
}
}
2022-05-28 03:56:42 +08:00
/// Bind the event listener to receive events from the Rust core.
2023-06-06 07:39:44 +08:00
updateEventListener ( SessionID sessionId , String peerId ) {
platformFFI . setEventCallback ( startEventListener ( sessionId , peerId ) ) ;
2020-11-19 00:32:46 +08:00
}
2023-10-08 21:44:54 +08:00
_handlePortableServiceRunning ( String peerId , Map < String , dynamic > evt ) {
final running = evt [ ' running ' ] = = ' true ' ;
parent . target ? . elevationModel . onPortableServiceRunning ( running ) ;
if ( running ) {
if ( pi . primaryDisplay ! = kInvalidDisplayIndex ) {
if ( pi . currentDisplay ! = pi . primaryDisplay ) {
// Notify to switch display
msgBox ( sessionId , ' custom-nook-nocancel-hasclose-info ' , ' Prompt ' ,
' elevated_switch_display_msg ' , ' ' , parent . target ! . dialogManager ) ;
bind . sessionSwitchDisplay (
sessionId: sessionId ,
value: Int32List . fromList ( [ pi . primaryDisplay ] ) ) ;
}
}
}
}
handleAliasChanged ( Map < String , dynamic > evt ) {
if ( ! isDesktop ) return ;
final String peerId = evt [ ' id ' ] ;
final String alias = evt [ ' alias ' ] ;
String label = getDesktopTabLabel ( peerId , alias ) ;
final rxTabLabel = PeerStringOption . find ( evt [ ' id ' ] , ' tabLabel ' ) ;
if ( rxTabLabel . value ! = label ) {
rxTabLabel . value = label ;
}
}
updateCurDisplay ( SessionID sessionId ) {
final newRect = displaysRect ( ) ;
if ( newRect = = null ) {
return ;
}
if ( newRect ! = _rect ) {
if ( newRect . left ! = _rect ? . left | | newRect . top ! = _rect ? . top ) {
2023-02-25 22:47:22 +08:00
parent . target ? . cursorModel
2023-10-08 21:44:54 +08:00
. updateDisplayOrigin ( newRect . left , newRect . top ) ;
2023-02-25 22:47:22 +08:00
}
2023-10-16 07:26:55 +08:00
_rect = newRect ;
2023-10-08 21:44:54 +08:00
parent . target ? . canvasModel . updateViewStyle ( ) ;
2023-06-06 07:39:44 +08:00
_updateSessionWidthHeight ( sessionId ) ;
2023-02-25 22:47:22 +08:00
}
}
2023-06-06 07:39:44 +08:00
handleSwitchDisplay (
Map < String , dynamic > evt , SessionID sessionId , String peerId ) {
2023-10-08 21:44:54 +08:00
final curDisplay = int . parse ( evt [ ' display ' ] ) ;
if ( _pi . currentDisplay ! = kAllDisplayValue ) {
2023-10-18 09:59:02 +08:00
if ( bind . peerGetDefaultSessionsCount ( id: peerId ) > 1 ) {
if ( curDisplay ! = _pi . currentDisplay ) {
return ;
}
}
2023-10-08 21:44:54 +08:00
_pi . currentDisplay = curDisplay ;
}
2023-02-25 22:47:22 +08:00
var newDisplay = Display ( ) ;
2023-05-18 16:17:51 +08:00
newDisplay . x = double . tryParse ( evt [ ' x ' ] ) ? ? newDisplay . x ;
newDisplay . y = double . tryParse ( evt [ ' y ' ] ) ? ? newDisplay . y ;
newDisplay . width = int . tryParse ( evt [ ' width ' ] ) ? ? newDisplay . width ;
newDisplay . height = int . tryParse ( evt [ ' height ' ] ) ? ? newDisplay . height ;
newDisplay . cursorEmbedded = int . tryParse ( evt [ ' cursor_embedded ' ] ) = = 1 ;
newDisplay . originalWidth =
2023-05-18 21:25:48 +08:00
int . tryParse ( evt [ ' original_width ' ] ) ? ? kInvalidResolutionValue ;
2023-05-18 16:17:51 +08:00
newDisplay . originalHeight =
2023-05-18 21:25:48 +08:00
int . tryParse ( evt [ ' original_height ' ] ) ? ? kInvalidResolutionValue ;
2023-10-08 21:44:54 +08:00
_pi . displays [ curDisplay ] = newDisplay ;
2022-06-02 17:16:23 +08:00
2023-10-08 21:44:54 +08:00
updateCurDisplay ( sessionId ) ;
2022-12-30 16:14:30 +08:00
try {
2023-10-08 21:44:54 +08:00
CurrentDisplayState . find ( peerId ) . value = curDisplay ;
2022-12-30 16:14:30 +08:00
} catch ( e ) {
//
}
2022-09-21 16:03:08 +08:00
parent . target ? . recordingModel . onSwitchDisplay ( ) ;
2023-04-21 21:40:34 +08:00
handleResolutions ( peerId , evt [ ' resolutions ' ] ) ;
2020-11-25 23:52:58 +08:00
notifyListeners ( ) ;
2020-11-19 00:32:46 +08:00
}
2023-06-06 07:39:44 +08:00
cancelMsgBox ( Map < String , dynamic > evt , SessionID sessionId ) {
2022-11-15 16:49:55 +08:00
if ( parent . target = = null ) return ;
final dialogManager = parent . target ! . dialogManager ;
2023-06-06 07:39:44 +08:00
final tag = ' $ sessionId - ${ evt [ ' tag ' ] } ' ;
2022-11-15 16:49:55 +08:00
dialogManager . dismissByTag ( tag ) ;
}
2022-05-28 03:56:42 +08:00
/// Handle the message box event based on [evt] and [id].
2023-06-06 07:39:44 +08:00
handleMsgBox ( Map < String , dynamic > evt , SessionID sessionId , String peerId ) {
2022-08-12 18:42:02 +08:00
if ( parent . target = = null ) return ;
final dialogManager = parent . target ! . dialogManager ;
2022-10-14 11:19:49 +08:00
final type = evt [ ' type ' ] ;
final title = evt [ ' title ' ] ;
final text = evt [ ' text ' ] ;
final link = evt [ ' link ' ] ;
2022-03-28 17:24:52 +08:00
if ( type = = ' re-input-password ' ) {
2023-06-06 07:39:44 +08:00
wrongPasswordDialog ( sessionId , dialogManager , type , title , text ) ;
2022-03-28 17:24:52 +08:00
} else if ( type = = ' input-password ' ) {
2023-06-06 07:39:44 +08:00
enterPasswordDialog ( sessionId , dialogManager ) ;
2023-03-30 14:11:56 +08:00
} else if ( type = = ' session-login ' | | type = = ' session-re-login ' ) {
2023-06-06 07:39:44 +08:00
enterUserLoginDialog ( sessionId , dialogManager ) ;
2023-03-30 14:11:56 +08:00
} else if ( type = = ' session-login-password ' | |
type = = ' session-login-password ' ) {
2023-06-06 07:39:44 +08:00
enterUserLoginAndPasswordDialog ( sessionId , dialogManager ) ;
2022-08-04 17:24:02 +08:00
} else if ( type = = ' restarting ' ) {
2023-06-06 07:39:44 +08:00
showMsgBox ( sessionId , type , title , text , link , false , dialogManager ,
2022-10-25 10:27:34 +08:00
hasCancel: false ) ;
2022-11-20 15:53:08 +08:00
} else if ( type = = ' wait-remote-accept-nook ' ) {
2023-06-06 07:39:44 +08:00
showWaitAcceptDialog ( sessionId , type , title , text , dialogManager ) ;
2023-01-12 21:03:05 +08:00
} else if ( type = = ' on-uac ' | | type = = ' on-foreground-elevated ' ) {
2023-06-06 07:39:44 +08:00
showOnBlockDialog ( sessionId , type , title , text , dialogManager ) ;
2023-01-12 21:03:05 +08:00
} else if ( type = = ' wait-uac ' ) {
2023-06-06 07:39:44 +08:00
showWaitUacDialog ( sessionId , dialogManager , type ) ;
2023-01-12 21:03:05 +08:00
} else if ( type = = ' elevation-error ' ) {
2023-06-06 07:39:44 +08:00
showElevationError ( sessionId , type , title , text , dialogManager ) ;
2023-04-21 21:40:34 +08:00
} else if ( type = = ' relay-hint ' ) {
2023-07-01 17:58:11 +08:00
showRelayHintDialog ( sessionId , type , title , text , dialogManager , peerId ) ;
2023-08-06 22:11:31 +08:00
} else if ( text = = ' Connected, waiting for image... ' ) {
showConnectedWaitingForImage ( dialogManager , sessionId , type , title , text ) ;
2022-03-28 17:24:52 +08:00
} else {
var hasRetry = evt [ ' hasRetry ' ] = = ' true ' ;
2023-06-06 07:39:44 +08:00
showMsgBox ( sessionId , type , title , text , link , hasRetry , dialogManager ) ;
2022-03-28 17:24:52 +08:00
}
}
2022-05-28 03:56:42 +08:00
/// Show a message box with [type], [title] and [text].
2023-06-06 07:39:44 +08:00
showMsgBox ( SessionID sessionId , String type , String title , String text ,
String link , bool hasRetry , OverlayDialogManager dialogManager ,
2022-08-04 17:24:02 +08:00
{ bool ? hasCancel } ) {
2023-06-06 07:39:44 +08:00
msgBox ( sessionId , type , title , text , link , dialogManager ,
2023-10-21 15:25:01 +08:00
hasCancel: hasCancel ,
reconnect: reconnect ,
reconnectTimeout: hasRetry ? _reconnects : null ) ;
2022-05-16 00:01:27 +08:00
_timer ? . cancel ( ) ;
2022-03-28 17:24:52 +08:00
if ( hasRetry ) {
_timer = Timer ( Duration ( seconds: _reconnects ) , ( ) {
2023-06-06 07:39:44 +08:00
reconnect ( dialogManager , sessionId , false ) ;
2022-03-28 17:24:52 +08:00
} ) ;
_reconnects * = 2 ;
} else {
_reconnects = 1 ;
}
}
2023-06-06 07:39:44 +08:00
void reconnect ( OverlayDialogManager dialogManager , SessionID sessionId ,
bool forceRelay ) {
bind . sessionReconnect ( sessionId: sessionId , forceRelay: forceRelay ) ;
2023-02-16 14:54:13 +08:00
clearPermissions ( ) ;
2023-10-21 15:25:01 +08:00
dialogManager . dismissAll ( ) ;
2023-02-16 14:54:13 +08:00
dialogManager . showLoading ( translate ( ' Connecting... ' ) ,
onCancel: closeConnection ) ;
}
2023-06-06 07:39:44 +08:00
void showRelayHintDialog ( SessionID sessionId , String type , String title ,
2023-07-01 17:58:11 +08:00
String text , OverlayDialogManager dialogManager , String peerId ) {
2023-06-06 07:39:44 +08:00
dialogManager . show ( tag: ' $ sessionId - $ type ' , ( setState , close , context ) {
2023-02-14 19:44:14 +08:00
onClose ( ) {
closeConnection ( ) ;
close ( ) ;
}
final style =
ElevatedButton . styleFrom ( backgroundColor: Colors . green [ 700 ] ) ;
2023-07-01 17:58:11 +08:00
var hint = " \n \n ${ translate ( ' relay_hint_tip ' ) } " ;
if ( text . contains ( " 10054 " ) | | text . contains ( " 104 " ) ) {
hint = " " ;
}
final alreadyForceAlwaysRelay = bind
. mainGetPeerOptionSync ( id: peerId , key: ' force-always-relay ' )
. isNotEmpty ;
2023-02-14 19:44:14 +08:00
return CustomAlertDialog (
title: null ,
2023-07-01 17:58:11 +08:00
content: msgboxContent ( type , title , " ${ translate ( text ) } $ hint " ) ,
2023-02-14 19:44:14 +08:00
actions: [
dialogButton ( ' Close ' , onPressed: onClose , isOutline: true ) ,
2023-02-16 14:54:13 +08:00
dialogButton ( ' Retry ' ,
2023-06-06 07:39:44 +08:00
onPressed: ( ) = > reconnect ( dialogManager , sessionId , false ) ) ,
2023-07-01 17:58:11 +08:00
if ( ! alreadyForceAlwaysRelay )
dialogButton ( ' Connect via relay ' ,
onPressed: ( ) = > reconnect ( dialogManager , sessionId , true ) ,
buttonStyle: style ) ,
2023-02-14 19:44:14 +08:00
] ,
onCancel: onClose ,
) ;
} ) ;
}
2023-08-06 22:11:31 +08:00
void showConnectedWaitingForImage ( OverlayDialogManager dialogManager ,
SessionID sessionId , String type , String title , String text ) {
onClose ( ) {
closeConnection ( ) ;
}
2023-08-23 23:57:09 +08:00
if ( waitForFirstImage . isFalse ) return ;
2023-08-09 14:59:52 +08:00
dialogManager . show (
( setState , close , context ) = > CustomAlertDialog (
title: null ,
content: SelectionArea ( child: msgboxContent ( type , title , text ) ) ,
actions: [
dialogButton ( " Cancel " , onPressed: onClose , isOutline: true )
] ,
onCancel: onClose ) ,
tag: ' $ sessionId -waiting-for-image ' ,
) ;
2023-08-23 23:57:09 +08:00
waitForImageDialogShow . value = true ;
2023-08-24 12:03:29 +08:00
waitForImageTimer = Timer ( Duration ( milliseconds: 1500 ) , ( ) {
if ( waitForFirstImage . isTrue ) {
bind . sessionInputOsPassword ( sessionId: sessionId , value: ' ' ) ;
}
} ) ;
2023-08-09 14:59:52 +08:00
bind . sessionOnWaitingForImageDialogShow ( sessionId: sessionId ) ;
2023-08-06 22:11:31 +08:00
}
2023-06-06 07:39:44 +08:00
_updateSessionWidthHeight ( SessionID sessionId ) {
2023-10-08 21:44:54 +08:00
if ( _rect = = null ) return ;
if ( _rect ! . width < = 0 | | _rect ! . height < = 0 ) {
2023-03-16 09:37:35 +08:00
debugPrintStack (
2023-10-08 21:44:54 +08:00
label: ' invalid display size ( ${ _rect ! . width } , ${ _rect ! . height } ) ' ) ;
2023-03-09 19:38:47 +08:00
} else {
2023-10-08 21:44:54 +08:00
final displays = _pi . getCurDisplays ( ) ;
if ( displays . length = = 1 ) {
bind . sessionSetSize (
sessionId: sessionId ,
display:
pi . currentDisplay = = kAllDisplayValue ? 0 : pi . currentDisplay ,
width: _rect ! . width . toInt ( ) ,
height: _rect ! . height . toInt ( ) ,
) ;
} else {
for ( int i = 0 ; i < displays . length ; + + i ) {
bind . sessionSetSize (
sessionId: sessionId ,
display: i ,
width: displays [ i ] . width . toInt ( ) ,
height: displays [ i ] . height . toInt ( ) ,
) ;
}
}
2023-03-09 19:38:47 +08:00
}
2023-02-21 21:56:46 +08:00
}
2022-05-28 03:56:42 +08:00
/// Handle the peer info event based on [evt].
2023-10-17 00:30:34 +08:00
handlePeerInfo ( Map < String , dynamic > evt , String peerId , bool isCache ) async {
2023-08-14 18:28:31 +08:00
cachedPeerData . peerInfo = evt ;
2022-09-01 21:18:29 +08:00
// recent peer updated by handle_peer_info(ui_session_interface.rs) --> handle_peer_info(client.rs) --> save_config(client.rs)
bind . mainLoadRecentPeers ( ) ;
2022-08-12 18:42:02 +08:00
parent . target ? . dialogManager . dismissAll ( ) ;
2020-11-27 17:34:09 +08:00
_pi . version = evt [ ' version ' ] ;
2023-10-08 21:44:54 +08:00
_pi . isSupportMultiUiSession =
bind . isSupportMultiUiSession ( version: _pi . version ) ;
2020-11-19 00:32:46 +08:00
_pi . username = evt [ ' username ' ] ;
_pi . hostname = evt [ ' hostname ' ] ;
_pi . platform = evt [ ' platform ' ] ;
2022-09-07 12:20:53 +08:00
_pi . sasEnabled = evt [ ' sas_enabled ' ] = = ' true ' ;
2023-10-08 21:44:54 +08:00
final currentDisplay = int . parse ( evt [ ' current_display ' ] ) ;
if ( _pi . primaryDisplay = = kInvalidDisplayIndex ) {
_pi . primaryDisplay = currentDisplay ;
}
if ( bind . peerGetDefaultSessionsCount ( id: peerId ) < = 1 ) {
_pi . currentDisplay = currentDisplay ;
}
2022-03-16 15:33:00 +08:00
2022-08-26 23:28:08 +08:00
try {
CurrentDisplayState . find ( peerId ) . value = _pi . currentDisplay ;
} catch ( e ) {
//
}
2023-03-18 13:48:56 +08:00
final connType = parent . target ? . connType ;
2022-04-28 22:44:54 +08:00
if ( isPeerAndroid ) {
_touchMode = true ;
} else {
2023-06-06 07:39:44 +08:00
_touchMode = await bind . sessionGetOption (
sessionId: sessionId , arg: ' touch-mode ' ) ! =
' ' ;
2022-04-28 22:44:54 +08:00
}
2023-03-18 13:48:56 +08:00
if ( connType = = ConnType . fileTransfer ) {
2022-06-13 21:07:26 +08:00
parent . target ? . fileModel . onReady ( ) ;
2023-03-18 13:48:56 +08:00
} else if ( connType = = ConnType . defaultConn ) {
2022-03-16 15:33:00 +08:00
_pi . displays = [ ] ;
List < dynamic > displays = json . decode ( evt [ ' displays ' ] ) ;
for ( int i = 0 ; i < displays . length ; + + i ) {
2023-05-18 16:17:51 +08:00
_pi . displays . add ( evtToDisplay ( displays [ i ] ) ) ;
2022-03-16 15:33:00 +08:00
}
2023-10-08 21:44:54 +08:00
_pi . displaysCount . value = _pi . displays . length ;
2022-03-16 15:33:00 +08:00
if ( _pi . currentDisplay < _pi . displays . length ) {
2023-10-08 21:44:54 +08:00
// now replaced to _updateCurDisplay
updateCurDisplay ( sessionId ) ;
2022-03-16 15:33:00 +08:00
}
2022-10-04 21:19:31 +08:00
if ( displays . isNotEmpty ) {
2022-04-26 21:21:08 +08:00
_reconnects = 1 ;
2023-08-23 23:57:09 +08:00
waitForFirstImage . value = true ;
2022-03-16 15:33:00 +08:00
}
2022-12-02 21:34:20 +08:00
Map < String , dynamic > features = json . decode ( evt [ ' features ' ] ) ;
_pi . features . privacyMode = features [ ' privacy_mode ' ] = = 1 ;
2023-02-09 15:53:51 +08:00
handleResolutions ( peerId , evt [ " resolutions " ] ) ;
2023-02-24 15:51:13 +08:00
parent . target ? . elevationModel . onPeerInfo ( _pi ) ;
2020-11-19 00:53:10 +08:00
}
2023-03-18 13:48:56 +08:00
if ( connType = = ConnType . defaultConn ) {
2023-06-06 07:39:44 +08:00
setViewOnly (
peerId ,
bind . sessionGetToggleOptionSync (
sessionId: sessionId , arg: ' view-only ' ) ) ;
2023-03-18 13:48:56 +08:00
}
2023-03-21 11:27:30 +08:00
if ( connType = = ConnType . defaultConn ) {
2023-10-16 23:19:07 +08:00
final platformAdditions = evt [ ' platform_additions ' ] ;
if ( platformAdditions ! = null & & platformAdditions ! = ' ' ) {
2023-03-21 12:25:47 +08:00
try {
2023-10-16 23:19:07 +08:00
_pi . platformAdditions = json . decode ( platformAdditions ) ;
2023-03-21 12:25:47 +08:00
} catch ( e ) {
2023-10-16 23:19:07 +08:00
debugPrint ( ' Failed to decode platformAdditions $ e ' ) ;
2023-03-21 12:25:47 +08:00
}
}
2023-03-21 11:27:30 +08:00
}
2023-05-19 20:48:47 +08:00
2023-08-24 12:03:29 +08:00
_pi . isSet . value = true ;
2023-05-19 20:48:47 +08:00
stateGlobal . resetLastResolutionGroupValues ( peerId ) ;
2020-11-29 14:28:07 +08:00
notifyListeners ( ) ;
2023-10-17 00:30:34 +08:00
if ( ! isCache ) {
2023-10-17 13:57:06 +08:00
tryUseAllMyDisplaysForTheRemoteSession ( peerId ) ;
2023-10-17 00:30:34 +08:00
}
}
2023-10-17 13:57:06 +08:00
tryUseAllMyDisplaysForTheRemoteSession ( String peerId ) async {
if ( bind . sessionGetUseAllMyDisplaysForTheRemoteSession (
sessionId: sessionId ) ! =
2023-10-17 00:30:34 +08:00
' Y ' ) {
return ;
}
if ( ! _pi . isSupportMultiDisplay | | _pi . displays . length < = 1 ) {
return ;
}
final screenRectList = await getScreenRectList ( ) ;
if ( screenRectList . length < = 1 ) {
return ;
}
// to-do: peer currentDisplay is the primary display, but the primary display may not be the first display.
// local primary display also may not be the first display.
//
// 0 is assumed to be the primary display here, for now.
// move to the first display and set fullscreen
bind . sessionSwitchDisplay (
sessionId: sessionId , value: Int32List . fromList ( [ 0 ] ) ) ;
_pi . currentDisplay = 0 ;
try {
CurrentDisplayState . find ( peerId ) . value = _pi . currentDisplay ;
} catch ( e ) {
//
}
await tryMoveToScreenAndSetFullscreen ( screenRectList [ 0 ] ) ;
final length = _pi . displays . length < screenRectList . length
? _pi . displays . length
: screenRectList . length ;
for ( var i = 1 ; i < length ; i + + ) {
openMonitorInNewTabOrWindow ( i , peerId , _pi ,
screenRect: screenRectList [ i ] ) ;
}
2020-11-19 00:32:46 +08:00
}
2022-08-08 22:00:01 +08:00
2023-09-04 14:17:54 +08:00
tryShowAndroidActionsOverlay ( { int delayMSecs = 10 } ) {
if ( isPeerAndroid ) {
if ( parent . target ? . connType = = ConnType . defaultConn & &
parent . target ! = null & &
parent . target ! . ffiModel . permissions [ ' keyboard ' ] ! = false ) {
Timer (
Duration ( milliseconds: delayMSecs ) ,
( ) = > parent . target ! . dialogManager
. showMobileActionsOverlay ( ffi: parent . target ! ) ) ;
}
}
}
2023-02-09 15:53:51 +08:00
handleResolutions ( String id , dynamic resolutions ) {
try {
final List < dynamic > dynamicArray = jsonDecode ( resolutions as String ) ;
List < Resolution > arr = List . empty ( growable: true ) ;
for ( int i = 0 ; i < dynamicArray . length ; i + + ) {
var width = dynamicArray [ i ] [ " width " ] ;
var height = dynamicArray [ i ] [ " height " ] ;
if ( width is int & & width > 0 & & height is int & & height > 0 ) {
arr . add ( Resolution ( width , height ) ) ;
}
}
arr . sort ( ( a , b ) {
if ( b . width ! = a . width ) {
return b . width - a . width ;
} else {
return b . height - a . height ;
}
} ) ;
_pi . resolutions = arr ;
} catch ( e ) {
debugPrint ( " Failed to parse resolutions: $ e " ) ;
}
}
2023-05-18 16:17:51 +08:00
Display evtToDisplay ( Map < String , dynamic > evt ) {
var d = Display ( ) ;
d . x = evt [ ' x ' ] ? . toDouble ( ) ? ? d . x ;
d . y = evt [ ' y ' ] ? . toDouble ( ) ? ? d . y ;
d . width = evt [ ' width ' ] ? ? d . width ;
d . height = evt [ ' height ' ] ? ? d . height ;
d . cursorEmbedded = evt [ ' cursor_embedded ' ] = = 1 ;
2023-05-18 21:25:48 +08:00
d . originalWidth = evt [ ' original_width ' ] ? ? kInvalidResolutionValue ;
d . originalHeight = evt [ ' original_height ' ] ? ? kInvalidResolutionValue ;
2023-05-18 16:17:51 +08:00
return d ;
}
2023-10-03 22:15:58 +08:00
updateLastCursorId ( Map < String , dynamic > evt ) {
parent . target ? . cursorModel . id = int . parse ( evt [ ' id ' ] ) ;
}
handleCursorId ( Map < String , dynamic > evt ) {
2023-08-14 18:28:31 +08:00
cachedPeerData . lastCursorId = evt ;
2023-10-03 22:15:58 +08:00
parent . target ? . cursorModel . updateCursorId ( evt ) ;
2023-08-14 18:28:31 +08:00
}
handleCursorData ( Map < String , dynamic > evt ) async {
cachedPeerData . cursorDataList . add ( evt ) ;
await parent . target ? . cursorModel . updateCursorData ( evt ) ;
}
2023-02-17 13:32:17 +08:00
/// Handle the peer info synchronization event based on [evt].
2023-10-08 21:44:54 +08:00
handleSyncPeerInfo (
Map < String , dynamic > evt , SessionID sessionId , String peerId ) async {
2023-02-17 13:32:17 +08:00
if ( evt [ ' displays ' ] ! = null ) {
2023-08-14 18:28:31 +08:00
cachedPeerData . peerInfo [ ' displays ' ] = evt [ ' displays ' ] ;
2023-02-17 13:32:17 +08:00
List < dynamic > displays = json . decode ( evt [ ' displays ' ] ) ;
List < Display > newDisplays = [ ] ;
for ( int i = 0 ; i < displays . length ; + + i ) {
2023-05-18 16:17:51 +08:00
newDisplays . add ( evtToDisplay ( displays [ i ] ) ) ;
2023-02-17 13:32:17 +08:00
}
_pi . displays = newDisplays ;
2023-10-08 21:44:54 +08:00
_pi . displaysCount . value = _pi . displays . length ;
2023-10-18 09:59:02 +08:00
2023-10-08 21:44:54 +08:00
if ( _pi . currentDisplay = = kAllDisplayValue ) {
updateCurDisplay ( sessionId ) ;
// to-do: What if the displays are changed?
} else {
if ( _pi . currentDisplay > = 0 & &
_pi . currentDisplay < _pi . displays . length ) {
updateCurDisplay ( sessionId ) ;
} else {
if ( _pi . displays . isNotEmpty ) {
// Notify to switch display
msgBox ( sessionId , ' custom-nook-nocancel-hasclose-info ' , ' Prompt ' ,
' display_is_plugged_out_msg ' , ' ' , parent . target ! . dialogManager ) ;
final newDisplay = pi . primaryDisplay = = kInvalidDisplayIndex
? 0
: pi . primaryDisplay ;
final displays = newDisplay ;
bind . sessionSwitchDisplay (
sessionId: sessionId , value: Int32List . fromList ( [ displays ] ) ) ;
if ( _pi . isSupportMultiUiSession ) {
// If the peer supports multi-ui-session, no switch display message will be send back.
// We need to update the display manually.
switchToNewDisplay ( newDisplay , sessionId , peerId ) ;
}
} else {
msgBox ( sessionId , ' nocancel-error ' , ' Prompt ' , ' No Displays ' , ' ' ,
parent . target ! . dialogManager ) ;
}
}
2023-02-09 15:53:51 +08:00
}
2023-02-17 13:32:17 +08:00
}
notifyListeners ( ) ;
}
2023-10-08 21:44:54 +08:00
// Directly switch to the new display without waiting for the response.
switchToNewDisplay ( int display , SessionID sessionId , String peerId ) {
2023-10-18 22:39:28 +08:00
// VideoHandler creation is upon when video frames are received, so either caching commands(don't know next width/height) or stopping recording when switching displays.
parent . target ? . recordingModel . onClose ( ) ;
2023-10-08 21:44:54 +08:00
// no need to wait for the response
pi . currentDisplay = display ;
updateCurDisplay ( sessionId ) ;
try {
CurrentDisplayState . find ( peerId ) . value = display ;
} catch ( e ) {
//
}
}
2022-08-26 23:28:08 +08:00
updateBlockInputState ( Map < String , dynamic > evt , String peerId ) {
2022-08-08 22:00:01 +08:00
_inputBlocked = evt [ ' input_state ' ] = = ' on ' ;
notifyListeners ( ) ;
2022-08-26 23:28:08 +08:00
try {
BlockInputState . find ( peerId ) . value = evt [ ' input_state ' ] = = ' on ' ;
} catch ( e ) {
//
}
2022-08-08 22:00:01 +08:00
}
2023-06-06 07:39:44 +08:00
updatePrivacyMode (
Map < String , dynamic > evt , SessionID sessionId , String peerId ) {
2022-08-08 22:00:01 +08:00
notifyListeners ( ) ;
2022-08-26 23:28:08 +08:00
try {
2023-06-06 07:39:44 +08:00
PrivacyModeState . find ( peerId ) . value = bind . sessionGetToggleOptionSync (
sessionId: sessionId , arg: ' privacy-mode ' ) ;
2022-08-26 23:28:08 +08:00
} catch ( e ) {
//
}
2022-08-08 22:00:01 +08:00
}
2023-03-16 09:37:35 +08:00
void setViewOnly ( String id , bool value ) {
2023-10-08 21:44:54 +08:00
if ( versionCmp ( _pi . version , ' 1.2.0 ' ) < 0 ) return ;
2023-03-23 22:52:58 +08:00
// tmp fix for https://github.com/rustdesk/rustdesk/pull/3706#issuecomment-1481242389
// because below rx not used in mobile version, so not initialized, below code will cause crash
// current our flutter code quality is fucking shit now. !!!!!!!!!!!!!!!!
try {
if ( value ) {
ShowRemoteCursorState . find ( id ) . value = value ;
} else {
2023-06-06 07:39:44 +08:00
ShowRemoteCursorState . find ( id ) . value = bind . sessionGetToggleOptionSync (
sessionId: sessionId , arg: ' show-remote-cursor ' ) ;
2023-03-23 22:52:58 +08:00
}
} catch ( e ) {
//
2023-03-16 09:37:35 +08:00
}
if ( _viewOnly ! = value ) {
_viewOnly = value ;
notifyListeners ( ) ;
}
}
2020-11-19 00:32:46 +08:00
}
class ImageModel with ChangeNotifier {
2022-02-17 15:22:14 +08:00
ui . Image ? _image ;
2020-11-19 00:32:46 +08:00
2022-02-17 15:22:14 +08:00
ui . Image ? get image = > _image ;
2020-11-19 00:32:46 +08:00
2022-09-13 21:52:22 +08:00
String id = ' ' ;
2022-05-31 22:09:36 +08:00
2023-06-06 07:39:44 +08:00
late final SessionID sessionId ;
2022-06-13 21:07:26 +08:00
WeakReference < FFI > parent ;
2023-02-21 21:56:46 +08:00
final List < Function ( String ) > callbacksOnFirstImage = [ ] ;
2022-12-27 16:45:13 +08:00
2023-06-06 07:39:44 +08:00
ImageModel ( this . parent ) {
sessionId = parent . target ! . sessionId ;
}
2022-06-13 21:07:26 +08:00
2023-02-21 21:56:46 +08:00
addCallbackOnFirstImage ( Function ( String ) cb ) = > callbacksOnFirstImage . add ( cb ) ;
2022-12-27 16:45:13 +08:00
2023-10-08 21:44:54 +08:00
onRgba ( int display , Uint8List rgba ) {
2022-06-13 21:07:26 +08:00
final pid = parent . target ? . id ;
2023-02-15 16:44:40 +08:00
img . decodeImageFromPixels (
2022-05-31 22:09:36 +08:00
rgba ,
2023-10-08 21:44:54 +08:00
parent . target ? . ffiModel . rect ? . width . toInt ( ) ? ? 0 ,
parent . target ? . ffiModel . rect ? . height . toInt ( ) ? ? 0 ,
2023-02-15 16:44:40 +08:00
isWeb ? ui . PixelFormat . rgba8888 : ui . PixelFormat . bgra8888 ,
onPixelsCopied: ( ) {
// Unlock the rgba memory from rust codes.
2023-10-08 21:44:54 +08:00
platformFFI . nextRgba ( sessionId , display ) ;
2023-02-15 16:44:40 +08:00
} ) . then ( ( image ) {
2022-06-13 21:07:26 +08:00
if ( parent . target ? . id ! = pid ) return ;
2022-05-31 22:09:36 +08:00
try {
// my throw exception, because the listener maybe already dispose
2022-10-05 00:22:40 +08:00
update ( image ) ;
2022-05-31 22:09:36 +08:00
} catch ( e ) {
2022-09-07 22:06:05 +08:00
debugPrint ( ' update image: $ e ' ) ;
2022-05-19 23:45:44 +08:00
}
} ) ;
}
2022-10-05 00:22:40 +08:00
update ( ui . Image ? image ) async {
2020-11-24 22:03:04 +08:00
if ( _image = = null & & image ! = null ) {
2022-08-13 15:08:17 +08:00
if ( isWebDesktop | | isDesktop ) {
2022-09-12 10:52:38 +08:00
await parent . target ? . canvasModel . updateViewStyle ( ) ;
await parent . target ? . canvasModel . updateScrollStyle ( ) ;
2022-02-03 00:53:59 +08:00
} else {
2022-03-07 22:54:34 +08:00
final size = MediaQueryData . fromWindow ( ui . window ) . size ;
2022-08-03 15:31:19 +08:00
final canvasWidth = size . width ;
2022-10-05 00:22:40 +08:00
final canvasHeight = size . height ;
2022-08-03 15:31:19 +08:00
final xscale = canvasWidth / image . width ;
final yscale = canvasHeight / image . height ;
parent . target ? . canvasModel . scale = min ( xscale , yscale ) ;
2022-06-13 21:07:26 +08:00
}
if ( parent . target ! = null ) {
2022-09-12 10:52:38 +08:00
await initializeCursorAndCanvas ( parent . target ! ) ;
}
if ( parent . target ? . ffiModel . isPeerAndroid ? ? false ) {
2023-06-06 07:39:44 +08:00
bind . sessionSetViewStyle ( sessionId: sessionId , value: ' adaptive ' ) ;
2022-09-12 10:52:38 +08:00
parent . target ? . canvasModel . updateViewStyle ( ) ;
2022-02-03 00:53:59 +08:00
}
2020-11-24 22:03:04 +08:00
}
2020-11-19 00:32:46 +08:00
_image = image ;
2020-11-19 18:22:06 +08:00
if ( image ! = null ) notifyListeners ( ) ;
2020-11-19 00:32:46 +08:00
}
2020-11-24 12:11:55 +08:00
2022-08-03 15:31:19 +08:00
// mobile only
// for desktop, height should minus tabbar height
2020-11-24 12:11:55 +08:00
double get maxScale {
2022-07-02 21:24:49 +08:00
if ( _image = = null ) return 1.5 ;
2022-03-07 22:54:34 +08:00
final size = MediaQueryData . fromWindow ( ui . window ) . size ;
2022-02-17 15:22:14 +08:00
final xscale = size . width / _image ! . width ;
final yscale = size . height / _image ! . height ;
2022-07-02 21:24:49 +08:00
return max ( 1.5 , max ( xscale , yscale ) ) ;
2020-11-24 12:11:55 +08:00
}
2022-08-03 15:31:19 +08:00
// mobile only
// for desktop, height should minus tabbar height
2020-11-24 12:11:55 +08:00
double get minScale {
2022-07-02 21:24:49 +08:00
if ( _image = = null ) return 1.5 ;
2022-03-07 22:54:34 +08:00
final size = MediaQueryData . fromWindow ( ui . window ) . size ;
2022-02-17 15:22:14 +08:00
final xscale = size . width / _image ! . width ;
final yscale = size . height / _image ! . height ;
2022-07-02 21:24:49 +08:00
return min ( xscale , yscale ) / 1.5 ;
2020-11-24 12:11:55 +08:00
}
2020-11-19 00:32:46 +08:00
}
2022-08-11 00:12:47 +08:00
enum ScrollStyle {
scrollbar ,
2022-08-12 20:14:53 +08:00
scrollauto ,
2022-08-11 00:12:47 +08:00
}
2022-09-13 21:59:06 +08:00
class ViewStyle {
final String style ;
final double width ;
final double height ;
final int displayWidth ;
final int displayHeight ;
ViewStyle ( {
2023-02-27 14:24:15 +08:00
required this . style ,
required this . width ,
required this . height ,
required this . displayWidth ,
required this . displayHeight ,
2022-09-13 21:59:06 +08:00
} ) ;
2023-02-27 14:24:15 +08:00
static defaultViewStyle ( ) {
final desktop = ( isDesktop | | isWebDesktop ) ;
final w =
desktop ? kDesktopDefaultDisplayWidth : kMobileDefaultDisplayWidth ;
final h =
desktop ? kDesktopDefaultDisplayHeight : kMobileDefaultDisplayHeight ;
return ViewStyle (
style: ' ' ,
width: w . toDouble ( ) ,
height: h . toDouble ( ) ,
displayWidth: w ,
displayHeight: h ,
) ;
}
2022-09-13 21:59:06 +08:00
static int _double2Int ( double v ) = > ( v * 100 ) . round ( ) . toInt ( ) ;
@ override
bool operator = = ( Object other ) = >
other is ViewStyle & &
other . runtimeType = = runtimeType & &
_innerEqual ( other ) ;
bool _innerEqual ( ViewStyle other ) {
return style = = other . style & &
ViewStyle . _double2Int ( other . width ) = = ViewStyle . _double2Int ( width ) & &
ViewStyle . _double2Int ( other . height ) = = ViewStyle . _double2Int ( height ) & &
other . displayWidth = = displayWidth & &
other . displayHeight = = displayHeight ;
}
@ override
int get hashCode = > Object . hash (
style ,
ViewStyle . _double2Int ( width ) ,
ViewStyle . _double2Int ( height ) ,
displayWidth ,
displayHeight ,
) . hashCode ;
double get scale {
double s = 1.0 ;
2022-12-30 16:14:30 +08:00
if ( style = = kRemoteViewStyleAdaptive ) {
2023-02-27 14:24:15 +08:00
if ( width ! = 0 & &
height ! = 0 & &
displayWidth ! = 0 & &
displayHeight ! = 0 ) {
final s1 = width / displayWidth ;
final s2 = height / displayHeight ;
s = s1 < s2 ? s1 : s2 ;
}
2022-09-13 21:59:06 +08:00
}
return s ;
}
}
2020-11-23 23:18:42 +08:00
class CanvasModel with ChangeNotifier {
2022-09-03 10:39:33 +08:00
// image offset of canvas
double _x = 0 ;
// image offset of canvas
double _y = 0 ;
// image scale
double _scale = 1.0 ;
2023-02-02 14:39:58 +08:00
double _devicePixelRatio = 1.0 ;
2023-01-06 18:14:31 +08:00
Size _size = Size . zero ;
2022-09-03 10:39:33 +08:00
// the tabbar over the image
2022-11-01 17:01:43 +08:00
// double tabBarHeight = 0.0;
2022-10-04 21:19:31 +08:00
// the window border's width
2022-11-01 17:01:43 +08:00
// double windowBorderWidth = 0.0;
2022-09-13 21:52:22 +08:00
// remote id
2022-09-07 12:20:53 +08:00
String id = ' ' ;
2023-06-06 07:39:44 +08:00
late final SessionID sessionId ;
2022-08-12 20:14:53 +08:00
// scroll offset x percent
double _scrollX = 0.0 ;
// scroll offset y percent
double _scrollY = 0.0 ;
2022-08-13 15:08:17 +08:00
ScrollStyle _scrollStyle = ScrollStyle . scrollauto ;
2023-02-27 14:24:15 +08:00
ViewStyle _lastViewStyle = ViewStyle . defaultViewStyle ( ) ;
2020-11-23 23:18:42 +08:00
2023-01-06 20:25:18 +08:00
final _imageOverflow = false . obs ;
2022-06-13 21:07:26 +08:00
WeakReference < FFI > parent ;
2023-06-06 07:39:44 +08:00
CanvasModel ( this . parent ) {
sessionId = parent . target ! . sessionId ;
}
2020-11-23 23:18:42 +08:00
double get x = > _x ;
double get y = > _y ;
double get scale = > _scale ;
2023-02-02 14:39:58 +08:00
double get devicePixelRatio = > _devicePixelRatio ;
2023-01-06 18:14:31 +08:00
Size get size = > _size ;
2022-08-11 00:12:47 +08:00
ScrollStyle get scrollStyle = > _scrollStyle ;
2022-12-30 16:14:30 +08:00
ViewStyle get viewStyle = > _lastViewStyle ;
2023-01-06 20:25:18 +08:00
RxBool get imageOverflow = > _imageOverflow ;
2022-12-30 16:14:30 +08:00
_resetScroll ( ) = > setScrollPercent ( 0.0 , 0.0 ) ;
2020-11-23 23:18:42 +08:00
2022-08-12 20:14:53 +08:00
setScrollPercent ( double x , double y ) {
_scrollX = x ;
_scrollY = y ;
}
double get scrollX = > _scrollX ;
double get scrollY = > _scrollY ;
2023-02-28 15:30:46 +08:00
static double get leftToEdge = > ( isDesktop | | isWebDesktop )
? windowBorderWidth + kDragToResizeAreaPadding . left
: 0 ;
static double get rightToEdge = > ( isDesktop | | isWebDesktop )
? windowBorderWidth + kDragToResizeAreaPadding . right
: 0 ;
static double get topToEdge = > ( isDesktop | | isWebDesktop )
? tabBarHeight + windowBorderWidth + kDragToResizeAreaPadding . top
: 0 ;
static double get bottomToEdge = > ( isDesktop | | isWebDesktop )
? windowBorderWidth + kDragToResizeAreaPadding . bottom
: 0 ;
2023-02-28 14:50:51 +08:00
2022-09-12 10:52:38 +08:00
updateViewStyle ( ) async {
2023-01-06 18:14:31 +08:00
Size getSize ( ) {
final size = MediaQueryData . fromWindow ( ui . window ) . size ;
// If minimized, w or h may be negative here.
2023-02-28 14:50:51 +08:00
double w = size . width - leftToEdge - rightToEdge ;
double h = size . height - topToEdge - bottomToEdge ;
2023-01-06 18:14:31 +08:00
return Size ( w < 0 ? 0 : w , h < 0 ? 0 : h ) ;
}
2023-06-06 07:39:44 +08:00
final style = await bind . sessionGetViewStyle ( sessionId: sessionId ) ;
2022-09-13 21:59:06 +08:00
if ( style = = null ) {
2022-05-31 22:09:36 +08:00
return ;
}
2023-01-06 18:14:31 +08:00
_size = getSize ( ) ;
2022-09-13 21:59:06 +08:00
final displayWidth = getDisplayWidth ( ) ;
final displayHeight = getDisplayHeight ( ) ;
final viewStyle = ViewStyle (
style: style ,
2023-01-06 18:14:31 +08:00
width: size . width ,
height: size . height ,
2022-09-13 21:59:06 +08:00
displayWidth: displayWidth ,
displayHeight: displayHeight ,
) ;
if ( _lastViewStyle = = viewStyle ) {
return ;
2022-02-03 00:53:59 +08:00
}
2022-12-30 16:14:30 +08:00
if ( _lastViewStyle . style ! = viewStyle . style ) {
_resetScroll ( ) ;
}
2022-09-13 21:59:06 +08:00
_lastViewStyle = viewStyle ;
_scale = viewStyle . scale ;
2023-01-06 18:14:31 +08:00
2023-02-02 14:39:58 +08:00
_devicePixelRatio = ui . window . devicePixelRatio ;
2023-01-06 18:14:31 +08:00
if ( kIgnoreDpi & & style = = kRemoteViewStyleOriginal ) {
2023-02-02 14:39:58 +08:00
_scale = 1.0 / _devicePixelRatio ;
2023-01-06 18:14:31 +08:00
}
_x = ( size . width - displayWidth * _scale ) / 2 ;
_y = ( size . height - displayHeight * _scale ) / 2 ;
2023-01-06 20:25:18 +08:00
_imageOverflow . value = _x < 0 | | y < 0 ;
2022-02-03 00:53:59 +08:00
notifyListeners ( ) ;
2023-02-03 17:02:28 +08:00
parent . target ? . inputModel . refreshMousePos ( ) ;
2022-02-03 00:53:59 +08:00
}
2022-08-12 20:14:53 +08:00
updateScrollStyle ( ) async {
2023-06-06 07:39:44 +08:00
final style = await bind . sessionGetScrollStyle ( sessionId: sessionId ) ;
2022-11-24 11:19:16 +08:00
if ( style = = kRemoteScrollStyleBar ) {
2022-08-11 00:12:47 +08:00
_scrollStyle = ScrollStyle . scrollbar ;
2022-12-30 16:14:30 +08:00
_resetScroll ( ) ;
2022-08-13 15:08:17 +08:00
} else {
_scrollStyle = ScrollStyle . scrollauto ;
2022-08-11 00:12:47 +08:00
}
2022-08-13 15:08:17 +08:00
notifyListeners ( ) ;
2022-08-11 00:12:47 +08:00
}
2022-09-12 10:52:38 +08:00
update ( double x , double y , double scale ) {
2020-12-21 18:28:28 +08:00
_x = x ;
_y = y ;
_scale = scale ;
notifyListeners ( ) ;
}
2022-12-31 21:41:16 +08:00
bool get cursorEmbedded = >
2023-10-08 21:44:54 +08:00
parent . target ? . ffiModel . _pi . cursorEmbedded ? ? false ;
2022-11-29 16:36:35 +08:00
2022-06-13 21:07:26 +08:00
int getDisplayWidth ( ) {
2022-08-31 18:41:55 +08:00
final defaultWidth = ( isDesktop | | isWebDesktop )
? kDesktopDefaultDisplayWidth
: kMobileDefaultDisplayWidth ;
2023-10-08 21:44:54 +08:00
return parent . target ? . ffiModel . rect ? . width . toInt ( ) ? ? defaultWidth ;
2022-06-13 21:07:26 +08:00
}
int getDisplayHeight ( ) {
2022-08-31 18:41:55 +08:00
final defaultHeight = ( isDesktop | | isWebDesktop )
? kDesktopDefaultDisplayHeight
: kMobileDefaultDisplayHeight ;
2023-10-08 21:44:54 +08:00
return parent . target ? . ffiModel . rect ? . height . toInt ( ) ? ? defaultHeight ;
2022-06-13 21:07:26 +08:00
}
2023-02-28 14:50:51 +08:00
static double get windowBorderWidth = > stateGlobal . windowBorderWidth . value ;
static double get tabBarHeight = > stateGlobal . tabBarHeight ;
2022-11-01 17:01:43 +08:00
2022-09-12 16:35:56 +08:00
moveDesktopMouse ( double x , double y ) {
2023-02-27 22:24:00 +08:00
if ( size . width = = 0 | | size . height = = 0 ) {
return ;
}
2022-06-27 16:50:02 +08:00
// On mobile platforms, move the canvas with the cursor.
2022-08-12 20:14:53 +08:00
final dw = getDisplayWidth ( ) * _scale ;
final dh = getDisplayHeight ( ) * _scale ;
var dxOffset = 0 ;
var dyOffset = 0 ;
2023-02-25 22:47:22 +08:00
try {
if ( dw > size . width ) {
dxOffset = ( x - dw * ( x / size . width ) - _x ) . toInt ( ) ;
}
if ( dh > size . height ) {
dyOffset = ( y - dh * ( y / size . height ) - _y ) . toInt ( ) ;
}
} catch ( e ) {
2023-02-26 23:29:36 +08:00
debugPrintStack (
label:
' (x,y) ( $ x , $ y ), (_x,_y) ( $ _x , $ _y ), _scale $ _scale , display size ( ${ getDisplayWidth ( ) } , ${ getDisplayHeight ( ) } ), size $ size , , $ e ' ) ;
2023-02-25 22:47:22 +08:00
return ;
2022-08-12 20:14:53 +08:00
}
2023-02-25 22:47:22 +08:00
2022-08-12 20:14:53 +08:00
_x + = dxOffset ;
_y + = dyOffset ;
if ( dxOffset ! = 0 | | dyOffset ! = 0 ) {
notifyListeners ( ) ;
}
2022-09-03 10:39:33 +08:00
// If keyboard is not permitted, do not move cursor when mouse is moving.
2023-03-28 10:52:43 +08:00
if ( parent . target ! = null & & parent . target ! . ffiModel . keyboard ) {
2022-09-07 12:20:53 +08:00
// Draw cursor if is not desktop.
if ( ! isDesktop ) {
2022-09-03 10:39:33 +08:00
parent . target ! . cursorModel . moveLocal ( x , y ) ;
2022-09-07 12:20:53 +08:00
} else {
try {
RemoteCursorMovedState . find ( id ) . value = false ;
} catch ( e ) {
//
}
2022-09-03 10:39:33 +08:00
}
}
2022-02-06 16:29:56 +08:00
}
2020-11-24 12:11:55 +08:00
set scale ( v ) {
_scale = v ;
notifyListeners ( ) ;
}
2022-09-12 16:35:56 +08:00
panX ( double dx ) {
2020-11-24 22:03:04 +08:00
_x + = dx ;
notifyListeners ( ) ;
2020-11-23 23:18:42 +08:00
}
2022-09-12 16:35:56 +08:00
resetOffset ( ) {
2022-05-23 16:02:37 +08:00
if ( isWebDesktop ) {
2022-02-03 00:53:59 +08:00
updateViewStyle ( ) ;
} else {
2022-09-03 10:39:33 +08:00
_x = ( size . width - getDisplayWidth ( ) * _scale ) / 2 ;
_y = ( size . height - getDisplayHeight ( ) * _scale ) / 2 ;
2022-02-03 00:53:59 +08:00
}
2020-11-27 17:59:42 +08:00
notifyListeners ( ) ;
}
2022-09-12 16:35:56 +08:00
panY ( double dy ) {
2020-11-23 23:18:42 +08:00
_y + = dy ;
notifyListeners ( ) ;
}
2022-09-12 16:35:56 +08:00
updateScale ( double v ) {
2022-06-13 21:07:26 +08:00
if ( parent . target ? . imageModel . image = = null ) return ;
2022-09-03 10:39:33 +08:00
final offset = parent . target ? . cursorModel . offset ? ? const Offset ( 0 , 0 ) ;
2022-06-13 21:07:26 +08:00
var r = parent . target ? . cursorModel . getVisibleRect ( ) ? ? Rect . zero ;
2020-11-25 14:41:57 +08:00
final px0 = ( offset . dx - r . left ) * _scale ;
final py0 = ( offset . dy - r . top ) * _scale ;
2020-11-23 23:18:42 +08:00
_scale * = v ;
2022-06-13 21:07:26 +08:00
final maxs = parent . target ? . imageModel . maxScale ? ? 1 ;
final mins = parent . target ? . imageModel . minScale ? ? 1 ;
2020-11-24 12:11:55 +08:00
if ( _scale > maxs ) _scale = maxs ;
if ( _scale < mins ) _scale = mins ;
2022-06-13 21:07:26 +08:00
r = parent . target ? . cursorModel . getVisibleRect ( ) ? ? Rect . zero ;
2020-11-25 14:41:57 +08:00
final px1 = ( offset . dx - r . left ) * _scale ;
final py1 = ( offset . dy - r . top ) * _scale ;
_x - = px1 - px0 ;
_y - = py1 - py0 ;
2020-11-23 23:18:42 +08:00
notifyListeners ( ) ;
}
2022-09-12 16:35:56 +08:00
clear ( [ bool notify = false ] ) {
2020-11-23 23:18:42 +08:00
_x = 0 ;
_y = 0 ;
_scale = 1.0 ;
2021-08-22 07:50:12 +08:00
if ( notify ) notifyListeners ( ) ;
2020-11-23 23:18:42 +08:00
}
}
2022-09-07 22:06:05 +08:00
// data for cursor
class CursorData {
final String peerId ;
final int id ;
2023-02-01 22:12:28 +08:00
final img2 . Image image ;
2022-10-27 18:40:45 +08:00
double scale ;
Uint8List ? data ;
2022-11-15 22:35:10 +08:00
final double hotxOrigin ;
final double hotyOrigin ;
2022-10-30 13:50:44 +08:00
double hotx ;
double hoty ;
2022-09-07 22:06:05 +08:00
final int width ;
final int height ;
CursorData ( {
required this . peerId ,
required this . id ,
2022-10-27 18:40:45 +08:00
required this . image ,
required this . scale ,
2022-09-07 22:06:05 +08:00
required this . data ,
2022-11-15 22:35:10 +08:00
required this . hotxOrigin ,
required this . hotyOrigin ,
2022-09-07 22:06:05 +08:00
required this . width ,
required this . height ,
2022-11-15 22:35:10 +08:00
} ) : hotx = hotxOrigin * scale ,
hoty = hotxOrigin * scale ;
2022-09-08 10:52:30 +08:00
int _doubleToInt ( double v ) = > ( v * 10e6 ) . round ( ) . toInt ( ) ;
2023-02-02 14:39:58 +08:00
double _checkUpdateScale ( double scale ) {
2022-11-16 18:07:58 +08:00
double oldScale = this . scale ;
2023-02-02 14:39:58 +08:00
if ( scale ! = 1.0 ) {
2022-11-16 18:07:58 +08:00
// Update data if scale changed.
2023-02-01 20:58:21 +08:00
final tgtWidth = ( width * scale ) . toInt ( ) ;
final tgtHeight = ( width * scale ) . toInt ( ) ;
if ( tgtWidth < kMinCursorSize | | tgtHeight < kMinCursorSize ) {
double sw = kMinCursorSize . toDouble ( ) / width ;
double sh = kMinCursorSize . toDouble ( ) / height ;
scale = sw < sh ? sh : sw ;
2022-10-27 18:40:45 +08:00
}
2022-11-16 18:07:58 +08:00
}
2023-02-01 20:58:21 +08:00
if ( _doubleToInt ( oldScale ) ! = _doubleToInt ( scale ) ) {
2023-02-01 22:12:28 +08:00
if ( Platform . isWindows ) {
2022-10-27 18:40:45 +08:00
data = img2
. copyResize (
2023-02-01 22:12:28 +08:00
image ,
2022-10-27 18:40:45 +08:00
width: ( width * scale ) . toInt ( ) ,
height: ( height * scale ) . toInt ( ) ,
2022-11-16 18:07:58 +08:00
interpolation: img2 . Interpolation . average ,
2022-10-27 18:40:45 +08:00
)
2023-05-11 14:26:59 +08:00
. getBytes ( order: img2 . ChannelOrder . bgra ) ;
2023-02-01 22:12:28 +08:00
} else {
data = Uint8List . fromList (
img2 . encodePng (
img2 . copyResize (
image ,
width: ( width * scale ) . toInt ( ) ,
height: ( height * scale ) . toInt ( ) ,
interpolation: img2 . Interpolation . average ,
) ,
) ,
) ;
2022-10-27 18:40:45 +08:00
}
}
2022-11-16 18:07:58 +08:00
2022-10-27 18:40:45 +08:00
this . scale = scale ;
2022-11-15 22:35:10 +08:00
hotx = hotxOrigin * scale ;
hoty = hotyOrigin * scale ;
2022-10-27 18:40:45 +08:00
return scale ;
}
2023-02-02 14:39:58 +08:00
String updateGetKey ( double scale ) {
scale = _checkUpdateScale ( scale ) ;
2022-10-27 18:40:45 +08:00
return ' ${ peerId } _ ${ id } _ ${ _doubleToInt ( width * scale ) } _ ${ _doubleToInt ( height * scale ) } ' ;
}
2022-09-07 22:06:05 +08:00
}
2022-11-22 21:34:53 +08:00
const _forbiddenCursorPng =
' iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IB2cksfwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAkZQTFRFAAAA2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4GWAwCAAAAAAAA2B4GAAAAMTExAAAAAAAA2B4G2B4G2B4GAAAAmZmZkZGRAQEBAAAA2B4G2B4G2B4G////oKCgAwMDag8D2B4G2B4G2B4Gra2tBgYGbg8D2B4G2B4Gubm5CQkJTwsCVgwC2B4GxcXFDg4OAAAAAAAA2B4G2B4Gz8/PFBQUAAAAAAAA2B4G2B4G2B4G2B4G2B4G2B4G2B4GDgIA2NjYGxsbAAAAAAAA2B4GFwMB4eHhIyMjAAAAAAAA2B4G6OjoLCwsAAAAAAAA2B4G2B4G2B4G2B4G2B4GCQEA4ODgv7+/iYmJY2NjAgICAAAA9PT0Ojo6AAAAAAAAAAAA+/v7SkpKhYWFr6+vAAAAAAAA8/PzOTk5ERER9fX1KCgoAAAAgYGBKioqAAAAAAAApqamlpaWAAAAAAAAAAAAAAAAAAAAAAAALi4u/v7+GRkZAAAAAAAAAAAAAAAAAAAAfn5+AAAAAAAAV1dXkJCQAAAAAAAAAQEBAAAAAAAAAAAA7Hz6BAAAAMJ0Uk5TAAIWEwEynNz6//fVkCAatP2fDUHs6cDD8d0mPfT5fiEskiIR584A0gejr3AZ+P4plfALf5ZiTL85a4ziD6697fzN3UYE4v/4TwrNHuT///tdRKZh///+1U/ZBv///yjb///eAVL//50Cocv//6oFBbPvpGZCbfT//7cIhv///8INM///zBEcWYSZmO7//////1P////ts/////8vBv//////gv//R/z///QQz9sevP///2waXhNO/+fc//8mev/5gAe2r90MAAAByUlEQVR4nGNggANGJmYWBpyAlY2dg5OTi5uHF6s0H78AJxRwCAphyguLgKRExcQlQLSkFLq8tAwnp6ycPNABjAqKQKNElVDllVU4OVVhVquJA81Q10BRoAkUUYbJa4Edoo0sr6PLqaePLG/AyWlohKTAmJPTBFnelAFoixmSAnNOTgsUeQZLTk4rJAXWnJw2EHlbiDyDPCenHZICe04HFrh+RydnBgYWPU5uJAWinJwucPNd3dw9GDw5Ob2QFHBzcnrD7ffx9fMPCOTkDEINhmC4+3x8Q0LDwlEDIoKTMzIKKg9SEBIdE8sZh6SAJZ6Tkx0qD1YQkpCYlIwclCng0AXLQxSEpKalZyCryATKZwkhKQjJzsnNQ1KQXwBUUVhUXBJYWgZREFJeUVmFpMKlWg+anmqgCkJq6+obkG1pLEBTENLU3NKKrIKhrb2js8u4G6Kgpze0r3/CRAZMAHbkpJDJU6ZMmTqtFbuC6TNmhsyaMnsOFlmwgrnzpsxfELJwEXZ5Bp/FS3yWLlsesmLlKuwKVk9Ys5Zh3foN0zduwq5g85atDAzbpqSGbN9RhV0FGOzctWH3lD14FOzdt3H/gQw8Cg4u2gQPAwBYDXXdIH+wqAAAAABJRU5ErkJggg== ' ;
const _defaultCursorPng =
' iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARzQklUCAgICHwIZIgAAAFmSURBVFiF7dWxSlxREMbx34QFDRowYBchZSxSCWlMCOwD5FGEFHap06UI7KPsAyyEEIQFqxRaCqYTsqCJFsKkuAeRXb17wrqV918dztw55zszc2fo6Oh47MR/e3zO1/iAHWmznHKGQwx9ip/LEbCfazbsoY8j/JLOhcC6sCW9wsjEwJf483AC9nPNc1+lFRwI13d+l3rYFS799rFGxJMqARv2pBXh+72XQ7gWvklPS7TmMl9Ak/M+DqrENvxAv/guKKApuKPWl0/TROK4+LbSqzhuB+OZ3fRSeFPWY+Fkyn56Y29hfgTSpnQ+s98cvorVey66uPlNFxKwZOYLCGfCs5n9NMYVrsp6mvXSoFqpqYFDvMBkStgJJe93dZOwVXxbqUnBENulydSReqUrDhcX0PT2EXarBYS3GNXMhboinBgIl9K71kg0L3+PvyYGdVpruT2MwrF0iotiXfIwus0Dj+OOjo6Of+e7ab74RkpgAAAAAElFTkSuQmCC ' ;
final preForbiddenCursor = PredefinedCursor (
png: _forbiddenCursorPng ,
id: - 2 ,
) ;
final preDefaultCursor = PredefinedCursor (
png: _defaultCursorPng ,
id: - 1 ,
hotxGetter: ( double w ) = > w / 2 ,
hotyGetter: ( double h ) = > h / 2 ,
) ;
class PredefinedCursor {
ui . Image ? _image ;
img2 . Image ? _image2 ;
CursorData ? _cache ;
String png ;
int id ;
double Function ( double ) ? hotxGetter ;
double Function ( double ) ? hotyGetter ;
PredefinedCursor (
{ required this . png , required this . id , this . hotxGetter , this . hotyGetter } ) {
init ( ) ;
}
ui . Image ? get image = > _image ;
CursorData ? get cache = > _cache ;
init ( ) {
_image2 = img2 . decodePng ( base64Decode ( png ) ) ;
if ( _image2 ! = null ) {
( ) async {
final defaultImg = _image2 ! ;
// This function is called only one time, no need to care about the performance.
2023-05-11 14:26:59 +08:00
Uint8List data = defaultImg . getBytes ( order: img2 . ChannelOrder . rgba ) ;
2022-11-22 21:34:53 +08:00
_image = await img . decodeImageFromPixels (
data , defaultImg . width , defaultImg . height , ui . PixelFormat . rgba8888 ) ;
double scale = 1.0 ;
if ( Platform . isWindows ) {
2023-05-11 14:26:59 +08:00
data = _image2 ! . getBytes ( order: img2 . ChannelOrder . bgra ) ;
2022-11-22 21:34:53 +08:00
} else {
data = Uint8List . fromList ( img2 . encodePng ( _image2 ! ) ) ;
}
_cache = CursorData (
peerId: ' ' ,
id: id ,
2023-02-01 22:12:28 +08:00
image: _image2 ! . clone ( ) ,
2022-11-22 21:34:53 +08:00
scale: scale ,
data: data ,
hotxOrigin:
hotxGetter ! = null ? hotxGetter ! ( _image2 ! . width . toDouble ( ) ) : 0 ,
hotyOrigin:
hotyGetter ! = null ? hotyGetter ! ( _image2 ! . height . toDouble ( ) ) : 0 ,
width: _image2 ! . width ,
height: _image2 ! . height ,
) ;
} ( ) ;
}
}
}
2020-11-19 00:32:46 +08:00
class CursorModel with ChangeNotifier {
2022-02-17 15:22:14 +08:00
ui . Image ? _image ;
2022-09-07 17:14:52 +08:00
final _images = < int , Tuple3 < ui . Image , double , double > > { } ;
2022-10-30 13:50:44 +08:00
CursorData ? _cache ;
final _cacheMap = < int , CursorData > { } ;
final _cacheKeys = < String > { } ;
2020-11-22 18:29:04 +08:00
double _x = - 10000 ;
double _y = - 10000 ;
2023-10-03 21:16:12 +08:00
int _id = - 1 ;
2020-11-19 00:32:46 +08:00
double _hotx = 0 ;
double _hoty = 0 ;
double _displayOriginX = 0 ;
double _displayOriginY = 0 ;
2023-02-03 20:27:05 +08:00
DateTime ? _firstUpdateMouseTime ;
2022-11-14 15:05:44 +08:00
bool gotMouseControl = true ;
DateTime _lastPeerMouse = DateTime . now ( )
2023-02-03 20:27:05 +08:00
. subtract ( Duration ( milliseconds: 3000 * kMouseControlTimeoutMSec ) ) ;
2023-10-03 21:16:12 +08:00
String peerId = ' ' ;
2022-06-13 21:07:26 +08:00
WeakReference < FFI > parent ;
2020-11-19 00:32:46 +08:00
2022-02-17 15:22:14 +08:00
ui . Image ? get image = > _image ;
2022-10-30 14:38:35 +08:00
CursorData ? get cache = > _cache ;
2022-02-02 17:25:56 +08:00
2020-11-23 23:18:42 +08:00
double get x = > _x - _displayOriginX ;
double get y = > _y - _displayOriginY ;
2022-02-02 17:25:56 +08:00
2020-11-25 14:41:57 +08:00
Offset get offset = > Offset ( _x , _y ) ;
2022-02-02 17:25:56 +08:00
2020-11-23 23:18:42 +08:00
double get hotx = > _hotx ;
double get hoty = > _hoty ;
2020-11-19 00:32:46 +08:00
2023-10-03 22:15:58 +08:00
set id ( int id ) = > _id = id ;
2022-11-14 15:05:44 +08:00
bool get isPeerControlProtected = >
DateTime . now ( ) . difference ( _lastPeerMouse ) . inMilliseconds <
2022-11-08 13:37:08 +08:00
kMouseControlTimeoutMSec ;
2023-02-03 20:27:05 +08:00
bool isConnIn2Secs ( ) {
if ( _firstUpdateMouseTime = = null ) {
_firstUpdateMouseTime = DateTime . now ( ) ;
return true ;
} else {
return DateTime . now ( ) . difference ( _firstUpdateMouseTime ! ) . inSeconds < 2 ;
}
}
2022-11-22 21:34:53 +08:00
CursorModel ( this . parent ) ;
2022-06-13 21:07:26 +08:00
2022-10-30 13:50:44 +08:00
Set < String > get cachedKeys = > _cacheKeys ;
addKey ( String key ) = > _cacheKeys . add ( key ) ;
2022-09-07 22:06:05 +08:00
2020-11-25 14:41:57 +08:00
// remote physical display coordinate
2020-11-24 22:03:04 +08:00
Rect getVisibleRect ( ) {
2022-03-07 22:54:34 +08:00
final size = MediaQueryData . fromWindow ( ui . window ) . size ;
2022-06-13 21:07:26 +08:00
final xoffset = parent . target ? . canvasModel . x ? ? 0 ;
final yoffset = parent . target ? . canvasModel . y ? ? 0 ;
final scale = parent . target ? . canvasModel . scale ? ? 1 ;
2020-11-24 22:03:04 +08:00
final x0 = _displayOriginX - xoffset / scale ;
final y0 = _displayOriginY - yoffset / scale ;
return Rect . fromLTWH ( x0 , y0 , size . width / scale , size . height / scale ) ;
}
2020-11-25 11:20:40 +08:00
double adjustForKeyboard ( ) {
2020-12-21 17:26:23 +08:00
final m = MediaQueryData . fromWindow ( ui . window ) ;
var keyboardHeight = m . viewInsets . bottom ;
final size = m . size ;
2020-11-25 11:20:40 +08:00
if ( keyboardHeight < 100 ) return 0 ;
2022-06-13 21:07:26 +08:00
final s = parent . target ? . canvasModel . scale ? ? 1.0 ;
2020-12-21 17:26:23 +08:00
final thresh = ( size . height - keyboardHeight ) / 2 ;
2020-11-25 14:41:57 +08:00
var h = ( _y - getVisibleRect ( ) . top ) * s ; // local physical display height
2020-11-27 12:05:23 +08:00
return h - thresh ;
2020-11-25 11:20:40 +08:00
}
2022-09-12 16:35:56 +08:00
move ( double x , double y ) {
2022-02-17 18:00:44 +08:00
moveLocal ( x , y ) ;
2022-09-27 20:35:02 +08:00
parent . target ? . inputModel . moveMouse ( _x , _y ) ;
2022-02-17 18:00:44 +08:00
}
2022-09-12 16:35:56 +08:00
moveLocal ( double x , double y ) {
2022-06-13 21:07:26 +08:00
final scale = parent . target ? . canvasModel . scale ? ? 1.0 ;
final xoffset = parent . target ? . canvasModel . x ? ? 0 ;
final yoffset = parent . target ? . canvasModel . y ? ? 0 ;
2021-08-21 17:18:14 +08:00
_x = ( x - xoffset ) / scale + _displayOriginX ;
_y = ( y - yoffset ) / scale + _displayOriginY ;
notifyListeners ( ) ;
}
2022-09-12 16:35:56 +08:00
reset ( ) {
2021-08-21 17:18:14 +08:00
_x = _displayOriginX ;
_y = _displayOriginY ;
2022-09-27 20:35:02 +08:00
parent . target ? . inputModel . moveMouse ( _x , _y ) ;
2022-06-13 21:07:26 +08:00
parent . target ? . canvasModel . clear ( true ) ;
2021-08-21 17:18:14 +08:00
notifyListeners ( ) ;
}
2022-09-12 16:35:56 +08:00
updatePan ( double dx , double dy , bool touchMode ) {
2021-08-21 17:18:14 +08:00
if ( touchMode ) {
2022-06-13 21:07:26 +08:00
final scale = parent . target ? . canvasModel . scale ? ? 1.0 ;
2022-04-19 13:07:45 +08:00
_x + = dx / scale ;
_y + = dy / scale ;
2022-09-27 20:35:02 +08:00
parent . target ? . inputModel . moveMouse ( _x , _y ) ;
2022-04-19 13:07:45 +08:00
notifyListeners ( ) ;
2021-08-21 17:18:14 +08:00
return ;
}
2023-07-17 20:07:55 +08:00
if ( parent . target ? . imageModel . image = = null ) return ;
2022-06-13 21:07:26 +08:00
final scale = parent . target ? . canvasModel . scale ? ? 1.0 ;
2020-11-25 00:13:23 +08:00
dx / = scale ;
dy / = scale ;
2020-11-24 22:03:04 +08:00
final r = getVisibleRect ( ) ;
var cx = r . center . dx ;
var cy = r . center . dy ;
var tryMoveCanvasX = false ;
if ( dx > 0 ) {
2022-05-11 22:34:41 +08:00
final maxCanvasCanMove = _displayOriginX +
2022-06-13 21:07:26 +08:00
( parent . target ? . imageModel . image ! . width ? ? 1280 ) -
2022-05-11 22:34:41 +08:00
r . right . roundToDouble ( ) ;
2020-11-24 22:03:04 +08:00
tryMoveCanvasX = _x + dx > cx & & maxCanvasCanMove > 0 ;
if ( tryMoveCanvasX ) {
dx = min ( dx , maxCanvasCanMove ) ;
} else {
final maxCursorCanMove = r . right - _x ;
dx = min ( dx , maxCursorCanMove ) ;
}
} else if ( dx < 0 ) {
2022-05-11 22:34:41 +08:00
final maxCanvasCanMove = _displayOriginX - r . left . roundToDouble ( ) ;
2020-11-24 22:03:04 +08:00
tryMoveCanvasX = _x + dx < cx & & maxCanvasCanMove < 0 ;
if ( tryMoveCanvasX ) {
dx = max ( dx , maxCanvasCanMove ) ;
} else {
final maxCursorCanMove = r . left - _x ;
dx = max ( dx , maxCursorCanMove ) ;
}
}
var tryMoveCanvasY = false ;
if ( dy > 0 ) {
2022-05-11 22:34:41 +08:00
final mayCanvasCanMove = _displayOriginY +
2022-06-13 21:07:26 +08:00
( parent . target ? . imageModel . image ! . height ? ? 720 ) -
2022-05-11 22:34:41 +08:00
r . bottom . roundToDouble ( ) ;
2020-11-24 22:03:04 +08:00
tryMoveCanvasY = _y + dy > cy & & mayCanvasCanMove > 0 ;
if ( tryMoveCanvasY ) {
dy = min ( dy , mayCanvasCanMove ) ;
} else {
2020-11-24 23:36:46 +08:00
final mayCursorCanMove = r . bottom - _y ;
2020-11-24 22:03:04 +08:00
dy = min ( dy , mayCursorCanMove ) ;
}
} else if ( dy < 0 ) {
2022-05-11 22:34:41 +08:00
final mayCanvasCanMove = _displayOriginY - r . top . roundToDouble ( ) ;
2020-11-24 22:03:04 +08:00
tryMoveCanvasY = _y + dy < cy & & mayCanvasCanMove < 0 ;
if ( tryMoveCanvasY ) {
dy = max ( dy , mayCanvasCanMove ) ;
} else {
2020-11-24 23:36:46 +08:00
final mayCursorCanMove = r . top - _y ;
2020-11-24 22:03:04 +08:00
dy = max ( dy , mayCursorCanMove ) ;
}
}
if ( dx = = 0 & & dy = = 0 ) return ;
_x + = dx ;
_y + = dy ;
if ( tryMoveCanvasX & & dx ! = 0 ) {
2022-06-13 21:07:26 +08:00
parent . target ? . canvasModel . panX ( - dx ) ;
2020-11-24 22:03:04 +08:00
}
if ( tryMoveCanvasY & & dy ! = 0 ) {
2022-06-13 21:07:26 +08:00
parent . target ? . canvasModel . panY ( - dy ) ;
2020-11-24 22:03:04 +08:00
}
2022-09-27 20:35:02 +08:00
parent . target ? . inputModel . moveMouse ( _x , _y ) ;
2020-11-24 22:03:04 +08:00
notifyListeners ( ) ;
}
2022-09-11 10:50:48 +08:00
updateCursorData ( Map < String , dynamic > evt ) async {
2023-10-03 21:16:12 +08:00
final id = int . parse ( evt [ ' id ' ] ) ;
final hotx = double . parse ( evt [ ' hotx ' ] ) ;
final hoty = double . parse ( evt [ ' hoty ' ] ) ;
final width = int . parse ( evt [ ' width ' ] ) ;
final height = int . parse ( evt [ ' height ' ] ) ;
2020-11-19 00:32:46 +08:00
List < dynamic > colors = json . decode ( evt [ ' colors ' ] ) ;
final rgba = Uint8List . fromList ( colors . map ( ( s ) = > s as int ) . toList ( ) ) ;
2022-09-11 10:50:48 +08:00
final image = await img . decodeImageFromPixels (
rgba , width , height , ui . PixelFormat . rgba8888 ) ;
2023-10-03 21:16:12 +08:00
if ( await _updateCache ( rgba , image , id , hotx , hoty , width , height ) ) {
_images [ id ] = Tuple3 ( image , hotx , hoty ) ;
2022-09-07 22:06:05 +08:00
}
2023-10-03 21:16:12 +08:00
// Update last cursor data.
// Do not use the previous `image` and `id`, because `_id` may be changed.
2023-10-03 21:18:53 +08:00
_updateCurData ( ) ;
2022-09-11 10:50:48 +08:00
}
2022-11-21 18:56:27 +08:00
Future < bool > _updateCache (
2023-10-03 21:16:12 +08:00
Uint8List rgba ,
ui . Image image ,
int id ,
double hotx ,
double hoty ,
int w ,
int h ,
) async {
2022-11-21 18:56:27 +08:00
Uint8List ? data ;
2023-06-06 07:39:44 +08:00
img2 . Image imgOrigin = img2 . Image . fromBytes (
width: w , height: h , bytes: rgba . buffer , order: img2 . ChannelOrder . rgba ) ;
2022-10-10 10:53:10 +08:00
if ( Platform . isWindows ) {
2023-05-11 14:26:59 +08:00
data = imgOrigin . getBytes ( order: img2 . ChannelOrder . bgra ) ;
2022-11-21 18:56:27 +08:00
} else {
ByteData ? imgBytes =
await image . toByteData ( format: ui . ImageByteFormat . png ) ;
if ( imgBytes = = null ) {
return false ;
}
data = imgBytes . buffer . asUint8List ( ) ;
2022-10-10 10:53:10 +08:00
}
2023-10-03 21:16:12 +08:00
final cache = CursorData (
peerId: peerId ,
2022-09-07 22:06:05 +08:00
id: id ,
2022-11-21 18:56:27 +08:00
image: imgOrigin ,
2022-10-27 18:40:45 +08:00
scale: 1.0 ,
2022-10-27 20:05:36 +08:00
data: data ,
2023-10-03 21:16:12 +08:00
hotxOrigin: hotx ,
hotyOrigin: hoty ,
2022-09-07 22:06:05 +08:00
width: w ,
height: h ,
) ;
2023-10-03 21:16:12 +08:00
_cacheMap [ id ] = cache ;
2022-11-13 23:41:07 +08:00
return true ;
2022-09-07 22:06:05 +08:00
}
2023-10-03 21:18:53 +08:00
bool _updateCurData ( ) {
_cache = _cacheMap [ _id ] ;
final tmp = _images [ _id ] ;
2020-11-19 00:32:46 +08:00
if ( tmp ! = null ) {
2022-09-07 17:14:52 +08:00
_image = tmp . item1 ;
_hotx = tmp . item2 ;
_hoty = tmp . item3 ;
2023-10-03 21:16:12 +08:00
try {
// may throw exception, because the listener maybe already dispose
notifyListeners ( ) ;
} catch ( e ) {
debugPrint (
2023-10-03 21:18:53 +08:00
' WARNING: updateCursorId $ _id , without notifyListeners(). $ e ' ) ;
2023-10-03 21:16:12 +08:00
}
return true ;
2022-11-12 22:33:10 +08:00
} else {
2023-10-03 21:16:12 +08:00
return false ;
}
}
2023-10-03 22:15:58 +08:00
updateCursorId ( Map < String , dynamic > evt ) {
2023-10-03 21:18:53 +08:00
if ( ! _updateCurData ( ) ) {
2022-11-12 22:33:10 +08:00
debugPrint (
2023-10-03 21:18:53 +08:00
' WARNING: updateCursorId $ _id , cache is ${ _cache = = null ? " null " : " not null " } . without notifyListeners() ' ) ;
2020-11-19 00:32:46 +08:00
}
}
2022-06-27 16:50:02 +08:00
/// Update the cursor position.
2022-09-11 10:50:48 +08:00
updateCursorPosition ( Map < String , dynamic > evt , String id ) async {
2023-02-03 20:27:05 +08:00
if ( ! isConnIn2Secs ( ) ) {
2023-02-03 18:28:47 +08:00
gotMouseControl = false ;
2023-02-03 20:27:05 +08:00
_lastPeerMouse = DateTime . now ( ) ;
2023-02-03 18:28:47 +08:00
}
2020-11-19 00:32:46 +08:00
_x = double . parse ( evt [ ' x ' ] ) ;
_y = double . parse ( evt [ ' y ' ] ) ;
2022-09-07 12:20:53 +08:00
try {
2022-09-07 17:14:52 +08:00
RemoteCursorMovedState . find ( id ) . value = true ;
2022-09-07 12:20:53 +08:00
} catch ( e ) {
//
}
2020-11-19 00:32:46 +08:00
notifyListeners ( ) ;
}
2022-09-12 16:35:56 +08:00
updateDisplayOrigin ( double x , double y ) {
2020-11-19 00:32:46 +08:00
_displayOriginX = x ;
_displayOriginY = y ;
2020-12-21 21:52:20 +08:00
_x = x + 1 ;
_y = y + 1 ;
2022-09-27 20:35:02 +08:00
parent . target ? . inputModel . moveMouse ( x , y ) ;
2022-06-13 21:07:26 +08:00
parent . target ? . canvasModel . resetOffset ( ) ;
2020-11-19 00:32:46 +08:00
notifyListeners ( ) ;
}
2022-09-12 16:35:56 +08:00
updateDisplayOriginWithCursor (
2022-05-31 14:44:06 +08:00
double x , double y , double xCursor , double yCursor ) {
2020-12-21 18:28:28 +08:00
_displayOriginX = x ;
_displayOriginY = y ;
_x = xCursor ;
_y = yCursor ;
2022-09-27 20:35:02 +08:00
parent . target ? . inputModel . moveMouse ( x , y ) ;
2020-12-21 18:28:28 +08:00
notifyListeners ( ) ;
}
2022-09-12 16:35:56 +08:00
clear ( ) {
2020-11-22 18:29:04 +08:00
_x = - 10000 ;
_x = - 10000 ;
2020-11-19 00:32:46 +08:00
_image = null ;
_images . clear ( ) ;
2022-09-07 22:06:05 +08:00
2022-10-30 13:50:44 +08:00
_clearCache ( ) ;
_cache = null ;
_cacheMap . clear ( ) ;
2022-09-07 22:06:05 +08:00
}
2022-10-30 13:50:44 +08:00
_clearCache ( ) {
final keys = { . . . cachedKeys } ;
for ( var k in keys ) {
2022-12-11 14:17:29 +08:00
debugPrint ( " deleting cursor with key $ k " ) ;
CursorManager . instance . deleteCursor ( k ) ;
2022-09-07 22:06:05 +08:00
}
2020-11-19 00:32:46 +08:00
}
}
2022-08-04 17:24:02 +08:00
class QualityMonitorData {
String ? speed ;
String ? fps ;
String ? delay ;
String ? targetBitrate ;
String ? codecFormat ;
}
class QualityMonitorModel with ChangeNotifier {
WeakReference < FFI > parent ;
QualityMonitorModel ( this . parent ) ;
var _show = false ;
final _data = QualityMonitorData ( ) ;
bool get show = > _show ;
QualityMonitorData get data = > _data ;
2023-06-06 07:39:44 +08:00
checkShowQualityMonitor ( SessionID sessionId ) async {
2022-08-16 15:22:57 +08:00
final show = await bind . sessionGetToggleOption (
2023-06-06 07:39:44 +08:00
sessionId: sessionId , arg: ' show-quality-monitor ' ) = =
2022-08-05 20:29:43 +08:00
true ;
2022-08-04 17:24:02 +08:00
if ( _show ! = show ) {
_show = show ;
notifyListeners ( ) ;
}
}
updateQualityStatus ( Map < String , dynamic > evt ) {
try {
2022-09-07 12:20:53 +08:00
if ( ( evt [ ' speed ' ] as String ) . isNotEmpty ) _data . speed = evt [ ' speed ' ] ;
2023-10-08 21:44:54 +08:00
if ( ( evt [ ' fps ' ] as String ) . isNotEmpty ) {
final fps = jsonDecode ( evt [ ' fps ' ] ) as Map < String , dynamic > ;
final pi = parent . target ? . ffiModel . pi ;
if ( pi ! = null ) {
final currentDisplay = pi . currentDisplay ;
if ( currentDisplay ! = kAllDisplayValue ) {
final fps2 = fps [ currentDisplay . toString ( ) ] ;
if ( fps2 ! = null ) {
_data . fps = fps2 . toString ( ) ;
}
} else if ( fps . isNotEmpty ) {
final fpsList = [ ] ;
for ( var i = 0 ; i < pi . displays . length ; i + + ) {
fpsList . add ( ( fps [ i . toString ( ) ] ? ? 0 ) . toString ( ) ) ;
}
_data . fps = fpsList . join ( ' ' ) ;
}
} else {
_data . fps = null ;
}
}
2022-09-07 12:20:53 +08:00
if ( ( evt [ ' delay ' ] as String ) . isNotEmpty ) _data . delay = evt [ ' delay ' ] ;
if ( ( evt [ ' target_bitrate ' ] as String ) . isNotEmpty ) {
_data . targetBitrate = evt [ ' target_bitrate ' ] ;
}
if ( ( evt [ ' codec_format ' ] as String ) . isNotEmpty ) {
_data . codecFormat = evt [ ' codec_format ' ] ;
}
2022-08-04 17:24:02 +08:00
notifyListeners ( ) ;
2022-09-07 22:06:05 +08:00
} catch ( e ) {
//
}
2022-08-04 17:24:02 +08:00
}
}
2022-09-15 17:31:28 +08:00
class RecordingModel with ChangeNotifier {
WeakReference < FFI > parent ;
RecordingModel ( this . parent ) ;
bool _start = false ;
get start = > _start ;
2022-09-21 16:03:08 +08:00
onSwitchDisplay ( ) {
2022-10-25 10:27:34 +08:00
if ( isIOS | | ! _start ) return ;
2023-06-06 07:39:44 +08:00
final sessionId = parent . target ? . sessionId ;
2022-09-15 17:31:28 +08:00
int ? width = parent . target ? . canvasModel . getDisplayWidth ( ) ;
2022-10-25 10:27:34 +08:00
int ? height = parent . target ? . canvasModel . getDisplayHeight ( ) ;
2023-06-06 07:39:44 +08:00
if ( sessionId = = null | | width = = null | | height = = null ) return ;
2023-10-18 22:39:28 +08:00
final pi = parent . target ? . ffiModel . pi ;
if ( pi = = null ) return ;
final currentDisplay = pi . currentDisplay ;
if ( currentDisplay = = kAllDisplayValue ) return ;
bind . sessionRecordScreen (
sessionId: sessionId ,
start: true ,
display: currentDisplay ,
width: width ,
height: height ) ;
2022-09-15 17:31:28 +08:00
}
2023-08-07 21:32:36 +08:00
toggle ( ) async {
2022-10-25 10:27:34 +08:00
if ( isIOS ) return ;
2023-06-06 07:39:44 +08:00
final sessionId = parent . target ? . sessionId ;
if ( sessionId = = null ) return ;
2023-10-18 22:39:28 +08:00
final pi = parent . target ? . ffiModel . pi ;
if ( pi = = null ) return ;
final currentDisplay = pi . currentDisplay ;
if ( currentDisplay = = kAllDisplayValue ) return ;
2022-09-15 17:31:28 +08:00
_start = ! _start ;
notifyListeners ( ) ;
2023-10-18 22:39:28 +08:00
await _sendStatusMessage ( sessionId , pi , _start ) ;
2022-09-15 17:31:28 +08:00
if ( _start ) {
2023-10-18 22:39:28 +08:00
sessionRefreshVideo ( sessionId , pi ) ;
if ( versionCmp ( pi . version , ' 1.2.4 ' ) > = 0 ) {
// will not receive SwitchDisplay since 1.2.4
onSwitchDisplay ( ) ;
2023-10-08 21:44:54 +08:00
}
2022-09-21 16:03:08 +08:00
} else {
2023-10-18 22:39:28 +08:00
bind . sessionRecordScreen (
sessionId: sessionId ,
start: false ,
display: currentDisplay ,
width: 0 ,
height: 0 ) ;
2022-09-15 17:31:28 +08:00
}
}
2022-09-21 16:03:08 +08:00
2023-10-18 22:39:28 +08:00
onClose ( ) async {
2022-10-25 10:27:34 +08:00
if ( isIOS ) return ;
2023-06-06 07:39:44 +08:00
final sessionId = parent . target ? . sessionId ;
if ( sessionId = = null ) return ;
2023-10-18 22:39:28 +08:00
if ( ! _start ) return ;
2022-09-21 16:03:08 +08:00
_start = false ;
2023-10-18 22:39:28 +08:00
final pi = parent . target ? . ffiModel . pi ;
if ( pi = = null ) return ;
final currentDisplay = pi . currentDisplay ;
if ( currentDisplay = = kAllDisplayValue ) return ;
await _sendStatusMessage ( sessionId , pi , false ) ;
bind . sessionRecordScreen (
sessionId: sessionId ,
start: false ,
display: currentDisplay ,
width: 0 ,
height: 0 ) ;
}
_sendStatusMessage ( SessionID sessionId , PeerInfo pi , bool status ) async {
await bind . sessionRecordStatus ( sessionId: sessionId , status: status ) ;
2022-09-21 16:03:08 +08:00
}
2022-09-15 17:31:28 +08:00
}
2023-02-24 15:51:13 +08:00
class ElevationModel with ChangeNotifier {
WeakReference < FFI > parent ;
ElevationModel ( this . parent ) ;
bool _running = false ;
bool _canElevate = false ;
bool get showRequestMenu = > _canElevate & & ! _running ;
onPeerInfo ( PeerInfo pi ) {
_canElevate = pi . platform = = kPeerPlatformWindows & & pi . sasEnabled = = false ;
2023-09-30 11:22:18 +08:00
_running = false ;
2023-02-24 15:51:13 +08:00
}
2023-10-08 21:44:54 +08:00
onPortableServiceRunning ( bool running ) = > _running = running ;
2023-02-24 15:51:13 +08:00
}
2022-09-06 19:08:45 +08:00
enum ConnType { defaultConn , fileTransfer , portForward , rdp }
2022-09-13 21:52:22 +08:00
/// Flutter state manager and data communication with the Rust core.
2020-11-19 00:32:46 +08:00
class FFI {
2022-09-07 12:20:53 +08:00
var id = ' ' ;
var version = ' ' ;
2022-09-06 19:08:45 +08:00
var connType = ConnType . defaultConn ;
2023-06-09 11:32:36 +08:00
var closed = false ;
2023-07-01 09:33:48 +08:00
var auditNote = ' ' ;
2022-08-12 18:42:02 +08:00
/// dialogManager use late to ensure init after main page binding [globalKey]
late final dialogManager = OverlayDialogManager ( ) ;
2023-06-09 19:57:37 +08:00
late final SessionID sessionId ;
2022-08-12 18:42:02 +08:00
late final ImageModel imageModel ; // session
late final FfiModel ffiModel ; // session
late final CursorModel cursorModel ; // session
late final CanvasModel canvasModel ; // session
late final ServerModel serverModel ; // global
late final ChatModel chatModel ; // session
late final FileModel fileModel ; // session
late final AbModel abModel ; // global
2022-12-11 21:40:35 +08:00
late final GroupModel groupModel ; // global
2022-08-12 18:42:02 +08:00
late final UserModel userModel ; // global
2023-02-03 15:07:45 +08:00
late final PeerTabModel peerTabModel ; // global
2022-08-12 18:42:02 +08:00
late final QualityMonitorModel qualityMonitorModel ; // session
2023-02-03 15:07:45 +08:00
late final RecordingModel recordingModel ; // session
2022-09-27 20:35:02 +08:00
late final InputModel inputModel ; // session
2023-02-24 15:51:13 +08:00
late final ElevationModel elevationModel ; // session
2023-09-06 16:56:39 +08:00
late final CmFileModel cmFileModel ; // cm
2022-06-13 21:07:26 +08:00
2023-08-03 23:14:40 +08:00
FFI ( SessionID ? sId ) {
sessionId = sId ? ? ( isDesktop ? Uuid ( ) . v4obj ( ) : _constSessionId ) ;
2022-08-31 23:02:16 +08:00
imageModel = ImageModel ( WeakReference ( this ) ) ;
ffiModel = FfiModel ( WeakReference ( this ) ) ;
cursorModel = CursorModel ( WeakReference ( this ) ) ;
canvasModel = CanvasModel ( WeakReference ( this ) ) ;
2022-09-13 21:52:22 +08:00
serverModel = ServerModel ( WeakReference ( this ) ) ;
2022-08-31 23:02:16 +08:00
chatModel = ChatModel ( WeakReference ( this ) ) ;
fileModel = FileModel ( WeakReference ( this ) ) ;
userModel = UserModel ( WeakReference ( this ) ) ;
2023-02-03 15:07:45 +08:00
peerTabModel = PeerTabModel ( WeakReference ( this ) ) ;
2022-12-11 21:40:35 +08:00
abModel = AbModel ( WeakReference ( this ) ) ;
groupModel = GroupModel ( WeakReference ( this ) ) ;
2022-08-31 23:02:16 +08:00
qualityMonitorModel = QualityMonitorModel ( WeakReference ( this ) ) ;
2022-09-15 17:31:28 +08:00
recordingModel = RecordingModel ( WeakReference ( this ) ) ;
2022-09-27 20:35:02 +08:00
inputModel = InputModel ( WeakReference ( this ) ) ;
2023-02-24 15:51:13 +08:00
elevationModel = ElevationModel ( WeakReference ( this ) ) ;
2023-09-06 16:56:39 +08:00
cmFileModel = CmFileModel ( WeakReference ( this ) ) ;
2022-06-13 21:07:26 +08:00
}
2020-11-19 00:32:46 +08:00
2023-08-30 18:25:25 +08:00
/// Mobile reuse FFI
void mobileReset ( ) {
ffiModel . waitForFirstImage . value = true ;
ffiModel . waitForImageDialogShow . value = true ;
ffiModel . waitForImageTimer ? . cancel ( ) ;
ffiModel . waitForImageTimer = null ;
}
2022-09-27 20:35:02 +08:00
/// Start with the given [id]. Only transfer file if [isFileTransfer], only port forward if [isPortForward].
2023-10-08 21:44:54 +08:00
void start (
String id , {
bool isFileTransfer = false ,
bool isPortForward = false ,
bool isRdp = false ,
String ? switchUuid ,
String ? password ,
bool ? forceRelay ,
int ? tabWindowId ,
int ? display ,
List < int > ? displays ,
} ) {
2023-06-09 17:07:49 +08:00
closed = false ;
2023-07-01 09:33:48 +08:00
auditNote = ' ' ;
2023-08-30 18:25:25 +08:00
if ( isMobile ) mobileReset ( ) ;
2022-09-07 12:20:53 +08:00
assert ( ! ( isFileTransfer & & isPortForward ) , ' more than one connect type ' ) ;
2022-08-26 11:35:28 +08:00
if ( isFileTransfer ) {
2022-09-06 19:08:45 +08:00
connType = ConnType . fileTransfer ;
2022-08-26 11:35:28 +08:00
} else if ( isPortForward ) {
2022-09-06 19:08:45 +08:00
connType = ConnType . portForward ;
2022-08-26 11:35:28 +08:00
} else {
2022-06-13 21:07:26 +08:00
chatModel . resetClientMode ( ) ;
2023-03-27 16:11:17 +08:00
connType = ConnType . defaultConn ;
2022-06-13 21:07:26 +08:00
canvasModel . id = id ;
2022-09-13 21:52:22 +08:00
imageModel . id = id ;
2023-10-03 21:16:12 +08:00
cursorModel . peerId = id ;
2022-06-21 17:58:27 +08:00
}
2023-08-14 20:40:58 +08:00
// If tabWindowId != null, this session is a "tab -> window" one.
// Else this session is a new one.
if ( tabWindowId = = null ) {
2023-08-03 23:14:40 +08:00
// ignore: unused_local_variable
final addRes = bind . sessionAddSync (
sessionId: sessionId ,
id: id ,
isFileTransfer: isFileTransfer ,
isPortForward: isPortForward ,
isRdp: isRdp ,
2023-08-03 23:40:54 +08:00
switchUuid: switchUuid ? ? ' ' ,
2023-08-03 23:14:40 +08:00
forceRelay: forceRelay ? ? false ,
2023-08-03 23:40:54 +08:00
password: password ? ? ' ' ,
2023-08-03 23:14:40 +08:00
) ;
2023-10-08 21:44:54 +08:00
} else if ( display ! = null ) {
if ( displays = = null ) {
debugPrint (
' Unreachable, failed to add existed session to $ id , the displays is null while display is $ display ' ) ;
return ;
}
final addRes = bind . sessionAddExistedSync ( id: id , sessionId: sessionId ) ;
if ( addRes ! = ' ' ) {
debugPrint (
' Unreachable, failed to add existed session to $ id , $ addRes ' ) ;
return ;
}
bind . sessionTryAddDisplay (
sessionId: sessionId , displays: Int32List . fromList ( displays ) ) ;
ffiModel . pi . currentDisplay = display ;
2023-08-03 23:14:40 +08:00
}
2023-06-06 07:39:44 +08:00
final stream = bind . sessionStart ( sessionId: sessionId , id: id ) ;
final cb = ffiModel . startEventListener ( sessionId , id ) ;
2023-08-04 20:22:54 +08:00
2023-10-08 21:44:54 +08:00
// Force refresh displays.
// The controlled side may not refresh the image when the (peer,display) is already subscribed.
if ( displays ! = null ) {
for ( final display in displays ) {
bind . sessionRefresh ( sessionId: sessionId , display: display ) ;
}
}
2023-08-05 19:48:32 +08:00
final SimpleWrapper < bool > isToNewWindowNotified = SimpleWrapper ( false ) ;
2023-06-09 11:32:36 +08:00
// Preserved for the rgba data.
stream . listen ( ( message ) {
if ( closed ) return ;
2023-08-14 20:40:58 +08:00
if ( tabWindowId ! = null & & ! isToNewWindowNotified . value ) {
// Session is read to be moved to a new window.
// Get the cached data and handle the cached data.
Future . delayed ( Duration . zero , ( ) async {
2023-10-08 21:44:54 +08:00
final args = jsonEncode ( { ' id ' : id , ' close ' : display = = null } ) ;
2023-08-14 20:40:58 +08:00
final cachedData = await DesktopMultiWindow . invokeMethod (
2023-10-08 21:44:54 +08:00
tabWindowId , kWindowEventGetCachedSessionData , args ) ;
2023-08-14 20:40:58 +08:00
if ( cachedData = = null ) {
// unreachable
debugPrint ( ' Unreachable, the cached data is empty. ' ) ;
return ;
}
final data = CachedPeerData . fromString ( cachedData ) ;
if ( data = = null ) {
debugPrint ( ' Unreachable, the cached data cannot be decoded. ' ) ;
return ;
}
2023-09-27 09:16:51 +08:00
await ffiModel . handleCachedPeerData ( data , id ) ;
2023-10-08 21:44:54 +08:00
await sessionRefreshVideo ( sessionId , ffiModel . pi ) ;
2023-08-14 20:40:58 +08:00
} ) ;
2023-08-05 19:48:32 +08:00
isToNewWindowNotified . value = true ;
2023-08-04 20:22:54 +08:00
}
2023-06-09 11:32:36 +08:00
( ) async {
2023-01-19 21:21:28 +08:00
if ( message is EventToUI_Event ) {
2023-02-15 20:39:30 +08:00
if ( message . field0 = = " close " ) {
2023-06-09 11:32:36 +08:00
closed = true ;
debugPrint ( ' Exit session event loop ' ) ;
return ;
2023-02-15 20:39:30 +08:00
}
2023-04-26 16:07:58 +08:00
Map < String , dynamic > ? event ;
2022-06-21 17:58:27 +08:00
try {
2023-04-26 16:07:58 +08:00
event = json . decode ( message . field0 ) ;
2022-06-21 17:58:27 +08:00
} catch ( e ) {
2022-09-21 16:03:08 +08:00
debugPrint ( ' json.decode fail1(): $ e , ${ message . field0 } ' ) ;
2022-05-31 17:36:36 +08:00
}
2023-04-26 16:07:58 +08:00
if ( event ! = null ) {
await cb ( event ) ;
}
2023-01-19 21:21:28 +08:00
} else if ( message is EventToUI_Rgba ) {
2023-10-08 21:44:54 +08:00
final display = message . field0 ;
2023-02-21 23:46:13 +08:00
if ( useTextureRender ) {
2023-08-07 09:51:05 +08:00
onEvent2UIRgba ( ) ;
2023-02-21 23:46:13 +08:00
} else {
// Fetch the image buffer from rust codes.
2023-10-08 21:44:54 +08:00
final sz = platformFFI . getRgbaSize ( sessionId , display ) ;
2023-08-30 13:49:10 +08:00
if ( sz = = 0 ) {
2023-02-21 23:46:13 +08:00
return ;
}
2023-10-08 21:44:54 +08:00
final rgba = platformFFI . getRgba ( sessionId , display , sz ) ;
2023-02-21 23:46:13 +08:00
if ( rgba ! = null ) {
2023-08-07 09:51:05 +08:00
onEvent2UIRgba ( ) ;
2023-10-08 21:44:54 +08:00
imageModel . onRgba ( display , rgba ) ;
2023-02-21 23:46:13 +08:00
}
2023-02-21 21:56:46 +08:00
}
2022-05-31 17:36:36 +08:00
}
2023-06-09 11:32:36 +08:00
} ( ) ;
} ) ;
2022-06-21 17:58:27 +08:00
// every instance will bind a stream
2022-06-17 22:21:49 +08:00
this . id = id ;
2020-11-19 00:32:46 +08:00
}
2023-08-07 09:51:05 +08:00
void onEvent2UIRgba ( ) async {
2023-08-23 23:57:09 +08:00
if ( ffiModel . waitForImageDialogShow . isTrue ) {
ffiModel . waitForImageDialogShow . value = false ;
2023-08-24 12:03:29 +08:00
ffiModel . waitForImageTimer ? . cancel ( ) ;
2023-08-07 09:51:05 +08:00
clearWaitingForImage ( dialogManager , sessionId ) ;
}
2023-08-23 23:57:09 +08:00
if ( ffiModel . waitForFirstImage . value = = true ) {
ffiModel . waitForFirstImage . value = false ;
2023-08-07 09:51:05 +08:00
dialogManager . dismissAll ( ) ;
await canvasModel . updateViewStyle ( ) ;
await canvasModel . updateScrollStyle ( ) ;
for ( final cb in imageModel . callbacksOnFirstImage ) {
cb ( id ) ;
}
}
}
2022-05-28 03:56:42 +08:00
/// Login with [password], choose if the client should [remember] it.
2023-06-06 07:39:44 +08:00
void login ( String osUsername , String osPassword , SessionID sessionId ,
String password , bool remember ) {
2023-03-23 19:57:58 +08:00
bind . sessionLogin (
2023-06-06 07:39:44 +08:00
sessionId: sessionId ,
2023-03-28 08:57:19 +08:00
osUsername: osUsername ,
osPassword: osPassword ,
2023-03-23 19:57:58 +08:00
password: password ,
remember: remember ) ;
2020-11-19 00:32:46 +08:00
}
2022-05-28 03:56:42 +08:00
/// Close the remote session.
2023-08-03 23:14:40 +08:00
Future < void > close ( { bool closeSession = true } ) async {
2023-06-09 11:32:36 +08:00
closed = true ;
2022-03-25 16:34:27 +08:00
chatModel . close ( ) ;
2022-06-13 21:07:26 +08:00
if ( imageModel . image ! = null & & ! isWebDesktop ) {
2023-06-06 07:39:44 +08:00
await setCanvasConfig (
sessionId ,
cursorModel . x ,
cursorModel . y ,
canvasModel . x ,
canvasModel . y ,
canvasModel . scale ,
ffiModel . pi . currentDisplay ) ;
2022-02-03 17:19:25 +08:00
}
2022-10-05 00:22:40 +08:00
imageModel . update ( null ) ;
2020-11-25 16:28:46 +08:00
cursorModel . clear ( ) ;
ffiModel . clear ( ) ;
canvasModel . clear ( ) ;
2022-09-27 20:35:02 +08:00
inputModel . resetModifiers ( ) ;
2023-08-03 23:14:40 +08:00
if ( closeSession ) {
await bind . sessionClose ( sessionId: sessionId ) ;
}
2022-09-07 12:20:53 +08:00
debugPrint ( ' model $ id closed ' ) ;
2023-02-08 09:11:53 +08:00
id = ' ' ;
2020-11-19 00:32:46 +08:00
}
2022-09-27 20:35:02 +08:00
void setMethodCallHandler ( FMethod callback ) {
2022-08-03 22:03:31 +08:00
platformFFI . setMethodCallHandler ( callback ) ;
2022-02-10 02:07:53 +08:00
}
2022-06-13 21:07:26 +08:00
Future < bool > invokeMethod ( String method , [ dynamic arguments ] ) async {
2022-08-03 22:03:31 +08:00
return await platformFFI . invokeMethod ( method , arguments ) ;
2020-11-19 00:32:46 +08:00
}
}
2023-05-18 21:25:48 +08:00
const kInvalidResolutionValue = - 1 ;
const kVirtualDisplayResolutionValue = 0 ;
2020-11-19 00:32:46 +08:00
class Display {
double x = 0 ;
double y = 0 ;
int width = 0 ;
int height = 0 ;
2022-12-31 21:41:16 +08:00
bool cursorEmbedded = false ;
2023-05-18 21:25:48 +08:00
int originalWidth = kInvalidResolutionValue ;
int originalHeight = kInvalidResolutionValue ;
2022-09-12 10:52:38 +08:00
Display ( ) {
width = ( isDesktop | | isWebDesktop )
? kDesktopDefaultDisplayWidth
: kMobileDefaultDisplayWidth ;
height = ( isDesktop | | isWebDesktop )
? kDesktopDefaultDisplayHeight
: kMobileDefaultDisplayHeight ;
}
2023-02-25 22:47:22 +08:00
@ override
bool operator = = ( Object other ) = >
other is Display & &
other . runtimeType = = runtimeType & &
_innerEqual ( other ) ;
bool _innerEqual ( Display other ) = >
other . x = = x & &
other . y = = y & &
other . width = = width & &
other . height = = height & &
other . cursorEmbedded = = cursorEmbedded ;
2023-05-18 16:17:51 +08:00
2023-05-18 21:25:48 +08:00
bool get isOriginalResolutionSet = >
originalWidth ! = kInvalidResolutionValue & &
originalHeight ! = kInvalidResolutionValue ;
bool get isVirtualDisplayResolution = >
originalWidth = = kVirtualDisplayResolutionValue & &
originalHeight = = kVirtualDisplayResolutionValue ;
2023-05-18 16:17:51 +08:00
bool get isOriginalResolution = >
width = = originalWidth & & height = = originalHeight ;
2020-11-19 00:32:46 +08:00
}
2023-02-09 15:53:51 +08:00
class Resolution {
int width = 0 ;
int height = 0 ;
Resolution ( this . width , this . height ) ;
@ override
String toString ( ) {
return ' Resolution( $ width , $ height ) ' ;
}
}
2022-12-02 21:34:20 +08:00
class Features {
bool privacyMode = false ;
}
2023-10-08 21:44:54 +08:00
const kInvalidDisplayIndex = - 1 ;
2023-08-24 12:03:29 +08:00
class PeerInfo with ChangeNotifier {
2022-09-07 12:20:53 +08:00
String version = ' ' ;
String username = ' ' ;
String hostname = ' ' ;
String platform = ' ' ;
2022-02-17 15:22:14 +08:00
bool sasEnabled = false ;
2023-10-08 21:44:54 +08:00
bool isSupportMultiUiSession = false ;
2022-02-17 15:22:14 +08:00
int currentDisplay = 0 ;
2023-10-08 21:44:54 +08:00
int primaryDisplay = kInvalidDisplayIndex ;
2022-02-17 15:22:14 +08:00
List < Display > displays = [ ] ;
2022-12-02 21:34:20 +08:00
Features features = Features ( ) ;
2023-02-09 15:53:51 +08:00
List < Resolution > resolutions = [ ] ;
2023-10-16 23:19:07 +08:00
Map < String , dynamic > platformAdditions = { } ;
2023-03-21 11:27:30 +08:00
2023-10-08 21:44:54 +08:00
RxInt displaysCount = 0. obs ;
2023-08-24 12:03:29 +08:00
RxBool isSet = false . obs ;
2023-10-16 23:19:07 +08:00
bool get isWayland = > platformAdditions [ ' is_wayland ' ] = = true ;
bool get isHeadless = > platformAdditions [ ' headless ' ] = = true ;
2023-10-08 21:44:54 +08:00
2023-10-09 17:22:22 +08:00
bool get isSupportMultiDisplay = > isDesktop & & isSupportMultiUiSession ;
2023-10-08 21:44:54 +08:00
bool get cursorEmbedded = > tryGetDisplay ( ) ? . cursorEmbedded ? ? false ;
Display ? tryGetDisplay ( ) {
if ( displays . isEmpty ) {
return null ;
}
if ( currentDisplay = = kAllDisplayValue ) {
return displays [ 0 ] ;
} else {
if ( currentDisplay > 0 & & currentDisplay < displays . length ) {
return displays [ currentDisplay ] ;
} else {
return displays [ 0 ] ;
}
}
}
Display ? tryGetDisplayIfNotAllDisplay ( ) {
if ( displays . isEmpty ) {
return null ;
}
if ( currentDisplay = = kAllDisplayValue ) {
return null ;
}
2023-10-17 23:31:50 +08:00
if ( currentDisplay > = 0 & & currentDisplay < displays . length ) {
2023-10-08 21:44:54 +08:00
return displays [ currentDisplay ] ;
} else {
return null ;
}
}
List < Display > getCurDisplays ( ) {
if ( currentDisplay = = kAllDisplayValue ) {
return displays ;
} else {
if ( currentDisplay > = 0 & & currentDisplay < displays . length ) {
return [ displays [ currentDisplay ] ] ;
} else {
return [ ] ;
}
}
}
2020-11-19 00:32:46 +08:00
}
2020-12-21 18:28:28 +08:00
2022-11-10 21:25:12 +08:00
const canvasKey = ' canvas ' ;
2023-06-06 07:39:44 +08:00
Future < void > setCanvasConfig (
SessionID sessionId ,
double xCursor ,
double yCursor ,
double xCanvas ,
double yCanvas ,
double scale ,
int currentDisplay ) async {
2022-09-01 21:18:29 +08:00
final p = < String , dynamic > { } ;
2020-12-21 18:28:28 +08:00
p [ ' xCursor ' ] = xCursor ;
p [ ' yCursor ' ] = yCursor ;
p [ ' xCanvas ' ] = xCanvas ;
p [ ' yCanvas ' ] = yCanvas ;
p [ ' scale ' ] = scale ;
p [ ' currentDisplay ' ] = currentDisplay ;
2023-08-10 22:27:35 +08:00
await bind . sessionSetFlutterOption (
2023-06-06 07:39:44 +08:00
sessionId: sessionId , k: canvasKey , v: jsonEncode ( p ) ) ;
2020-12-21 18:28:28 +08:00
}
2023-06-06 07:39:44 +08:00
Future < Map < String , dynamic > ? > getCanvasConfig ( SessionID sessionId ) async {
2022-05-23 16:02:37 +08:00
if ( ! isWebDesktop ) return null ;
2023-06-06 07:39:44 +08:00
var p =
2023-08-10 22:27:35 +08:00
await bind . sessionGetFlutterOption ( sessionId: sessionId , k: canvasKey ) ;
2022-11-10 21:25:12 +08:00
if ( p = = null | | p . isEmpty ) return null ;
try {
Map < String , dynamic > m = json . decode ( p ) ;
return m ;
} catch ( e ) {
return null ;
}
2020-12-21 18:28:28 +08:00
}
2022-09-27 20:35:02 +08:00
Future < void > initializeCursorAndCanvas ( FFI ffi ) async {
2023-06-06 07:39:44 +08:00
var p = await getCanvasConfig ( ffi . sessionId ) ;
2020-12-21 18:28:28 +08:00
int currentDisplay = 0 ;
if ( p ! = null ) {
currentDisplay = p [ ' currentDisplay ' ] ;
}
2022-06-13 21:07:26 +08:00
if ( p = = null | | currentDisplay ! = ffi . ffiModel . pi . currentDisplay ) {
2023-10-08 21:44:54 +08:00
ffi . cursorModel . updateDisplayOrigin (
ffi . ffiModel . rect ? . left ? ? 0 , ffi . ffiModel . rect ? . top ? ? 0 ) ;
2020-12-21 18:28:28 +08:00
return ;
}
double xCursor = p [ ' xCursor ' ] ;
double yCursor = p [ ' yCursor ' ] ;
double xCanvas = p [ ' xCanvas ' ] ;
double yCanvas = p [ ' yCanvas ' ] ;
double scale = p [ ' scale ' ] ;
2023-10-08 21:44:54 +08:00
ffi . cursorModel . updateDisplayOriginWithCursor ( ffi . ffiModel . rect ? . left ? ? 0 ,
ffi . ffiModel . rect ? . top ? ? 0 , xCursor , yCursor ) ;
2022-06-13 21:07:26 +08:00
ffi . canvasModel . update ( xCanvas , yCanvas , scale ) ;
2020-12-21 18:28:28 +08:00
}
2023-08-05 21:40:15 +08:00
2023-08-06 22:11:31 +08:00
clearWaitingForImage ( OverlayDialogManager ? dialogManager , SessionID sessionId ) {
2023-08-09 15:32:51 +08:00
dialogManager ? . dismissByTag ( ' $ sessionId -waiting-for-image ' ) ;
2023-08-05 21:40:15 +08:00
}