2022-05-17 19:59:37 +08:00
import ' dart:convert ' ;
2022-05-25 00:28:59 +08:00
import ' dart:ffi ' ;
2022-01-26 12:48:16 +08:00
import ' dart:io ' ;
import ' dart:typed_data ' ;
2022-05-25 00:28:59 +08:00
2022-05-30 13:25:06 +08:00
import ' package:device_info_plus/device_info_plus.dart ' ;
2022-02-10 02:07:53 +08:00
import ' package:external_path/external_path.dart ' ;
2022-05-25 00:28:59 +08:00
import ' package:ffi/ffi.dart ' ;
2022-07-27 22:56:28 +08:00
import ' package:flutter/foundation.dart ' ;
2022-02-10 02:07:53 +08:00
import ' package:flutter/services.dart ' ;
2023-02-04 11:23:36 +08:00
import ' package:flutter_hbb/consts.dart ' ;
2023-02-12 10:28:04 +08:00
import ' package:get/get.dart ' ;
2022-05-25 00:28:59 +08:00
import ' package:package_info_plus/package_info_plus.dart ' ;
import ' package:path_provider/path_provider.dart ' ;
2022-11-30 13:56:02 +08:00
import ' package:win32/win32.dart ' as win32 ;
2022-05-25 00:28:59 +08:00
2022-02-28 18:29:25 +08:00
import ' ../common.dart ' ;
2022-05-25 00:28:59 +08:00
import ' ../generated_bridge.dart ' ;
2022-01-26 12:48:16 +08:00
class RgbaFrame extends Struct {
@ Uint32 ( )
2022-02-17 15:22:14 +08:00
external int len ;
external Pointer < Uint8 > data ;
2022-01-26 12:48:16 +08:00
}
typedef F2 = Pointer < Utf8 > Function ( Pointer < Utf8 > , Pointer < Utf8 > ) ;
2023-02-12 10:28:04 +08:00
typedef F3 = Pointer < Uint8 > Function ( Pointer < Utf8 > ) ;
2023-02-12 01:52:11 +08:00
typedef F4 = Uint64 Function ( Pointer < Utf8 > ) ;
typedef F4Dart = int Function ( Pointer < Utf8 > ) ;
2023-02-12 10:28:04 +08:00
typedef F5 = Void Function ( Pointer < Utf8 > ) ;
typedef F5Dart = void Function ( Pointer < Utf8 > ) ;
2022-09-11 19:52:38 -07:00
typedef HandleEvent = Future < void > Function ( Map < String , dynamic > evt ) ;
2023-03-01 00:05:06 +09:00
// pub fn session_register_texture(id: *const char, ptr: usize)
2023-02-19 15:25:30 +08:00
typedef F6 = Void Function ( Pointer < Utf8 > , Uint64 ) ;
typedef F6Dart = void Function ( Pointer < Utf8 > , int ) ;
2022-01-26 12:48:16 +08:00
2022-05-28 03:56:42 +08:00
/// FFI wrapper around the native Rust core.
/// Hides the platform differences.
2022-01-26 12:48:16 +08:00
class PlatformFFI {
2022-06-13 21:07:26 +08:00
String _dir = ' ' ;
2022-12-01 21:48:19 +08:00
// _homeDir is only needed for Android and IOS.
2022-06-13 21:07:26 +08:00
String _homeDir = ' ' ;
2022-08-08 17:53:51 +08:00
F2 ? _translate ;
2022-09-10 19:50:48 -07:00
final _eventHandlers = < String , Map < String , HandleEvent > > { } ;
2022-06-13 21:07:26 +08:00
late RustdeskImpl _ffiBind ;
2022-08-03 22:03:31 +08:00
late String _appType ;
2022-09-10 19:50:48 -07:00
StreamEventHandler ? _eventCallback ;
2022-01-26 12:48:16 +08:00
2022-08-03 22:03:31 +08:00
PlatformFFI . _ ( ) ;
static final PlatformFFI instance = PlatformFFI . _ ( ) ;
2022-09-12 01:35:56 -07:00
final _toAndroidChannel = const MethodChannel ( ' mChannel ' ) ;
2022-08-03 22:03:31 +08:00
2022-06-13 21:07:26 +08:00
RustdeskImpl get ffiBind = > _ffiBind ;
2023-02-12 10:28:04 +08:00
F3 ? _session_get_rgba ;
2023-02-12 01:52:11 +08:00
F4Dart ? _session_get_rgba_size ;
2023-02-12 10:28:04 +08:00
F5Dart ? _session_next_rgba ;
2023-02-19 15:25:30 +08:00
F6Dart ? _session_register_texture ;
2022-05-31 14:44:06 +08:00
2022-08-03 22:03:31 +08:00
static get localeName = > Platform . localeName ;
2023-02-04 11:23:36 +08:00
static get isMain = > instance . _appType = = kAppTypeMain ;
2022-01-26 19:00:23 +08:00
static Future < String > getVersion ( ) async {
PackageInfo packageInfo = await PackageInfo . fromPlatform ( ) ;
return packageInfo . version ;
}
2022-07-27 22:56:28 +08:00
bool registerEventHandler (
2022-08-31 16:41:05 +08:00
String eventName , String handlerName , HandleEvent handler ) {
debugPrint ( ' registerEventHandler $ eventName $ handlerName ' ) ;
var handlers = _eventHandlers [ eventName ] ;
2022-07-27 22:56:28 +08:00
if ( handlers = = null ) {
2022-08-31 16:41:05 +08:00
_eventHandlers [ eventName ] = { handlerName: handler } ;
2022-07-27 22:56:28 +08:00
return true ;
} else {
2022-08-31 16:41:05 +08:00
if ( handlers . containsKey ( handlerName ) ) {
2022-07-27 22:56:28 +08:00
return false ;
} else {
2022-08-31 16:41:05 +08:00
handlers [ handlerName ] = handler ;
2022-07-27 22:56:28 +08:00
return true ;
}
}
}
2022-08-31 16:41:05 +08:00
void unregisterEventHandler ( String eventName , String handlerName ) {
debugPrint ( ' unregisterEventHandler $ eventName $ handlerName ' ) ;
var handlers = _eventHandlers [ eventName ] ;
2022-07-27 22:56:28 +08:00
if ( handlers ! = null ) {
2022-08-31 16:41:05 +08:00
handlers . remove ( handlerName ) ;
2022-07-27 22:56:28 +08:00
}
}
2022-08-08 17:53:51 +08:00
String translate ( String name , String locale ) {
2022-08-12 18:42:02 +08:00
if ( _translate = = null ) return name ;
2022-08-08 17:53:51 +08:00
var a = name . toNativeUtf8 ( ) ;
var b = locale . toNativeUtf8 ( ) ;
var p = _translate ! ( a , b ) ;
assert ( p ! = nullptr ) ;
final res = p . toDartString ( ) ;
calloc . free ( p ) ;
calloc . free ( a ) ;
calloc . free ( b ) ;
return res ;
}
2023-02-12 10:28:04 +08:00
Uint8List ? getRgba ( String id , int bufSize ) {
2023-02-12 01:52:11 +08:00
if ( _session_get_rgba = = null ) return null ;
var a = id . toNativeUtf8 ( ) ;
2023-02-12 10:28:04 +08:00
try {
final buffer = _session_get_rgba ! ( a ) ;
if ( buffer = = nullptr ) {
return null ;
}
final data = buffer . asTypedList ( bufSize ) ;
return data ;
} finally {
malloc . free ( a ) ;
}
2023-02-12 01:52:11 +08:00
}
int ? getRgbaSize ( String id ) {
if ( _session_get_rgba_size = = null ) return null ;
var a = id . toNativeUtf8 ( ) ;
final bufferSize = _session_get_rgba_size ! ( a ) ;
malloc . free ( a ) ;
return bufferSize ;
}
2023-02-12 10:28:04 +08:00
void nextRgba ( String id ) {
if ( _session_next_rgba = = null ) return ;
final a = id . toNativeUtf8 ( ) ;
_session_next_rgba ! ( a ) ;
malloc . free ( a ) ;
}
2023-02-19 15:25:30 +08:00
void registerTexture ( String id , int ptr ) {
if ( _session_register_texture = = null ) return ;
final a = id . toNativeUtf8 ( ) ;
_session_register_texture ! ( a , ptr ) ;
malloc . free ( a ) ;
}
2022-05-28 03:56:42 +08:00
/// Init the FFI class, loads the native Rust core library.
2022-09-10 19:50:48 -07:00
Future < void > init ( String appType ) async {
2022-08-03 22:03:31 +08:00
_appType = appType ;
2022-01-26 12:48:16 +08:00
final dylib = Platform . isAndroid
? DynamicLibrary . open ( ' librustdesk.so ' )
2022-05-25 00:28:59 +08:00
: Platform . isLinux
2022-09-12 01:35:56 -07:00
? DynamicLibrary . open ( ' librustdesk.so ' )
2022-05-25 00:28:59 +08:00
: Platform . isWindows
2022-09-12 01:35:56 -07:00
? DynamicLibrary . open ( ' librustdesk.dll ' )
2022-11-22 20:30:22 +08:00
: Platform . isMacOS
? DynamicLibrary . open ( " liblibrustdesk.dylib " )
: DynamicLibrary . process ( ) ;
2022-09-12 01:35:56 -07:00
debugPrint ( ' initializing FFI $ _appType ' ) ;
2022-01-26 12:48:16 +08:00
try {
2022-08-08 17:53:51 +08:00
_translate = dylib . lookupFunction < F2 , F2 > ( ' translate ' ) ;
2023-02-12 10:28:04 +08:00
_session_get_rgba = dylib . lookupFunction < F3 , F3 > ( " session_get_rgba " ) ;
_session_get_rgba_size =
dylib . lookupFunction < F4 , F4Dart > ( " session_get_rgba_size " ) ;
_session_next_rgba =
dylib . lookupFunction < F5 , F5Dart > ( " session_next_rgba " ) ;
2023-03-01 00:05:06 +09:00
_session_register_texture =
dylib . lookupFunction < F6 , F6Dart > ( " session_register_texture " ) ;
2022-09-28 10:55:19 +08:00
try {
// SYSTEM user failed
_dir = ( await getApplicationDocumentsDirectory ( ) ) . path ;
} catch ( e ) {
debugPrint ( ' Failed to get documents directory: $ e ' ) ;
}
2022-05-31 22:09:36 +08:00
_ffiBind = RustdeskImpl ( dylib ) ;
2022-10-11 19:52:03 +08:00
if ( Platform . isLinux ) {
2023-02-04 11:23:36 +08:00
// Start a dbus service, no need to await
_ffiBind . mainStartDbusServer ( ) ;
2023-05-02 12:52:27 +08:00
_ffiBind . mainStartPa ( ) ;
2023-02-07 01:31:11 +08:00
} else if ( Platform . isMacOS & & isMain ) {
2023-05-02 12:52:27 +08:00
// Start ipc service for uri links.
_ffiBind . mainStartIpcUrlServer ( ) ;
2022-10-11 19:52:03 +08:00
}
2022-05-31 22:09:36 +08:00
_startListenEvent ( _ffiBind ) ; // global event
2022-04-18 17:01:45 +08:00
try {
2022-06-21 17:58:27 +08:00
if ( isAndroid ) {
// only support for android
_homeDir = ( await ExternalPath . getExternalStorageDirectories ( ) ) [ 0 ] ;
2022-12-01 21:48:19 +08:00
} else if ( isIOS ) {
_homeDir = _ffiBind . mainGetDataDirIos ( ) ;
2022-06-21 17:58:27 +08:00
} else {
2022-12-01 21:48:19 +08:00
// no need to set home dir
2022-06-21 17:58:27 +08:00
}
2022-04-18 17:01:45 +08:00
} catch ( e ) {
2022-12-09 10:49:47 +08:00
debugPrintStack ( label: ' initialize failed: $ e ' ) ;
2022-04-18 17:01:45 +08:00
}
2022-01-26 12:48:16 +08:00
String id = ' NA ' ;
String name = ' Flutter ' ;
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin ( ) ;
if ( Platform . isAndroid ) {
AndroidDeviceInfo androidInfo = await deviceInfo . androidInfo ;
name = ' ${ androidInfo . brand } - ${ androidInfo . model } ' ;
id = androidInfo . id . hashCode . toString ( ) ;
2022-11-30 13:57:46 +08:00
androidVersion = androidInfo . version . sdkInt ? ? 0 ;
2022-05-29 04:39:12 +08:00
} else if ( Platform . isIOS ) {
2022-01-26 12:48:16 +08:00
IosDeviceInfo iosInfo = await deviceInfo . iosInfo ;
2022-09-12 01:35:56 -07:00
name = iosInfo . utsname . machine ? ? ' ' ;
2022-01-26 12:48:16 +08:00
id = iosInfo . identifierForVendor . hashCode . toString ( ) ;
2022-05-30 13:25:06 +08:00
} else if ( Platform . isLinux ) {
LinuxDeviceInfo linuxInfo = await deviceInfo . linuxInfo ;
name = linuxInfo . name ;
id = linuxInfo . machineId ? ? linuxInfo . id ;
} else if ( Platform . isWindows ) {
2022-09-16 16:19:15 +08:00
try {
2022-11-30 13:56:02 +08:00
// request windows build number to fix overflow on win7
windowsBuildNumber = getWindowsTargetBuildNumber ( ) ;
2022-09-16 16:19:15 +08:00
WindowsDeviceInfo winInfo = await deviceInfo . windowsInfo ;
name = winInfo . computerName ;
id = winInfo . computerName ;
2022-12-09 10:49:47 +08:00
} catch ( e ) {
debugPrintStack ( label: " get windows device info failed: $ e " ) ;
2022-09-16 16:19:15 +08:00
name = " unknown " ;
id = " unknown " ;
}
2022-05-30 13:25:06 +08:00
} else if ( Platform . isMacOS ) {
MacOsDeviceInfo macOsInfo = await deviceInfo . macOsInfo ;
name = macOsInfo . computerName ;
2022-09-12 01:35:56 -07:00
id = macOsInfo . systemGUID ? ? ' ' ;
2022-01-26 12:48:16 +08:00
}
2022-12-01 21:48:19 +08:00
if ( isAndroid | | isIOS ) {
debugPrint (
' _appType: $ _appType ,info1-id: $ id ,info2-name: $ name ,dir: $ _dir ,homeDir: $ _homeDir ' ) ;
} else {
debugPrint (
' _appType: $ _appType ,info1-id: $ id ,info2-name: $ name ,dir: $ _dir ' ) ;
}
2023-03-13 13:46:44 +08:00
if ( desktopType = = DesktopType . cm ) {
await _ffiBind . cmStartListenIpcThread ( ) ;
}
2022-08-08 22:27:27 +08:00
await _ffiBind . mainDeviceId ( id: id ) ;
await _ffiBind . mainDeviceName ( name: name ) ;
await _ffiBind . mainSetHomeDir ( home: _homeDir ) ;
await _ffiBind . mainInit ( appDir: _dir ) ;
2022-01-26 12:48:16 +08:00
} catch ( e ) {
2022-12-09 10:49:47 +08:00
debugPrintStack ( label: ' initialize failed: $ e ' ) ;
2022-01-26 12:48:16 +08:00
}
2022-02-28 21:26:44 +08:00
version = await getVersion ( ) ;
2022-01-26 12:48:16 +08:00
}
2022-02-03 00:53:59 +08:00
2022-09-11 19:52:38 -07:00
Future < bool > _tryHandle ( Map < String , dynamic > evt ) async {
2022-07-27 22:56:28 +08:00
final name = evt [ ' name ' ] ;
if ( name ! = null ) {
final handlers = _eventHandlers [ name ] ;
if ( handlers ! = null ) {
if ( handlers . isNotEmpty ) {
2022-09-10 19:50:48 -07:00
for ( var handler in handlers . values ) {
2022-09-11 19:52:38 -07:00
await handler ( evt ) ;
2022-09-10 19:50:48 -07:00
}
2022-07-27 22:56:28 +08:00
return true ;
}
}
}
return false ;
}
2022-05-28 03:56:42 +08:00
/// Start listening to the Rust core's events and frames.
2022-06-13 21:07:26 +08:00
void _startListenEvent ( RustdeskImpl rustdeskImpl ) {
2022-05-19 23:45:44 +08:00
( ) async {
2022-08-03 22:03:31 +08:00
await for ( final message
in rustdeskImpl . startGlobalEventStream ( appType: _appType ) ) {
try {
Map < String , dynamic > event = json . decode ( message ) ;
// _tryHandle here may be more flexible than _eventCallback
2022-09-11 19:52:38 -07:00
if ( ! await _tryHandle ( event ) ) {
2022-08-03 22:03:31 +08:00
if ( _eventCallback ! = null ) {
2022-09-10 19:50:48 -07:00
await _eventCallback ! ( event ) ;
2022-07-27 22:56:28 +08:00
}
2022-05-19 23:45:44 +08:00
}
2022-08-03 22:03:31 +08:00
} catch ( e ) {
2022-09-10 19:50:48 -07:00
debugPrint ( ' json.decode fail(): $ e ' ) ;
2022-05-17 19:59:37 +08:00
}
}
2022-05-19 23:45:44 +08:00
} ( ) ;
2022-05-17 19:59:37 +08:00
}
2022-09-10 19:50:48 -07:00
void setEventCallback ( StreamEventHandler fun ) async {
2022-05-17 19:59:37 +08:00
_eventCallback = fun ;
}
2022-06-13 21:07:26 +08:00
void setRgbaCallback ( void Function ( Uint8List ) fun ) async { }
2022-05-19 23:45:44 +08:00
2022-06-13 21:07:26 +08:00
void startDesktopWebListener ( ) { }
2022-02-17 15:22:14 +08:00
2022-06-13 21:07:26 +08:00
void stopDesktopWebListener ( ) { }
2022-02-10 02:07:53 +08:00
2022-06-13 21:07:26 +08:00
void setMethodCallHandler ( FMethod callback ) {
2022-08-03 22:03:31 +08:00
_toAndroidChannel . setMethodCallHandler ( ( call ) async {
2022-02-10 02:07:53 +08:00
callback ( call . method , call . arguments ) ;
return null ;
} ) ;
}
2022-06-13 21:07:26 +08:00
invokeMethod ( String method , [ dynamic arguments ] ) async {
2022-04-18 11:46:36 +08:00
if ( ! isAndroid ) return Future < bool > ( ( ) = > false ) ;
2022-08-03 22:03:31 +08:00
return await _toAndroidChannel . invokeMethod ( method , arguments ) ;
2022-02-10 02:07:53 +08:00
}
2023-03-01 00:05:06 +09:00
void syncAndroidServiceAppDirConfigPath ( ) {
invokeMethod ( AndroidChannel . kSyncAppDirConfigPath , _dir ) ;
}
2022-01-26 12:48:16 +08:00
}