2022-05-29 17:19:50 +08:00
import ' dart:async ' ;
import ' dart:convert ' ;
2023-02-12 01:52:11 +08:00
import ' dart:ffi ' hide Size ;
2022-10-10 10:53:10 +08:00
import ' dart:io ' ;
2022-05-29 17:19:50 +08:00
import ' dart:math ' ;
import ' dart:typed_data ' ;
import ' dart:ui ' as ui ;
2023-02-12 01:52:11 +08:00
import ' package:ffi/ffi.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 ' ;
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 ' ;
2022-11-12 22:33:10 +08:00
import ' package:flutter_hbb/common/shared_state.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 ' ;
2022-05-29 17:19:50 +08:00
2022-02-28 18:29:25 +08:00
import ' ../common.dart ' ;
2022-09-10 19:50:48 -07:00
import ' ../utils/image.dart ' as img ;
2022-05-24 23:33:00 +08:00
import ' ../mobile/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 01:35:56 -07:00
typedef HandleMsgBox = Function ( Map < String , dynamic > evt , String id ) ;
2023-02-16 14:54:13 +08:00
typedef ReconnectHandle = Function ( OverlayDialogManager , String , bool ) ;
2022-10-27 12:07:48 +08:00
final _waitForImage = < String , bool > { } ;
2022-03-19 23:28:29 +08:00
2020-11-19 00:32:46 +08:00
class FfiModel with ChangeNotifier {
2022-02-17 15:22:14 +08:00
PeerInfo _pi = PeerInfo ( ) ;
Display _display = Display ( ) ;
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 ;
2022-06-13 21:07:26 +08:00
WeakReference < FFI > parent ;
2020-11-22 21:08:19 +08:00
2022-04-28 22: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
Display get display = > _display ;
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
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 ( ) ;
2022-04-17 00:44:05 +08:00
}
2022-09-12 01:35:56 -07:00
toggleTouchMode ( ) {
2022-04-28 22:44:54 +08:00
if ( ! isPeerAndroid ) {
_touchMode = ! _touchMode ;
notifyListeners ( ) ;
}
}
2022-09-12 01:35:56 -07: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
}
2020-11-24 23:36:46 +08:00
bool keyboard ( ) = > _permissions [ ' keyboard ' ] ! = false ;
2020-11-23 23:18:42 +08:00
2022-09-12 01:35:56 -07:00
clear ( ) {
2020-11-22 21:08:19 +08:00
_pi = PeerInfo ( ) ;
_display = Display ( ) ;
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 ( ) ;
}
2022-09-12 01:35:56 -07:00
setConnectionType ( String peerId , bool secure , bool 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-06 21:20:53 -07: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 01:35:56 -07: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
2022-09-10 19:50:48 -07:00
StreamEventHandler startEventListener ( String peerId ) {
return ( evt ) async {
2022-05-31 17:36:36 +08:00
var name = evt [ ' name ' ] ;
if ( name = = ' msgbox ' ) {
handleMsgBox ( evt , peerId ) ;
} else if ( name = = ' peer_info ' ) {
2022-05-31 22:09:36 +08:00
handlePeerInfo ( evt , peerId ) ;
2023-02-17 13:32:17 +08:00
} else if ( name = = ' sync_peer_info ' ) {
handleSyncPeerInfo ( evt , 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 ' ) {
2022-12-30 16:14:30 +08:00
handleSwitchDisplay ( evt , peerId ) ;
2022-05-31 17:36:36 +08:00
} else if ( name = = ' cursor_data ' ) {
2022-09-10 19:50:48 -07:00
await parent . target ? . cursorModel . updateCursorData ( evt ) ;
2022-05-31 17:36:36 +08:00
} else if ( name = = ' cursor_id ' ) {
2022-09-10 19:50:48 -07:00
await parent . target ? . cursorModel . updateCursorId ( evt ) ;
2022-05-31 17:36:36 +08:00
} else if ( name = = ' cursor_position ' ) {
2022-09-10 19:50:48 -07: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-06 21:20:53 -07: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-06 21:20:53 -07: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 ' ) {
2022-06-13 21:07:26 +08:00
parent . target ? . fileModel . tryUpdateJobProgress ( evt ) ;
2022-05-31 17:36:36 +08:00
} else if ( name = = ' job_done ' ) {
2022-06-13 21:07:26 +08:00
parent . target ? . fileModel . jobDone ( evt ) ;
2022-05-31 17:36:36 +08:00
} else if ( name = = ' job_error ' ) {
2022-06-13 21:07:26 +08:00
parent . target ? . fileModel . jobError ( evt ) ;
2022-05-31 17:36:36 +08:00
} else if ( name = = ' override_file_confirm ' ) {
2022-06-13 21:07:26 +08:00
parent . target ? . fileModel . overrideFileConfirm ( evt ) ;
2022-07-11 18:23:58 +08:00
} else if ( name = = ' load_last_job ' ) {
parent . target ? . fileModel . loadLastJob ( evt ) ;
2022-07-11 10:30:45 +08:00
} else if ( name = = ' update_folder_files ' ) {
parent . target ? . fileModel . 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 ' ) {
2022-08-26 23:28:08 +08:00
updatePrivacyMode ( evt , peerId ) ;
2022-10-11 19:52:03 +08:00
} else if ( name = = ' new_connection ' ) {
2023-01-18 13:54:56 +08:00
var uni_links = evt [ ' uni_links ' ] . toString ( ) ;
if ( uni_links . startsWith ( kUniLinksPrefix ) ) {
parseRustdeskUri ( uni_links ) ;
2022-10-12 21:57:19 +08:00
}
2022-11-12 22:33:10 +08:00
} else if ( name = = ' alias ' ) {
handleAliasChanged ( evt ) ;
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 ' ) {
cancelMsgBox ( evt , peerId ) ;
2023-01-17 13:28:33 +08:00
} else if ( name = = ' switch_back ' ) {
final peer_id = evt [ ' peer_id ' ] . toString ( ) ;
await bind . sessionSwitchSides ( id: peer_id ) ;
closeConnection ( id: peer_id ) ;
2023-02-24 15:51:13 +08:00
} else if ( name = = ' portable_service_running ' ) {
parent . target ? . elevationModel . onPortableServiceRunning ( evt ) ;
2023-02-04 11:23:36 +08:00
} else if ( name = = " on_url_scheme_received " ) {
final url = evt [ ' url ' ] . toString ( ) ;
parseRustdeskUri ( url ) ;
2023-02-06 20:10:39 +08:00
} else if ( name = = " on_voice_call_waiting " ) {
// Waiting for the response from the peer.
parent . target ? . chatModel . onVoiceCallWaiting ( ) ;
} else if ( name = = " on_voice_call_started " ) {
// Voice call is connected.
parent . target ? . chatModel . onVoiceCallStarted ( ) ;
} else if ( name = = " on_voice_call_closed " ) {
// Voice call is closed with reason.
final reason = evt [ ' reason ' ] . toString ( ) ;
parent . target ? . chatModel . onVoiceCallClosed ( reason ) ;
} else if ( name = = " on_voice_call_incoming " ) {
// Voice call is requested by the peer.
parent . target ? . chatModel . onVoiceCallIncoming ( ) ;
2023-02-07 16:11:55 +08:00
} else if ( name = = " update_voice_call_state " ) {
parent . target ? . serverModel . updateVoiceCallState ( evt ) ;
2023-02-06 20:10:39 +08:00
} else {
debugPrint ( " Unknown event name: $ name " ) ;
2022-05-31 17:36:36 +08:00
}
} ;
}
2022-05-28 03:56:42 +08:00
/// Bind the event listener to receive events from the Rust core.
2022-09-12 01:35:56 -07:00
updateEventListener ( String peerId ) {
2022-09-06 21:20:53 -07:00
platformFFI . setEventCallback ( startEventListener ( peerId ) ) ;
2020-11-19 00:32:46 +08:00
}
2022-11-12 22:33:10 +08:00
handleAliasChanged ( Map < String , dynamic > evt ) {
final rxAlias = PeerStringOption . find ( evt [ ' id ' ] , ' alias ' ) ;
if ( rxAlias . value ! = evt [ ' alias ' ] ) {
rxAlias . value = evt [ ' alias ' ] ;
}
}
2023-02-25 22:47:22 +08:00
_updateCurDisplay ( String peerId , Display newDisplay ) {
if ( newDisplay ! = _display ) {
if ( newDisplay . x ! = _display . x | | newDisplay . y ! = _display . y ) {
parent . target ? . cursorModel
. updateDisplayOrigin ( newDisplay . x , newDisplay . y ) ;
}
_display = newDisplay ;
_updateSessionWidthHeight ( peerId ) ;
}
}
2022-12-30 16:14:30 +08:00
handleSwitchDisplay ( Map < String , dynamic > evt , String peerId ) {
2020-11-19 00:32:46 +08:00
_pi . currentDisplay = int . parse ( evt [ ' display ' ] ) ;
2023-02-25 22:47:22 +08:00
var newDisplay = Display ( ) ;
newDisplay . x = double . parse ( evt [ ' x ' ] ) ;
newDisplay . y = double . parse ( evt [ ' y ' ] ) ;
newDisplay . width = int . parse ( evt [ ' width ' ] ) ;
newDisplay . height = int . parse ( evt [ ' height ' ] ) ;
newDisplay . cursorEmbedded = int . parse ( evt [ ' cursor_embedded ' ] ) = = 1 ;
2022-06-02 17:16:23 +08:00
2023-02-25 22:47:22 +08:00
_updateCurDisplay ( peerId , newDisplay ) ;
2023-02-21 21:56:46 +08:00
2022-12-30 16:14:30 +08:00
try {
CurrentDisplayState . find ( peerId ) . value = _pi . currentDisplay ;
} catch ( e ) {
//
}
2022-09-21 16:03:08 +08:00
parent . target ? . recordingModel . onSwitchDisplay ( ) ;
2023-02-09 15:53:51 +08:00
handleResolutions ( peerId , evt [ " resolutions " ] ) ;
2020-11-25 23:52:58 +08:00
notifyListeners ( ) ;
2020-11-19 00:32:46 +08:00
}
2022-11-15 16:49:55 +08:00
cancelMsgBox ( Map < String , dynamic > evt , String id ) {
if ( parent . target = = null ) return ;
final dialogManager = parent . target ! . dialogManager ;
final tag = ' $ id - ${ evt [ ' tag ' ] } ' ;
dialogManager . dismissByTag ( tag ) ;
}
2022-05-28 03:56:42 +08:00
/// Handle the message box event based on [evt] and [id].
2022-09-12 01:35:56 -07:00
handleMsgBox ( Map < String , dynamic > evt , String id ) {
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-01-30 17:56:35 +08:00
wrongPasswordDialog ( id , dialogManager , type , title , text ) ;
2022-03-28 17:24:52 +08:00
} else if ( type = = ' input-password ' ) {
2022-08-12 18:42:02 +08:00
enterPasswordDialog ( id , dialogManager ) ;
2022-08-04 17:24:02 +08:00
} else if ( type = = ' restarting ' ) {
2022-10-25 11:27:34 +09:00
showMsgBox ( id , type , title , text , link , false , dialogManager ,
hasCancel: false ) ;
2022-11-20 15:53:08 +08:00
} else if ( type = = ' wait-remote-accept-nook ' ) {
2023-01-30 17:56:35 +08:00
showWaitAcceptDialog ( id , type , title , text , dialogManager ) ;
2023-01-12 21:03:05 +08:00
} else if ( type = = ' on-uac ' | | type = = ' on-foreground-elevated ' ) {
showOnBlockDialog ( id , type , title , text , dialogManager ) ;
} else if ( type = = ' wait-uac ' ) {
2023-01-30 17:56:35 +08:00
showWaitUacDialog ( id , dialogManager , type ) ;
2023-01-12 21:03:05 +08:00
} else if ( type = = ' elevation-error ' ) {
showElevationError ( id , type , title , text , dialogManager ) ;
2023-02-14 19:44:14 +08:00
} else if ( type = = " relay-hint " ) {
showRelayHintDialog ( id , type , title , text , dialogManager ) ;
2022-03-28 17:24:52 +08:00
} else {
var hasRetry = evt [ ' hasRetry ' ] = = ' true ' ;
2022-10-14 11:19:49 +08:00
showMsgBox ( id , 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].
2022-10-14 11:19:49 +08:00
showMsgBox ( String id , String type , String title , String text , String link ,
bool hasRetry , OverlayDialogManager dialogManager ,
2022-08-04 17:24:02 +08:00
{ bool ? hasCancel } ) {
2023-02-16 14:54:13 +08:00
msgBox ( id , type , title , text , link , dialogManager ,
hasCancel: hasCancel , reconnect: reconnect ) ;
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-02-16 14:54:13 +08:00
reconnect ( dialogManager , id , false ) ;
2022-03-28 17:24:52 +08:00
} ) ;
_reconnects * = 2 ;
} else {
_reconnects = 1 ;
}
}
2023-02-16 14:54:13 +08:00
void reconnect (
OverlayDialogManager dialogManager , String id , bool forceRelay ) {
bind . sessionReconnect ( id: id , forceRelay: forceRelay ) ;
clearPermissions ( ) ;
dialogManager . showLoading ( translate ( ' Connecting... ' ) ,
onCancel: closeConnection ) ;
}
2023-02-14 19:44:14 +08:00
void showRelayHintDialog ( String id , String type , String title , String text ,
OverlayDialogManager dialogManager ) {
dialogManager . show ( tag: ' $ id - $ type ' , ( setState , close ) {
onClose ( ) {
closeConnection ( ) ;
close ( ) ;
}
final style =
ElevatedButton . styleFrom ( backgroundColor: Colors . green [ 700 ] ) ;
return CustomAlertDialog (
title: null ,
content: msgboxContent ( type , title ,
" ${ translate ( text ) } \n \n ${ translate ( ' relay_hint_tip ' ) } " ) ,
actions: [
dialogButton ( ' Close ' , onPressed: onClose , isOutline: true ) ,
2023-02-16 14:54:13 +08:00
dialogButton ( ' Retry ' ,
onPressed: ( ) = > reconnect ( dialogManager , id , false ) ) ,
2023-02-14 19:44:14 +08:00
dialogButton ( ' Connect via relay ' ,
2023-02-16 14:54:13 +08:00
onPressed: ( ) = > reconnect ( dialogManager , id , true ) ,
buttonStyle: style ) ,
2023-02-14 19:44:14 +08:00
dialogButton ( ' Always connect via relay ' , onPressed: ( ) {
const option = ' force-always-relay ' ;
bind . sessionPeerOption (
id: id , name: option , value: bool2option ( option , true ) ) ;
2023-02-16 14:54:13 +08:00
reconnect ( dialogManager , id , true ) ;
2023-02-14 19:44:14 +08:00
} , buttonStyle: style ) ,
] ,
onCancel: onClose ,
) ;
} ) ;
}
2023-02-25 22:47:22 +08:00
_updateSessionWidthHeight ( String id ) {
parent . target ? . canvasModel . updateViewStyle ( ) ;
2023-02-21 21:56:46 +08:00
bind . sessionSetSize ( id: id , width: display . width , height: display . height ) ;
}
2022-05-28 03:56:42 +08:00
/// Handle the peer info event based on [evt].
2022-09-12 01:35:56 -07:00
handlePeerInfo ( Map < String , dynamic > evt , String peerId ) async {
2022-09-01 06:18:29 -07: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 ' ] ;
2020-11-19 00:32:46 +08:00
_pi . username = evt [ ' username ' ] ;
_pi . hostname = evt [ ' hostname ' ] ;
_pi . platform = evt [ ' platform ' ] ;
2022-09-06 21:20:53 -07:00
_pi . sasEnabled = evt [ ' sas_enabled ' ] = = ' true ' ;
2020-11-19 00:32:46 +08:00
_pi . currentDisplay = int . parse ( evt [ ' current_display ' ] ) ;
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 ) {
//
}
2022-04-28 22:44:54 +08:00
if ( isPeerAndroid ) {
_touchMode = true ;
2022-09-08 22:18:02 +08:00
if ( parent . target ! = null & &
2022-09-29 21:09:40 +08:00
parent . target ! . connType = = ConnType . defaultConn & &
2022-09-08 22:18:02 +08:00
parent . target ! . ffiModel . permissions [ ' keyboard ' ] ! = false ) {
2022-09-29 20:50:01 +08:00
Timer (
const Duration ( milliseconds: 100 ) ,
( ) = > parent . target ! . dialogManager
. showMobileActionsOverlay ( ffi: parent . target ! ) ) ;
2022-04-28 22:44:54 +08:00
}
} else {
2022-08-03 22:03:31 +08:00
_touchMode =
2022-09-06 21:20:53 -07:00
await bind . sessionGetOption ( id: peerId , arg: ' touch-mode ' ) ! = ' ' ;
2022-04-28 22:44:54 +08:00
}
2022-09-06 19:08:45 +08:00
if ( parent . target ! = null & &
parent . target ! . connType = = ConnType . fileTransfer ) {
2022-06-13 21:07:26 +08:00
parent . target ? . fileModel . onReady ( ) ;
2022-03-19 23:28:29 +08:00
} else {
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 ) {
Map < String , dynamic > d0 = displays [ i ] ;
var d = Display ( ) ;
d . x = d0 [ ' x ' ] . toDouble ( ) ;
d . y = d0 [ ' y ' ] . toDouble ( ) ;
d . width = d0 [ ' width ' ] ;
d . height = d0 [ ' height ' ] ;
2022-12-31 21:41:16 +08:00
d . cursorEmbedded = d0 [ ' cursor_embedded ' ] = = 1 ;
2022-03-16 15:33:00 +08:00
_pi . displays . add ( d ) ;
}
2023-02-17 13:32:17 +08:00
stateGlobal . displaysCount . value = _pi . displays . length ;
2022-03-16 15:33:00 +08:00
if ( _pi . currentDisplay < _pi . displays . length ) {
_display = _pi . displays [ _pi . currentDisplay ] ;
2023-02-25 22:47:22 +08:00
_updateSessionWidthHeight ( peerId ) ;
2022-03-16 15:33:00 +08:00
}
2022-10-04 21:19:31 +08:00
if ( displays . isNotEmpty ) {
2022-08-12 18:42:02 +08:00
parent . target ? . dialogManager . showLoading (
translate ( ' Connected, waiting for image... ' ) ,
2022-08-16 21:27:21 +08:00
onCancel: closeConnection ) ;
2022-10-27 12:07:48 +08:00
_waitForImage [ peerId ] = true ;
2022-04-26 21:21:08 +08:00
_reconnects = 1 ;
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
}
2020-11-29 14:28:07 +08:00
notifyListeners ( ) ;
2020-11-19 00:32:46 +08:00
}
2022-08-08 22:00:01 +08:00
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-02-17 13:32:17 +08:00
/// Handle the peer info synchronization event based on [evt].
handleSyncPeerInfo ( Map < String , dynamic > evt , String peerId ) async {
if ( evt [ ' displays ' ] ! = null ) {
List < dynamic > displays = json . decode ( evt [ ' displays ' ] ) ;
List < Display > newDisplays = [ ] ;
for ( int i = 0 ; i < displays . length ; + + i ) {
Map < String , dynamic > d0 = displays [ i ] ;
var d = Display ( ) ;
d . x = d0 [ ' x ' ] . toDouble ( ) ;
d . y = d0 [ ' y ' ] . toDouble ( ) ;
d . width = d0 [ ' width ' ] ;
d . height = d0 [ ' height ' ] ;
d . cursorEmbedded = d0 [ ' cursor_embedded ' ] = = 1 ;
newDisplays . add ( d ) ;
}
_pi . displays = newDisplays ;
stateGlobal . displaysCount . value = _pi . displays . length ;
2023-02-09 15:53:51 +08:00
if ( _pi . currentDisplay > = 0 & & _pi . currentDisplay < _pi . displays . length ) {
2023-02-25 22:47:22 +08:00
_updateCurDisplay ( peerId , _pi . displays [ _pi . currentDisplay ] ) ;
2023-02-09 15:53:51 +08:00
}
2023-02-17 13:32:17 +08:00
}
notifyListeners ( ) ;
}
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
}
2022-08-26 23:28:08 +08:00
updatePrivacyMode ( Map < String , dynamic > evt , String peerId ) {
2022-08-08 22:00:01 +08:00
notifyListeners ( ) ;
2022-08-26 23:28:08 +08:00
try {
PrivacyModeState . find ( peerId ) . value =
bind . sessionGetToggleOptionSync ( id: peerId , arg: ' privacy-mode ' ) ;
} catch ( e ) {
//
}
2022-08-08 22:00:01 +08:00
}
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
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
2022-06-13 21:07:26 +08:00
ImageModel ( this . parent ) ;
2023-02-21 21:56:46 +08:00
addCallbackOnFirstImage ( Function ( String ) cb ) = > callbacksOnFirstImage . add ( cb ) ;
2022-12-27 16:45:13 +08:00
2022-10-05 00:22:40 +08:00
onRgba ( Uint8List rgba ) {
2022-10-27 12:07:48 +08:00
if ( _waitForImage [ id ] ! ) {
_waitForImage [ id ] = false ;
2022-08-12 18:42:02 +08:00
parent . target ? . dialogManager . dismissAll ( ) ;
2022-12-27 16:45:13 +08:00
if ( isDesktop ) {
2023-02-21 21:56:46 +08:00
for ( final cb in callbacksOnFirstImage ) {
2022-12-27 16:45:13 +08:00
cb ( id ) ;
}
}
2022-05-31 22:09:36 +08:00
}
2023-02-10 18:26:23 +08:00
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 ,
2022-06-13 21:07:26 +08:00
parent . target ? . ffiModel . display . width ? ? 0 ,
parent . target ? . ffiModel . display . height ? ? 0 ,
2023-02-15 16:44:40 +08:00
isWeb ? ui . PixelFormat . rgba8888 : ui . PixelFormat . bgra8888 ,
onPixelsCopied: ( ) {
// Unlock the rgba memory from rust codes.
platformFFI . nextRgba ( id ) ;
} ) . 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 07:06:05 -07: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-11 19:52:38 -07: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-11 19:52:38 -07:00
await initializeCursorAndCanvas ( parent . target ! ) ;
}
if ( parent . target ? . ffiModel . isPeerAndroid ? ? false ) {
2022-11-17 18:52:27 +08:00
bind . sessionSetViewStyle ( id: id , value: ' adaptive ' ) ;
2022-09-11 19:52:38 -07: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 06:59:06 -07: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 06:59:06 -07: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 06:59:06 -07: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 06:59:06 -07: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-06 21:20:53 -07:00
String id = ' ' ;
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 ;
CanvasModel ( this . parent ) ;
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 14:50:51 +08:00
static double get leftToEdge = >
windowBorderWidth + kDragToResizeAreaPadding . left ;
static double get rightToEdge = >
windowBorderWidth + kDragToResizeAreaPadding . right ;
static double get topToEdge = >
tabBarHeight + windowBorderWidth + kDragToResizeAreaPadding . top ;
static double get bottomToEdge = >
windowBorderWidth + kDragToResizeAreaPadding . bottom ;
2022-09-11 19:52:38 -07: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 ) ;
}
2022-11-17 18:52:27 +08:00
final style = await bind . sessionGetViewStyle ( id: id ) ;
2022-09-13 06:59:06 -07: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 06:59:06 -07: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 06:59:06 -07: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 06:59:06 -07: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 {
2022-11-17 18:52:27 +08:00
final style = await bind . sessionGetScrollStyle ( id: id ) ;
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-11 19:52:38 -07: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 = >
parent . target ? . ffiModel . display . 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 ;
return parent . target ? . ffiModel . display . width ? ? 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 ;
return parent . target ? . ffiModel . display . height ? ? 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 01:35:56 -07: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.
2022-09-06 21:20:53 -07:00
if ( parent . target ! = null & & parent . target ! . ffiModel . keyboard ( ) ) {
// Draw cursor if is not desktop.
if ( ! isDesktop ) {
2022-09-03 10:39:33 +08:00
parent . target ! . cursorModel . moveLocal ( x , y ) ;
2022-09-06 21:20:53 -07: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 01:35:56 -07: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 01:35:56 -07: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 01:35:56 -07:00
panY ( double dy ) {
2020-11-23 23:18:42 +08:00
_y + = dy ;
notifyListeners ( ) ;
}
2022-09-12 01:35:56 -07: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 01:35:56 -07: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 07:06:05 -07: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 07:06:05 -07: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 07:06:05 -07:00
required this . data ,
2022-11-15 22:35:10 +08:00
required this . hotxOrigin ,
required this . hotyOrigin ,
2022-09-07 07:06:05 -07:00
required this . width ,
required this . height ,
2022-11-15 22:35:10 +08:00
} ) : hotx = hotxOrigin * scale ,
hoty = hotxOrigin * scale ;
2022-09-07 19:52:30 -07: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
)
2022-10-30 13:50:44 +08:00
. getBytes ( format: img2 . Format . 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 07:06:05 -07: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.
Uint8List data = defaultImg . getBytes ( format: img2 . Format . rgba ) ;
_image = await img . decodeImageFromPixels (
data , defaultImg . width , defaultImg . height , ui . PixelFormat . rgba8888 ) ;
double scale = 1.0 ;
if ( Platform . isWindows ) {
data = _image2 ! . getBytes ( format: img2 . Format . bgra ) ;
} 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 02:14:52 -07: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 ;
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 ) ) ;
2022-09-13 21:52:22 +08:00
String id = ' ' ;
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
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 07:06:05 -07: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 01:35:56 -07: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 01:35:56 -07: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 01:35:56 -07: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 01:35:56 -07:00
updatePan ( double dx , double dy , bool touchMode ) {
2022-06-13 21:07:26 +08:00
if ( parent . target ? . imageModel . image = = null ) return ;
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 ;
}
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-10 19:50:48 -07:00
updateCursorData ( Map < String , dynamic > evt ) async {
2020-11-19 00:32:46 +08:00
var id = int . parse ( evt [ ' id ' ] ) ;
_hotx = double . parse ( evt [ ' hotx ' ] ) ;
_hoty = double . parse ( evt [ ' hoty ' ] ) ;
var width = int . parse ( evt [ ' width ' ] ) ;
var height = int . parse ( evt [ ' height ' ] ) ;
List < dynamic > colors = json . decode ( evt [ ' colors ' ] ) ;
final rgba = Uint8List . fromList ( colors . map ( ( s ) = > s as int ) . toList ( ) ) ;
2022-09-10 19:50:48 -07:00
final image = await img . decodeImageFromPixels (
rgba , width , height , ui . PixelFormat . rgba8888 ) ;
_image = image ;
2022-11-21 18:56:27 +08:00
if ( await _updateCache ( rgba , image , id , width , height ) ) {
2022-11-13 23:41:07 +08:00
_images [ id ] = Tuple3 ( image , _hotx , _hoty ) ;
} else {
_hotx = 0 ;
_hoty = 0 ;
}
2022-09-10 19:50:48 -07:00
try {
// my throw exception, because the listener maybe already dispose
notifyListeners ( ) ;
} catch ( e ) {
2022-11-12 22:33:10 +08:00
debugPrint ( ' WARNING: updateCursorId $ id , without notifyListeners(). $ e ' ) ;
2022-09-07 07:06:05 -07:00
}
2022-09-10 19:50:48 -07:00
}
2022-11-21 18:56:27 +08:00
Future < bool > _updateCache (
Uint8List rgba , ui . Image image , int id , int w , int h ) async {
Uint8List ? data ;
2023-02-01 22:12:28 +08:00
img2 . Image imgOrigin =
img2 . Image . fromBytes ( w , h , rgba , format: img2 . Format . rgba ) ;
2022-10-10 10:53:10 +08:00
if ( Platform . isWindows ) {
2022-11-21 18:56:27 +08:00
data = imgOrigin . getBytes ( format: img2 . Format . bgra ) ;
} 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
}
2022-10-30 13:50:44 +08:00
_cache = CursorData (
2022-09-07 07:06:05 -07:00
peerId: this . id ,
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 ,
2022-11-15 22:35:10 +08:00
hotxOrigin: _hotx ,
hotyOrigin: _hoty ,
2022-09-07 07:06:05 -07:00
width: w ,
height: h ,
) ;
2022-10-30 13:50:44 +08:00
_cacheMap [ id ] = _cache ! ;
2022-11-13 23:41:07 +08:00
return true ;
2022-09-07 07:06:05 -07:00
}
2022-09-10 19:50:48 -07:00
updateCursorId ( Map < String , dynamic > evt ) async {
final id = int . parse ( evt [ ' id ' ] ) ;
2022-10-30 13:50:44 +08:00
_cache = _cacheMap [ id ] ;
2022-09-10 19:50:48 -07:00
final tmp = _images [ id ] ;
2020-11-19 00:32:46 +08:00
if ( tmp ! = null ) {
2022-09-07 02:14:52 -07:00
_image = tmp . item1 ;
_hotx = tmp . item2 ;
_hoty = tmp . item3 ;
2020-11-19 00:32:46 +08:00
notifyListeners ( ) ;
2022-11-12 22:33:10 +08:00
} else {
debugPrint (
' 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-10 19:50:48 -07: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-06 21:20:53 -07:00
try {
2022-09-07 02:14:52 -07:00
RemoteCursorMovedState . find ( id ) . value = true ;
2022-09-06 21:20:53 -07:00
} catch ( e ) {
//
}
2020-11-19 00:32:46 +08:00
notifyListeners ( ) ;
}
2022-09-12 01:35:56 -07: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 01:35:56 -07: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 01:35:56 -07: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 07:06:05 -07:00
2022-10-30 13:50:44 +08:00
_clearCache ( ) ;
_cache = null ;
_cacheMap . clear ( ) ;
2022-09-07 07:06:05 -07: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 07:06:05 -07: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 ;
2022-08-05 20:29:43 +08:00
checkShowQualityMonitor ( String id ) async {
2022-08-16 15:22:57 +08:00
final show = await bind . sessionGetToggleOption (
2022-08-05 20:29:43 +08:00
id: id , arg: ' show-quality-monitor ' ) = =
true ;
2022-08-04 17:24:02 +08:00
if ( _show ! = show ) {
_show = show ;
notifyListeners ( ) ;
}
}
updateQualityStatus ( Map < String , dynamic > evt ) {
try {
2022-09-06 21:20:53 -07:00
if ( ( evt [ ' speed ' ] as String ) . isNotEmpty ) _data . speed = evt [ ' speed ' ] ;
if ( ( evt [ ' fps ' ] as String ) . isNotEmpty ) _data . fps = evt [ ' fps ' ] ;
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 07:06:05 -07: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 11:27:34 +09:00
if ( isIOS | | ! _start ) return ;
2022-09-15 17:31:28 +08:00
var id = parent . target ? . id ;
int ? width = parent . target ? . canvasModel . getDisplayWidth ( ) ;
2022-10-25 11:27:34 +09:00
int ? height = parent . target ? . canvasModel . getDisplayHeight ( ) ;
2022-09-15 17:31:28 +08:00
if ( id = = null | | width = = null | | height = = null ) return ;
2022-09-21 16:03:08 +08:00
bind . sessionRecordScreen ( id: id , start: true , width: width , height: height ) ;
2022-09-15 17:31:28 +08:00
}
2022-09-21 16:03:08 +08:00
toggle ( ) {
2022-10-25 11:27:34 +09:00
if ( isIOS ) return ;
2022-09-15 17:31:28 +08:00
var id = parent . target ? . id ;
2022-09-21 16:03:08 +08:00
if ( id = = null ) return ;
2022-09-15 17:31:28 +08:00
_start = ! _start ;
notifyListeners ( ) ;
if ( _start ) {
2022-09-21 16:03:08 +08:00
bind . sessionRefresh ( id: id ) ;
} else {
bind . sessionRecordScreen ( id: id , start: false , width: 0 , height: 0 ) ;
2022-09-15 17:31:28 +08:00
}
}
2022-09-21 16:03:08 +08:00
onClose ( ) {
2022-10-25 11:27:34 +09:00
if ( isIOS ) return ;
2022-09-21 16:03:08 +08:00
var id = parent . target ? . id ;
if ( id = = null ) return ;
_start = false ;
bind . sessionRecordScreen ( id: id , start: false , width: 0 , height: 0 ) ;
}
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 ;
}
onPortableServiceRunning ( Map < String , dynamic > evt ) {
_running = evt [ ' running ' ] = = ' true ' ;
}
}
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-06 21:20:53 -07:00
var id = ' ' ;
var version = ' ' ;
2022-09-06 19:08:45 +08:00
var connType = ConnType . defaultConn ;
2022-08-12 18:42:02 +08:00
/// dialogManager use late to ensure init after main page binding [globalKey]
late final dialogManager = OverlayDialogManager ( ) ;
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
2022-06-13 21:07:26 +08:00
FFI ( ) {
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 ) ) ;
2022-06-13 21:07:26 +08:00
}
2020-11-19 00:32:46 +08:00
2022-09-27 20:35:02 +08:00
/// Start with the given [id]. Only transfer file if [isFileTransfer], only port forward if [isPortForward].
void start ( String id ,
2023-01-17 13:28:33 +08:00
{ bool isFileTransfer = false ,
bool isPortForward = false ,
2023-02-13 16:40:24 +08:00
String ? switchUuid ,
bool ? forceRelay } ) {
2022-09-06 21:20:53 -07: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 ;
id = ' ft_ $ id ' ;
2022-08-26 11:35:28 +08:00
} else if ( isPortForward ) {
2022-09-06 19:08:45 +08:00
connType = ConnType . portForward ;
id = ' pf_ $ id ' ;
2022-08-26 11:35:28 +08:00
} else {
2022-06-13 21:07:26 +08:00
chatModel . resetClientMode ( ) ;
canvasModel . id = id ;
2022-09-13 21:52:22 +08:00
imageModel . id = id ;
2022-06-13 21:07:26 +08:00
cursorModel . id = id ;
2022-06-21 17:58:27 +08:00
}
2022-08-30 21:15:35 +08:00
// ignore: unused_local_variable
final addRes = bind . sessionAddSync (
2023-02-13 16:40:24 +08:00
id: id ,
isFileTransfer: isFileTransfer ,
isPortForward: isPortForward ,
switchUuid: switchUuid ? ? " " ,
forceRelay: forceRelay ? ? false ) ;
2022-08-30 21:15:35 +08:00
final stream = bind . sessionStart ( id: id ) ;
2022-06-21 17:58:27 +08:00
final cb = ffiModel . startEventListener ( id ) ;
( ) async {
2023-02-21 23:46:13 +08:00
final useTextureRender = bind . mainUseTextureRender ( ) ;
2023-02-12 01:52:11 +08:00
// Preserved for the rgba data.
2022-06-21 17:58:27 +08:00
await for ( final message in stream ) {
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 " ) {
break ;
}
2022-06-21 17:58:27 +08:00
try {
Map < String , dynamic > event = json . decode ( message . field0 ) ;
2022-09-11 19:52:38 -07:00
await cb ( event ) ;
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-01-19 21:21:28 +08:00
} else if ( message is EventToUI_Rgba ) {
2023-02-21 23:46:13 +08:00
if ( useTextureRender ) {
2023-02-21 21:56:46 +08:00
if ( _waitForImage [ id ] ! ) {
_waitForImage [ id ] = false ;
dialogManager . dismissAll ( ) ;
for ( final cb in imageModel . callbacksOnFirstImage ) {
cb ( id ) ;
}
await canvasModel . updateViewStyle ( ) ;
await canvasModel . updateScrollStyle ( ) ;
}
2023-02-21 23:46:13 +08:00
} else {
// Fetch the image buffer from rust codes.
final sz = platformFFI . getRgbaSize ( id ) ;
if ( sz = = null | | sz = = 0 ) {
return ;
}
final rgba = platformFFI . getRgba ( id , sz ) ;
if ( rgba ! = null ) {
imageModel . onRgba ( rgba ) ;
}
2023-02-21 21:56:46 +08:00
}
2022-05-31 17:36:36 +08:00
}
2022-06-21 17:58:27 +08:00
}
2023-02-15 21:27:50 +08:00
debugPrint ( ' Exit session event loop ' ) ;
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 ;
2022-06-21 17:58:27 +08:00
if ( isFileTransfer ) {
2022-09-06 19:08:45 +08:00
fileModel . initFileFetcher ( ) ;
2022-06-21 17:58:27 +08:00
}
2020-11-19 00:32:46 +08:00
}
2022-05-28 03:56:42 +08:00
/// Login with [password], choose if the client should [remember] it.
2022-09-27 20:35:02 +08:00
void login ( String id , String password , bool remember ) {
2022-06-13 21:07:26 +08:00
bind . sessionLogin ( id: id , password: password , remember: remember ) ;
2020-11-19 00:32:46 +08:00
}
2022-05-28 03:56:42 +08:00
/// Close the remote session.
2022-06-28 22:04:10 +08:00
Future < void > close ( ) async {
2022-03-25 16:34:27 +08:00
chatModel . close ( ) ;
2022-06-13 21:07:26 +08:00
if ( imageModel . image ! = null & & ! isWebDesktop ) {
2022-11-10 21:25:12 +08:00
await setCanvasConfig ( id , cursorModel . x , cursorModel . y , canvasModel . x ,
2022-03-07 22:54:34 +08:00
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-02-10 21:18:55 +08:00
await bind . sessionClose ( id: id ) ;
2022-09-06 21:20:53 -07:00
debugPrint ( ' model $ id closed ' ) ;
2023-02-08 10:11:53 +09: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
}
}
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 ;
2022-09-11 19:52:38 -07: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 ;
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 ;
}
2020-11-19 00:32:46 +08:00
class PeerInfo {
2022-09-06 21:20:53 -07:00
String version = ' ' ;
String username = ' ' ;
String hostname = ' ' ;
String platform = ' ' ;
2022-02-17 15:22:14 +08:00
bool sasEnabled = false ;
int currentDisplay = 0 ;
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 = [ ] ;
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 ' ;
Future < void > setCanvasConfig ( String id , double xCursor , double yCursor ,
2022-09-27 20:35:02 +08:00
double xCanvas , double yCanvas , double scale , int currentDisplay ) async {
2022-09-01 06:18:29 -07: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 ;
2022-11-10 21:25:12 +08:00
await bind . sessionSetFlutterConfig ( id: id , k: canvasKey , v: jsonEncode ( p ) ) ;
2020-12-21 18:28:28 +08:00
}
2022-11-10 21:25:12 +08:00
Future < Map < String , dynamic > ? > getCanvasConfig ( String id ) async {
2022-05-23 16:02:37 +08:00
if ( ! isWebDesktop ) return null ;
2022-11-10 21:25:12 +08:00
var p = await bind . sessionGetFlutterConfig ( id: id , k: canvasKey ) ;
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
void removePreference ( String id ) async {
2022-11-10 21:25:12 +08:00
await bind . sessionSetFlutterConfig ( id: id , k: canvasKey , v: ' ' ) ;
2020-12-21 19:05:31 +08:00
}
2022-09-27 20:35:02 +08:00
Future < void > initializeCursorAndCanvas ( FFI ffi ) async {
2022-11-10 21:25:12 +08:00
var p = await getCanvasConfig ( ffi . id ) ;
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 ) {
ffi . cursorModel
. updateDisplayOrigin ( ffi . ffiModel . display . x , ffi . ffiModel . display . y ) ;
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 ' ] ;
2022-06-13 21:07:26 +08:00
ffi . cursorModel . updateDisplayOriginWithCursor (
ffi . ffiModel . display . x , ffi . ffiModel . display . y , xCursor , yCursor ) ;
ffi . canvasModel . update ( xCanvas , yCanvas , scale ) ;
2020-12-21 18:28:28 +08:00
}