2020-11-28 13:22:19 +08:00
import ' package:flutter/services.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-03-19 23:28:29 +08:00
import ' package:flutter_hbb/models/server_model.dart ' ;
2022-04-19 13:07:45 +08:00
import ' package:flutter_smart_dialog/flutter_smart_dialog.dart ' ;
2020-12-21 18:28:28 +08:00
import ' package:shared_preferences/shared_preferences.dart ' ;
2020-11-24 12:11:55 +08:00
import ' dart:math ' ;
2020-11-19 00:32:46 +08:00
import ' dart:convert ' ;
import ' dart:typed_data ' ;
import ' dart:ui ' as ui ;
import ' package:flutter/material.dart ' ;
2020-11-23 23:18:42 +08:00
import ' package:tuple/tuple.dart ' ;
2020-11-19 00:32:46 +08:00
import ' dart:async ' ;
2022-02-28 18:29:25 +08:00
import ' ../common.dart ' ;
2022-03-28 17:24:52 +08:00
import ' ../widgets/dialog.dart ' ;
2022-04-28 22:44:54 +08:00
import ' ../widgets/overlay.dart ' ;
2022-01-26 12:48:16 +08:00
import ' native_model.dart ' if ( dart . library . html ) ' web_model.dart ' ;
2020-11-19 00:32:46 +08:00
2022-03-19 23:28:29 +08:00
typedef HandleMsgBox = void Function ( Map < String , dynamic > evt , String id ) ;
2022-05-19 23:45:44 +08:00
bool _waitForImage = false ;
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-02-02 00:46:21 +08:00
var _inputBlocked = false ;
2020-11-22 21:08:19 +08:00
final _permissions = Map < 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 ;
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 ;
bool get isPeerAndroid = > _pi . platform = = " Android " ;
2022-02-02 00:46:21 +08:00
set inputBlocked ( v ) {
_inputBlocked = v ;
}
2020-11-19 00:32:46 +08:00
FfiModel ( ) {
2021-08-02 20:54:56 +08:00
Translator . call = translate ;
2020-11-22 21:08:19 +08:00
clear ( ) ;
2022-04-17 00:44:05 +08:00
}
Future < void > init ( ) async {
await PlatformFFI . init ( ) ;
2020-11-19 00:32:46 +08:00
}
2022-04-28 22:44:54 +08:00
void toggleTouchMode ( ) {
if ( ! isPeerAndroid ) {
_touchMode = ! _touchMode ;
notifyListeners ( ) ;
}
}
2020-11-22 21:08:19 +08:00
void updatePermission ( Map < String , dynamic > evt ) {
evt . forEach ( ( k , v ) {
if ( k = = ' name ' ) return ;
_permissions [ k ] = v = = ' true ' ;
} ) ;
2020-11-23 17:45:38 +08:00
print ( ' $ _permissions ' ) ;
2022-02-06 16:29:56 +08:00
notifyListeners ( ) ;
2020-11-19 00:32:46 +08:00
}
2022-03-28 15:44:45 +08:00
void updateUser ( ) {
notifyListeners ( ) ;
}
2020-11-24 23:36:46 +08:00
bool keyboard ( ) = > _permissions [ ' keyboard ' ] ! = false ;
2020-11-23 23:18:42 +08:00
2020-11-19 00:32:46 +08:00
void clear ( ) {
2020-11-22 21:08:19 +08:00
_pi = PeerInfo ( ) ;
_display = Display ( ) ;
_waitForImage = false ;
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 ( ) ;
}
2020-11-29 00:13:55 +08:00
void setConnectionType ( bool secure , bool direct ) {
_secure = secure ;
_direct = direct ;
}
2022-02-17 15:22:14 +08:00
Image ? getConnectionImage ( ) {
String ? icon ;
2020-11-29 00:13:55 +08:00
if ( secure = = true & & direct = = true ) {
icon = ' secure ' ;
} else if ( secure = = false & & direct = = true ) {
icon = ' insecure ' ;
} else if ( secure = = false & & direct = = false ) {
icon = ' insecure_relay ' ;
} else if ( secure = = true & & direct = = false ) {
icon = ' secure_relay ' ;
}
return icon = = null
? null
: Image . asset ( ' assets/ $ icon .png ' , width: 48 , height: 48 ) ;
}
2020-11-28 13:22:19 +08:00
void 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-05-17 19:59:37 +08:00
void updateEventListener ( String peerId ) {
final void Function ( Map < String , dynamic > ) cb = ( evt ) {
2020-11-19 00:32:46 +08:00
var name = evt [ ' name ' ] ;
if ( name = = ' msgbox ' ) {
2022-03-19 23:28:29 +08:00
handleMsgBox ( evt , peerId ) ;
2020-11-19 00:32:46 +08:00
} else if ( name = = ' peer_info ' ) {
2022-03-19 23:28:29 +08:00
handlePeerInfo ( evt ) ;
2020-11-29 00:13:55 +08:00
} else if ( name = = ' connection_ready ' ) {
FFI . ffiModel . setConnectionType (
evt [ ' secure ' ] = = ' true ' , evt [ ' direct ' ] = = ' true ' ) ;
2020-11-19 00:32:46 +08:00
} else if ( name = = ' switch_display ' ) {
handleSwitchDisplay ( evt ) ;
} else if ( name = = ' cursor_data ' ) {
FFI . cursorModel . updateCursorData ( evt ) ;
} else if ( name = = ' cursor_id ' ) {
FFI . cursorModel . updateCursorId ( evt ) ;
} else if ( name = = ' cursor_position ' ) {
2022-05-19 23:45:44 +08:00
FFI . cursorModel . updateCursorPosition ( evt ) ;
2020-11-28 13:22:19 +08:00
} else if ( name = = ' clipboard ' ) {
Clipboard . setData ( ClipboardData ( text: evt [ ' content ' ] ) ) ;
2020-11-22 21:08:19 +08:00
} else if ( name = = ' permission ' ) {
FFI . ffiModel . updatePermission ( evt ) ;
2022-03-22 16:40:23 +08:00
} else if ( name = = ' chat_client_mode ' ) {
2022-03-28 15:44:45 +08:00
FFI . chatModel . receive ( ChatModel . clientModeID , evt [ ' text ' ] ? ? " " ) ;
2022-03-22 16:40:23 +08:00
} else if ( name = = ' chat_server_mode ' ) {
2022-03-28 15:44:45 +08:00
FFI . chatModel
. receive ( int . parse ( evt [ ' id ' ] as String ) , evt [ ' text ' ] ? ? " " ) ;
2022-03-07 22:54:34 +08:00
} else if ( name = = ' file_dir ' ) {
2022-03-16 15:33:00 +08:00
FFI . fileModel . receiveFileDir ( evt ) ;
2022-03-19 23:28:29 +08:00
} else if ( name = = ' job_progress ' ) {
2022-03-11 01:28:13 +08:00
FFI . fileModel . tryUpdateJobProgress ( evt ) ;
2022-03-19 23:28:29 +08:00
} else if ( name = = ' job_done ' ) {
2022-03-11 01:28:13 +08:00
FFI . fileModel . jobDone ( evt ) ;
2022-03-19 23:28:29 +08:00
} else if ( name = = ' job_error ' ) {
2022-03-11 01:28:13 +08:00
FFI . fileModel . jobError ( evt ) ;
2022-05-17 20:56:36 +08:00
} else if ( name = = ' override_file_confirm ' ) {
FFI . fileModel . overrideFileConfirm ( evt ) ;
2022-03-19 23:28:29 +08:00
} else if ( name = = ' try_start_without_auth ' ) {
FFI . serverModel . loginRequest ( evt ) ;
2022-03-22 21:47:42 +08:00
} else if ( name = = ' on_client_authorized ' ) {
FFI . serverModel . onClientAuthorized ( evt ) ;
2022-03-19 23:28:29 +08:00
} else if ( name = = ' on_client_remove ' ) {
FFI . serverModel . onClientRemove ( evt ) ;
2020-11-19 00:32:46 +08:00
}
2022-05-17 19:59:37 +08:00
} ;
PlatformFFI . setEventCallback ( cb ) ;
2020-11-19 00:32:46 +08:00
}
void handleSwitchDisplay ( Map < String , dynamic > evt ) {
2022-06-02 17:16:23 +08:00
final oldOrientation = _display . width > _display . height ;
2020-12-24 10:44:44 +08:00
var old = _pi . currentDisplay ;
2020-11-19 00:32:46 +08:00
_pi . currentDisplay = int . parse ( evt [ ' display ' ] ) ;
_display . x = double . parse ( evt [ ' x ' ] ) ;
_display . y = double . parse ( evt [ ' y ' ] ) ;
_display . width = int . parse ( evt [ ' width ' ] ) ;
_display . height = int . parse ( evt [ ' height ' ] ) ;
2020-12-24 10:44:44 +08:00
if ( old ! = _pi . currentDisplay )
FFI . cursorModel . updateDisplayOrigin ( _display . x , _display . y ) ;
2022-06-02 17:16:23 +08:00
// remote is mobile, and orientation changed
if ( ( _display . width > _display . height ) ! = oldOrientation ) {
FFI . canvasModel . updateViewStyle ( ) ;
}
2020-11-25 23:52:58 +08:00
notifyListeners ( ) ;
2020-11-19 00:32:46 +08:00
}
2022-03-28 17:24:52 +08:00
void handleMsgBox ( Map < String , dynamic > evt , String id ) {
var type = evt [ ' type ' ] ;
var title = evt [ ' title ' ] ;
var text = evt [ ' text ' ] ;
if ( type = = ' re-input-password ' ) {
wrongPasswordDialog ( id ) ;
} else if ( type = = ' input-password ' ) {
enterPasswordDialog ( id ) ;
} else {
var hasRetry = evt [ ' hasRetry ' ] = = ' true ' ;
showMsgBox ( type , title , text , hasRetry ) ;
}
}
void showMsgBox ( String type , String title , String text , bool hasRetry ) {
msgBox ( type , title , text ) ;
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 ) , ( ) {
FFI . reconnect ( ) ;
showLoading ( translate ( ' Connecting... ' ) ) ;
} ) ;
_reconnects * = 2 ;
} else {
_reconnects = 1 ;
}
}
2022-03-19 23:28:29 +08:00
void handlePeerInfo ( Map < String , dynamic > evt ) {
2022-04-19 13:07:45 +08:00
SmartDialog . dismiss ( ) ;
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 ' ] ;
_pi . sasEnabled = evt [ ' sas_enabled ' ] = = " true " ;
_pi . currentDisplay = int . parse ( evt [ ' current_display ' ] ) ;
2022-03-16 15:33:00 +08:00
2022-04-28 22:44:54 +08:00
if ( isPeerAndroid ) {
_touchMode = true ;
if ( FFI . ffiModel . permissions [ ' keyboard ' ] ! = false ) {
2022-05-16 00:01:27 +08:00
Timer ( Duration ( milliseconds: 100 ) , showMobileActionsOverlay ) ;
2022-04-28 22:44:54 +08:00
}
} else {
_touchMode = FFI . getByName ( ' peer_option ' , " touch-mode " ) ! = ' ' ;
}
2022-03-19 23:28:29 +08:00
if ( evt [ ' is_file_transfer ' ] = = " true " ) {
2022-03-16 15:33:00 +08:00
FFI . 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 ' ] ;
_pi . displays . add ( d ) ;
}
if ( _pi . currentDisplay < _pi . displays . length ) {
_display = _pi . displays [ _pi . currentDisplay ] ;
}
if ( displays . length > 0 ) {
showLoading ( translate ( ' Connected, waiting for image... ' ) ) ;
_waitForImage = true ;
2022-04-26 21:21:08 +08:00
_reconnects = 1 ;
2022-03-16 15:33:00 +08:00
}
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
}
}
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-05-19 23:45:44 +08:00
ImageModel ( ) {
PlatformFFI . setRgbaCallback ( ( rgba ) {
if ( _waitForImage ) {
_waitForImage = false ;
SmartDialog . dismiss ( ) ;
}
final pid = FFI . id ;
ui . decodeImageFromPixels (
rgba ,
FFI . ffiModel . display . width ,
FFI . ffiModel . display . height ,
isWeb ? ui . PixelFormat . rgba8888 : ui . PixelFormat . bgra8888 , ( image ) {
if ( FFI . id ! = pid ) return ;
try {
// my throw exception, because the listener maybe already dispose
FFI . imageModel . update ( image ) ;
} catch ( e ) {
print ( ' update image: $ e ' ) ;
}
} ) ;
} ) ;
}
2022-02-17 15:22:14 +08:00
void update ( ui . Image ? image ) {
2020-11-24 22:03:04 +08:00
if ( _image = = null & & image ! = null ) {
2022-02-03 00:53:59 +08:00
if ( isDesktop ) {
FFI . canvasModel . updateViewStyle ( ) ;
} else {
2022-03-07 22:54:34 +08:00
final size = MediaQueryData . fromWindow ( ui . window ) . size ;
2022-02-03 00:53:59 +08:00
final xscale = size . width / image . width ;
final yscale = size . height / image . height ;
FFI . canvasModel . scale = max ( xscale , yscale ) ;
}
2022-01-31 16:22:05 +08:00
initializeCursorAndCanvas ( ) ;
2022-05-10 14:44:47 +08:00
Future . delayed ( Duration ( milliseconds: 1 ) , ( ) {
if ( FFI . ffiModel . isPeerAndroid ) {
FFI . setByName (
' peer_option ' , ' {"name": "view-style", "value": "shrink"} ' ) ;
FFI . canvasModel . updateViewStyle ( ) ;
}
} ) ;
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
double get maxScale {
if ( _image = = null ) return 1.0 ;
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 ;
2020-11-24 12:11:55 +08:00
return max ( 1.0 , max ( xscale , yscale ) ) ;
}
double get minScale {
if ( _image = = null ) return 1.0 ;
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 ;
2020-11-24 12:11:55 +08:00
return min ( xscale , yscale ) ;
}
2020-11-19 00:32:46 +08:00
}
2020-11-23 23:18:42 +08:00
class CanvasModel with ChangeNotifier {
2022-02-17 15:22:14 +08:00
double _x = 0 ;
double _y = 0 ;
double _scale = 1.0 ;
2020-11-23 23:18:42 +08:00
2022-02-17 15:22:14 +08:00
CanvasModel ( ) ;
2020-11-23 23:18:42 +08:00
double get x = > _x ;
2022-02-02 17:25:56 +08:00
2020-11-23 23:18:42 +08:00
double get y = > _y ;
2022-02-02 17:25:56 +08:00
2020-11-23 23:18:42 +08:00
double get scale = > _scale ;
2022-02-03 00:53:59 +08:00
void updateViewStyle ( ) {
final s = FFI . getByName ( ' peer_option ' , ' view-style ' ) ;
2022-03-07 22:54:34 +08:00
final size = MediaQueryData . fromWindow ( ui . window ) . size ;
2022-02-03 00:53:59 +08:00
final s1 = size . width / FFI . ffiModel . display . width ;
final s2 = size . height / FFI . ffiModel . display . height ;
if ( s = = ' shrink ' ) {
final s = s1 < s2 ? s1 : s2 ;
if ( s < 1 ) {
_scale = s ;
}
} else if ( s = = ' stretch ' ) {
final s = s1 > s2 ? s1 : s2 ;
if ( s > 1 ) {
_scale = s ;
}
} else {
_scale = 1 ;
}
_x = ( size . width - FFI . ffiModel . display . width * _scale ) / 2 ;
_y = ( size . height - FFI . ffiModel . display . height * _scale ) / 2 ;
notifyListeners ( ) ;
}
2020-12-21 18:28:28 +08:00
void update ( double x , double y , double scale ) {
_x = x ;
_y = y ;
_scale = scale ;
notifyListeners ( ) ;
}
2022-02-06 16:29:56 +08:00
void moveDesktopMouse ( double x , double y ) {
2022-03-07 22:54:34 +08:00
final size = MediaQueryData . fromWindow ( ui . window ) . size ;
2022-02-06 16:29:56 +08:00
final dw = FFI . ffiModel . display . width * _scale ;
final dh = FFI . ffiModel . display . height * _scale ;
var dxOffset = 0 ;
var dyOffset = 0 ;
if ( dw > size . width ) {
dxOffset = ( x - dw * ( x / size . width ) - _x ) . toInt ( ) ;
}
if ( dh > size . height ) {
dyOffset = ( y - dh * ( y / size . height ) - _y ) . toInt ( ) ;
}
_x + = dxOffset ;
_y + = dyOffset ;
if ( dxOffset ! = 0 | | dyOffset ! = 0 ) {
notifyListeners ( ) ;
}
2022-02-17 18:00:44 +08:00
FFI . cursorModel . moveLocal ( x , y ) ;
2022-02-06 16:29:56 +08:00
}
2020-11-24 12:11:55 +08:00
set scale ( v ) {
_scale = v ;
notifyListeners ( ) ;
}
2020-11-24 22:03:04 +08:00
void panX ( double dx ) {
_x + = dx ;
notifyListeners ( ) ;
2020-11-23 23:18:42 +08:00
}
2020-11-27 17:59:42 +08:00
void resetOffset ( ) {
2022-02-03 00:53:59 +08:00
if ( isDesktop ) {
updateViewStyle ( ) ;
} else {
_x = 0 ;
_y = 0 ;
}
2020-11-27 17:59:42 +08:00
notifyListeners ( ) ;
}
2020-11-24 22:03:04 +08:00
void panY ( double dy ) {
2020-11-23 23:18:42 +08:00
_y + = dy ;
notifyListeners ( ) ;
}
void updateScale ( double v ) {
2020-11-27 10:52:09 +08:00
if ( FFI . imageModel . image = = null ) return ;
2020-11-25 14:41:57 +08:00
final offset = FFI . cursorModel . offset ;
var r = FFI . cursorModel . getVisibleRect ( ) ;
final px0 = ( offset . dx - r . left ) * _scale ;
final py0 = ( offset . dy - r . top ) * _scale ;
2020-11-23 23:18:42 +08:00
_scale * = v ;
2020-11-24 12:11:55 +08:00
final maxs = FFI . imageModel . maxScale ;
final mins = FFI . imageModel . minScale ;
if ( _scale > maxs ) _scale = maxs ;
if ( _scale < mins ) _scale = mins ;
2020-11-25 14:41:57 +08:00
r = FFI . cursorModel . getVisibleRect ( ) ;
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-02-02 17:25:56 +08:00
void 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
}
}
2020-11-19 00:32:46 +08:00
class CursorModel with ChangeNotifier {
2022-02-17 15:22:14 +08:00
ui . Image ? _image ;
2020-11-23 23:18:42 +08:00
final _images = Map < int , Tuple3 < ui . Image , double , double > > ( ) ;
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 ;
2022-02-17 15:22:14 +08:00
ui . Image ? get image = > _image ;
2022-02-02 17:25:56 +08:00
2020-11-23 23:18:42 +08:00
double get x = > _x - _displayOriginX ;
2022-02-02 17:25:56 +08:00
2020-11-23 23:18:42 +08:00
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 ;
2022-02-02 17:25:56 +08:00
2020-11-23 23:18:42 +08:00
double get hoty = > _hoty ;
2020-11-19 00:32:46 +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 ;
2020-11-24 22:03:04 +08:00
final xoffset = FFI . canvasModel . x ;
final yoffset = FFI . canvasModel . y ;
final scale = FFI . canvasModel . scale ;
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 ;
2020-11-25 14:41:57 +08:00
final s = FFI . canvasModel . scale ;
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-02-17 18:00:44 +08:00
void touch ( double x , double y , MouseButtons button ) {
moveLocal ( x , y ) ;
2022-02-06 16:29:56 +08:00
FFI . moveMouse ( _x , _y ) ;
2022-02-17 18:00:44 +08:00
FFI . tap ( button ) ;
2022-02-06 16:29:56 +08:00
}
2022-03-07 22:54:34 +08:00
void move ( double x , double y ) {
2022-02-17 18:00:44 +08:00
moveLocal ( x , y ) ;
FFI . moveMouse ( _x , _y ) ;
}
void moveLocal ( double x , double y ) {
2021-08-21 17:18:14 +08:00
final scale = FFI . canvasModel . scale ;
final xoffset = FFI . canvasModel . x ;
final yoffset = FFI . canvasModel . y ;
_x = ( x - xoffset ) / scale + _displayOriginX ;
_y = ( y - yoffset ) / scale + _displayOriginY ;
notifyListeners ( ) ;
}
void reset ( ) {
_x = _displayOriginX ;
_y = _displayOriginY ;
FFI . moveMouse ( _x , _y ) ;
2021-08-22 07:50:12 +08:00
FFI . canvasModel . clear ( true ) ;
2021-08-21 17:18:14 +08:00
notifyListeners ( ) ;
}
2022-03-07 22:54:34 +08:00
void updatePan ( double dx , double dy , bool touchMode ) {
2020-11-27 10:52:09 +08:00
if ( FFI . imageModel . image = = null ) return ;
2021-08-21 17:18:14 +08:00
if ( touchMode ) {
2022-04-19 13:07:45 +08:00
final scale = FFI . canvasModel . scale ;
_x + = dx / scale ;
_y + = dy / scale ;
FFI . moveMouse ( _x , _y ) ;
notifyListeners ( ) ;
2021-08-21 17:18:14 +08:00
return ;
}
2020-11-25 00:13:23 +08:00
final scale = FFI . canvasModel . scale ;
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 +
FFI . imageModel . image ! . width -
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 +
FFI . imageModel . image ! . height -
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 ) {
2020-11-24 23:36:46 +08:00
FFI . canvasModel . panX ( - dx ) ;
2020-11-24 22:03:04 +08:00
}
if ( tryMoveCanvasY & & dy ! = 0 ) {
2020-11-24 23:36:46 +08:00
FFI . canvasModel . panY ( - dy ) ;
2020-11-24 22:03:04 +08:00
}
2020-11-25 00:13:23 +08:00
FFI . moveMouse ( _x , _y ) ;
2020-11-24 22:03:04 +08:00
notifyListeners ( ) ;
}
2020-11-19 00:32:46 +08:00
void updateCursorData ( Map < String , dynamic > evt ) {
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 ( ) ) ;
2020-11-28 17:42:29 +08:00
var pid = FFI . id ;
2020-11-19 00:32:46 +08:00
ui . decodeImageFromPixels ( rgba , width , height , ui . PixelFormat . rgba8888 ,
2022-03-07 22:54:34 +08:00
( image ) {
if ( FFI . id ! = pid ) return ;
_image = image ;
_images [ id ] = Tuple3 ( image , _hotx , _hoty ) ;
try {
// my throw exception, because the listener maybe already dispose
notifyListeners ( ) ;
} catch ( e ) {
print ( ' notify cursor: $ e ' ) ;
}
} ) ;
2020-11-19 00:32:46 +08:00
}
void updateCursorId ( Map < String , dynamic > evt ) {
final tmp = _images [ int . parse ( evt [ ' id ' ] ) ] ;
if ( tmp ! = null ) {
2020-11-23 23:18:42 +08:00
_image = tmp . item1 ;
_hotx = tmp . item2 ;
_hoty = tmp . item3 ;
2020-11-19 00:32:46 +08:00
notifyListeners ( ) ;
}
}
void updateCursorPosition ( Map < String , dynamic > evt ) {
_x = double . parse ( evt [ ' x ' ] ) ;
_y = double . parse ( evt [ ' y ' ] ) ;
notifyListeners ( ) ;
}
void updateDisplayOrigin ( double x , double y ) {
_displayOriginX = x ;
_displayOriginY = y ;
2020-12-21 21:52:20 +08:00
_x = x + 1 ;
_y = y + 1 ;
2020-11-24 23:36:46 +08:00
FFI . moveMouse ( x , y ) ;
2020-11-27 22:50:24 +08:00
FFI . canvasModel . resetOffset ( ) ;
2020-11-19 00:32:46 +08:00
notifyListeners ( ) ;
}
2022-03-07 22:54:34 +08:00
void updateDisplayOriginWithCursor (
double x , double y , double xCursor , double yCursor ) {
2020-12-21 18:28:28 +08:00
_displayOriginX = x ;
_displayOriginY = y ;
_x = xCursor ;
_y = yCursor ;
FFI . moveMouse ( x , y ) ;
notifyListeners ( ) ;
}
2020-11-19 00:32:46 +08:00
void 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-03-07 22:54:34 +08:00
enum MouseButtons { left , right , wheel }
2022-02-17 18:00:44 +08:00
2022-03-07 22:54:34 +08:00
extension ToString on MouseButtons {
2022-02-17 18:00:44 +08:00
String get value {
switch ( this ) {
case MouseButtons . left:
return " left " ;
case MouseButtons . right:
return " right " ;
case MouseButtons . wheel:
return " wheel " ;
}
}
}
2020-11-19 00:32:46 +08:00
class FFI {
2022-02-28 21:26:44 +08:00
static var id = " " ;
2020-11-25 16:28:46 +08:00
static var shift = false ;
static var ctrl = false ;
static var alt = false ;
static var command = false ;
2022-02-28 21:26:44 +08:00
static var version = " " ;
2020-11-19 00:32:46 +08:00
static final imageModel = ImageModel ( ) ;
static final ffiModel = FfiModel ( ) ;
static final cursorModel = CursorModel ( ) ;
2020-11-23 23:18:42 +08:00
static final canvasModel = CanvasModel ( ) ;
2022-02-10 02:07:53 +08:00
static final serverModel = ServerModel ( ) ;
2022-03-03 14:58:57 +08:00
static final chatModel = ChatModel ( ) ;
2022-03-09 22:43:05 +08:00
static final fileModel = FileModel ( ) ;
2020-11-19 00:32:46 +08:00
static String getId ( ) {
return getByName ( ' remote_id ' ) ;
}
2022-02-17 18:00:44 +08:00
static void tap ( MouseButtons button ) {
sendMouse ( ' down ' , button ) ;
sendMouse ( ' up ' , button ) ;
2020-11-25 17:02:27 +08:00
}
2022-05-12 20:05:59 +08:00
static void scroll ( int y ) {
2020-11-25 17:02:27 +08:00
setByName ( ' send_mouse ' ,
2022-05-12 20:05:59 +08:00
json . encode ( modify ( { ' type ' : ' wheel ' , ' y ' : y . toString ( ) } ) ) ) ;
2020-11-25 16:28:46 +08:00
}
2020-11-28 13:22:19 +08:00
static void reconnect ( ) {
setByName ( ' reconnect ' ) ;
FFI . ffiModel . clearPermissions ( ) ;
}
2020-11-25 16:28:46 +08:00
static void resetModifiers ( ) {
shift = ctrl = alt = command = false ;
}
static Map < String , String > modify ( Map < String , String > evt ) {
if ( ctrl ) evt [ ' ctrl ' ] = ' true ' ;
if ( shift ) evt [ ' shift ' ] = ' true ' ;
if ( alt ) evt [ ' alt ' ] = ' true ' ;
if ( command ) evt [ ' command ' ] = ' true ' ;
return evt ;
2020-11-24 12:11:55 +08:00
}
2022-02-17 18:00:44 +08:00
static void sendMouse ( String type , MouseButtons button ) {
2020-11-25 16:28:46 +08:00
if ( ! ffiModel . keyboard ( ) ) return ;
2022-03-07 22:54:34 +08:00
setByName ( ' send_mouse ' ,
json . encode ( modify ( { ' type ' : type , ' buttons ' : button . value } ) ) ) ;
2020-11-25 16:28:46 +08:00
}
2022-05-07 18:04:00 +08:00
static void inputKey ( String name , { bool ? down , bool ? press } ) {
2020-11-25 16:28:46 +08:00
if ( ! ffiModel . keyboard ( ) ) return ;
2022-02-04 11:52:54 +08:00
setByName (
2022-04-25 18:25:25 +08:00
' input_key ' ,
json . encode ( modify ( {
' name ' : name ,
' down ' : ( down ? ? false ) . toString ( ) ,
2022-05-07 18:04:00 +08:00
' press ' : ( press ? ? true ) . toString ( )
2022-04-25 18:25:25 +08:00
} ) ) ) ;
2020-11-24 12:11:55 +08:00
}
2020-11-24 23:36:46 +08:00
static void moveMouse ( double x , double y ) {
2020-11-25 16:28:46 +08:00
if ( ! ffiModel . keyboard ( ) ) return ;
2020-11-24 23:36:46 +08:00
var x2 = x . toInt ( ) ;
var y2 = y . toInt ( ) ;
2020-11-25 16:28:46 +08:00
setByName ( ' send_mouse ' , json . encode ( modify ( { ' x ' : ' $ x2 ' , ' y ' : ' $ y2 ' } ) ) ) ;
2020-11-24 23:36:46 +08:00
}
2020-11-19 00:32:46 +08:00
static List < Peer > peers ( ) {
try {
2022-04-14 11:00:24 +08:00
var str = getByName ( ' peers ' ) ;
if ( str = = " " ) return [ ] ;
List < dynamic > peers = json . decode ( str ) ;
2020-11-19 00:32:46 +08:00
return peers
. map ( ( s ) = > s as List < dynamic > )
. map ( ( s ) = >
2022-03-07 22:54:34 +08:00
Peer . fromJson ( s [ 0 ] as String , s [ 1 ] as Map < String , dynamic > ) )
2020-11-19 00:32:46 +08:00
. toList ( ) ;
} catch ( e ) {
2020-11-26 20:28:37 +08:00
print ( ' peers(): $ e ' ) ;
2020-11-19 00:32:46 +08:00
}
return [ ] ;
}
2022-03-07 22:54:34 +08:00
static void connect ( String id , { bool isFileTransfer = false } ) {
if ( isFileTransfer ) {
setByName ( ' connect_file_transfer ' , id ) ;
} else {
2022-05-16 00:01:27 +08:00
FFI . chatModel . resetClientMode ( ) ;
2022-03-07 22:54:34 +08:00
setByName ( ' connect ' , id ) ;
}
2020-11-28 17:42:29 +08:00
FFI . id = id ;
2020-11-19 00:32:46 +08:00
}
2022-02-17 15:22:14 +08:00
static Map < String , dynamic > ? popEvent ( ) {
2020-11-19 00:32:46 +08:00
var s = getByName ( ' event ' ) ;
if ( s = = ' ' ) return null ;
try {
Map < String , dynamic > event = json . decode ( s ) ;
return event ;
} catch ( e ) {
2020-11-26 20:28:37 +08:00
print ( ' popEvent(): $ e ' ) ;
2020-11-19 00:32:46 +08:00
}
return null ;
}
static void login ( String password , bool remember ) {
setByName (
' login ' ,
json . encode ( {
' password ' : password ,
' remember ' : remember ? ' true ' : ' false ' ,
} ) ) ;
}
static void close ( ) {
2022-03-25 16:34:27 +08:00
chatModel . close ( ) ;
2022-02-03 17:19:25 +08:00
if ( FFI . imageModel . image ! = null & & ! isDesktop ) {
2022-03-07 22:54:34 +08:00
savePreference ( id , cursorModel . x , cursorModel . y , canvasModel . x ,
canvasModel . y , canvasModel . scale , ffiModel . pi . currentDisplay ) ;
2022-02-03 17:19:25 +08:00
}
2020-11-28 17:42:29 +08:00
id = " " ;
2020-11-19 00:32:46 +08:00
setByName ( ' close ' , ' ' ) ;
2020-11-25 16:28:46 +08:00
imageModel . update ( null ) ;
cursorModel . clear ( ) ;
ffiModel . clear ( ) ;
canvasModel . clear ( ) ;
resetModifiers ( ) ;
2020-11-19 00:32:46 +08:00
}
2022-01-26 12:48:16 +08:00
static String getByName ( String name , [ String arg = ' ' ] ) {
return PlatformFFI . getByName ( name , arg ) ;
2020-11-19 00:32:46 +08:00
}
2020-11-20 16:37:48 +08:00
static void setByName ( String name , [ String value = ' ' ] ) {
2022-01-26 12:48:16 +08:00
PlatformFFI . setByName ( name , value ) ;
2020-11-19 00:32:46 +08:00
}
2022-02-03 00:53:59 +08:00
static handleMouse ( Map < String , dynamic > evt ) {
2022-02-03 17:19:25 +08:00
var type = ' ' ;
2022-02-06 16:29:56 +08:00
var isMove = false ;
2022-02-03 17:19:25 +08:00
switch ( evt [ ' type ' ] ) {
case ' mousedown ' :
type = ' down ' ;
break ;
case ' mouseup ' :
type = ' up ' ;
break ;
case ' mousemove ' :
2022-02-06 16:29:56 +08:00
isMove = true ;
2022-02-03 17:19:25 +08:00
break ;
default :
return ;
}
evt [ ' type ' ] = type ;
var x = evt [ ' x ' ] ;
var y = evt [ ' y ' ] ;
2022-02-06 16:29:56 +08:00
if ( isMove ) {
FFI . canvasModel . moveDesktopMouse ( x , y ) ;
}
2022-02-03 17:19:25 +08:00
final d = FFI . ffiModel . display ;
x - = FFI . canvasModel . x ;
y - = FFI . canvasModel . y ;
2022-02-06 16:29:56 +08:00
if ( ! isMove & & ( x < 0 | | x > d . width | | y < 0 | | y > d . height ) ) {
2022-02-03 17:19:25 +08:00
return ;
}
2022-02-06 16:29:56 +08:00
x / = FFI . canvasModel . scale ;
y / = FFI . canvasModel . scale ;
2022-02-03 17:19:25 +08:00
x + = d . x ;
y + = d . y ;
if ( type ! = ' ' ) {
x = 0 ;
y = 0 ;
}
2022-04-25 18:25:25 +08:00
evt [ ' x ' ] = ' ${ x . round ( ) } ' ;
evt [ ' y ' ] = ' ${ y . round ( ) } ' ;
2022-02-03 17:19:25 +08:00
var buttons = ' ' ;
switch ( evt [ ' buttons ' ] ) {
case 1 :
buttons = ' left ' ;
break ;
case 2 :
buttons = ' right ' ;
break ;
case 4 :
buttons = ' wheel ' ;
break ;
2021-08-05 01:38:20 +08:00
}
2022-02-03 17:19:25 +08:00
evt [ ' buttons ' ] = buttons ;
setByName ( ' send_mouse ' , json . encode ( evt ) ) ;
2022-02-03 00:53:59 +08:00
}
static listenToMouse ( bool yesOrNo ) {
if ( yesOrNo ) {
2022-04-25 18:25:25 +08:00
PlatformFFI . startDesktopWebListener ( ) ;
2022-02-03 00:53:59 +08:00
} else {
PlatformFFI . stopDesktopWebListener ( ) ;
}
}
2022-02-10 02:07:53 +08:00
static void setMethodCallHandler ( FMethod callback ) {
PlatformFFI . setMethodCallHandler ( callback ) ;
}
2022-04-07 03:45:36 +08:00
static Future < bool > invokeMethod ( String method , [ dynamic arguments ] ) async {
2022-03-30 17:31:32 +08:00
return await PlatformFFI . invokeMethod ( method , arguments ) ;
2020-11-19 00:32:46 +08:00
}
}
class Peer {
final String id ;
final String username ;
final String hostname ;
final String platform ;
Peer . fromJson ( String id , Map < String , dynamic > json )
: id = id ,
username = json [ ' username ' ] ,
hostname = json [ ' hostname ' ] ,
platform = json [ ' platform ' ] ;
}
class Display {
double x = 0 ;
double y = 0 ;
int width = 0 ;
int height = 0 ;
}
class PeerInfo {
2022-02-17 15:22:14 +08:00
String version = " " ;
String username = " " ;
String hostname = " " ;
String platform = " " ;
bool sasEnabled = false ;
int currentDisplay = 0 ;
List < Display > displays = [ ] ;
2020-11-19 00:32:46 +08:00
}
2020-12-21 18:28:28 +08:00
void savePreference ( String id , double xCursor , double yCursor , double xCanvas ,
double yCanvas , double scale , int currentDisplay ) async {
SharedPreferences prefs = await SharedPreferences . getInstance ( ) ;
final p = Map < String , dynamic > ( ) ;
p [ ' xCursor ' ] = xCursor ;
p [ ' yCursor ' ] = yCursor ;
p [ ' xCanvas ' ] = xCanvas ;
p [ ' yCanvas ' ] = yCanvas ;
p [ ' scale ' ] = scale ;
p [ ' currentDisplay ' ] = currentDisplay ;
prefs . setString ( ' peer ' + id , json . encode ( p ) ) ;
}
2022-02-17 15:22:14 +08:00
Future < Map < String , dynamic > ? > getPreference ( String id ) async {
2022-02-03 17:19:25 +08:00
if ( ! isDesktop ) return null ;
2020-12-21 18:28:28 +08:00
SharedPreferences prefs = await SharedPreferences . getInstance ( ) ;
var p = prefs . getString ( ' peer ' + id ) ;
if ( p = = null ) return null ;
Map < String , dynamic > m = json . decode ( p ) ;
return m ;
}
2021-08-06 21:18:06 +08:00
void removePreference ( String id ) async {
SharedPreferences prefs = await SharedPreferences . getInstance ( ) ;
prefs . remove ( ' peer ' + id ) ;
2020-12-21 19:05:31 +08:00
}
2020-12-21 18:28:28 +08:00
void initializeCursorAndCanvas ( ) async {
var p = await getPreference ( FFI . id ) ;
int currentDisplay = 0 ;
if ( p ! = null ) {
currentDisplay = p [ ' currentDisplay ' ] ;
}
if ( p = = null | | currentDisplay ! = FFI . ffiModel . pi . currentDisplay ) {
FFI . cursorModel
. updateDisplayOrigin ( FFI . ffiModel . display . x , FFI . ffiModel . display . y ) ;
return ;
}
double xCursor = p [ ' xCursor ' ] ;
double yCursor = p [ ' yCursor ' ] ;
double xCanvas = p [ ' xCanvas ' ] ;
double yCanvas = p [ ' yCanvas ' ] ;
double scale = p [ ' scale ' ] ;
FFI . cursorModel . updateDisplayOriginWithCursor (
FFI . ffiModel . display . x , FFI . ffiModel . display . y , xCursor , yCursor ) ;
FFI . canvasModel . update ( xCanvas , yCanvas , scale ) ;
}
2021-08-02 20:54:56 +08:00
2021-08-02 22:21:23 +08:00
String translate ( String name ) {
2022-01-27 01:28:32 +08:00
if ( name . startsWith ( ' Failed to ' ) & & name . contains ( ' : ' ) ) {
2021-08-02 22:21:23 +08:00
return name . split ( ' : ' ) . map ( ( x ) = > translate ( x ) ) . join ( ' : ' ) ;
}
2022-01-26 19:00:23 +08:00
var a = ' translate ' ;
var b = ' {"locale": " $ localeName ", "text": " $ name "} ' ;
return FFI . getByName ( a , b ) ;
2021-08-02 20:54:56 +08:00
}