native_model.dart 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. import 'dart:convert';
  2. import 'dart:ffi';
  3. import 'dart:io';
  4. import 'package:device_info_plus/device_info_plus.dart';
  5. import 'package:external_path/external_path.dart';
  6. import 'package:ffi/ffi.dart';
  7. import 'package:flutter/foundation.dart';
  8. import 'package:flutter/services.dart';
  9. import 'package:flutter_hbb/consts.dart';
  10. import 'package:flutter_hbb/main.dart';
  11. import 'package:package_info_plus/package_info_plus.dart';
  12. import 'package:path_provider/path_provider.dart';
  13. import '../common.dart';
  14. import '../generated_bridge.dart';
  15. final class RgbaFrame extends Struct {
  16. @Uint32()
  17. external int len;
  18. external Pointer<Uint8> data;
  19. }
  20. typedef F3 = Pointer<Uint8> Function(Pointer<Utf8>, int);
  21. typedef F3Dart = Pointer<Uint8> Function(Pointer<Utf8>, Int32);
  22. typedef HandleEvent = Future<void> Function(Map<String, dynamic> evt);
  23. /// FFI wrapper around the native Rust core.
  24. /// Hides the platform differences.
  25. class PlatformFFI {
  26. String _dir = '';
  27. // _homeDir is only needed for Android and IOS.
  28. String _homeDir = '';
  29. final _eventHandlers = <String, Map<String, HandleEvent>>{};
  30. late RustdeskImpl _ffiBind;
  31. late String _appType;
  32. StreamEventHandler? _eventCallback;
  33. PlatformFFI._();
  34. static final PlatformFFI instance = PlatformFFI._();
  35. final _toAndroidChannel = const MethodChannel('mChannel');
  36. RustdeskImpl get ffiBind => _ffiBind;
  37. F3? _session_get_rgba;
  38. static get localeName => Platform.localeName;
  39. static get isMain => instance._appType == kAppTypeMain;
  40. static String getByName(String name, [String arg = '']) {
  41. return '';
  42. }
  43. static void setByName(String name, [String value = '']) {}
  44. static Future<String> getVersion() async {
  45. PackageInfo packageInfo = await PackageInfo.fromPlatform();
  46. return packageInfo.version;
  47. }
  48. bool registerEventHandler(
  49. String eventName, String handlerName, HandleEvent handler) {
  50. debugPrint('registerEventHandler $eventName $handlerName');
  51. var handlers = _eventHandlers[eventName];
  52. if (handlers == null) {
  53. _eventHandlers[eventName] = {handlerName: handler};
  54. return true;
  55. } else {
  56. if (handlers.containsKey(handlerName)) {
  57. return false;
  58. } else {
  59. handlers[handlerName] = handler;
  60. return true;
  61. }
  62. }
  63. }
  64. void unregisterEventHandler(String eventName, String handlerName) {
  65. debugPrint('unregisterEventHandler $eventName $handlerName');
  66. var handlers = _eventHandlers[eventName];
  67. if (handlers != null) {
  68. handlers.remove(handlerName);
  69. }
  70. }
  71. String translate(String name, String locale) =>
  72. _ffiBind.translate(name: name, locale: locale);
  73. Uint8List? getRgba(SessionID sessionId, int display, int bufSize) {
  74. if (_session_get_rgba == null) return null;
  75. final sessionIdStr = sessionId.toString();
  76. var a = sessionIdStr.toNativeUtf8();
  77. try {
  78. final buffer = _session_get_rgba!(a, display);
  79. if (buffer == nullptr) {
  80. return null;
  81. }
  82. final data = buffer.asTypedList(bufSize);
  83. return data;
  84. } finally {
  85. malloc.free(a);
  86. }
  87. }
  88. int getRgbaSize(SessionID sessionId, int display) =>
  89. _ffiBind.sessionGetRgbaSize(sessionId: sessionId, display: display);
  90. void nextRgba(SessionID sessionId, int display) =>
  91. _ffiBind.sessionNextRgba(sessionId: sessionId, display: display);
  92. void registerPixelbufferTexture(SessionID sessionId, int display, int ptr) =>
  93. _ffiBind.sessionRegisterPixelbufferTexture(
  94. sessionId: sessionId, display: display, ptr: ptr);
  95. void registerGpuTexture(SessionID sessionId, int display, int ptr) =>
  96. _ffiBind.sessionRegisterGpuTexture(
  97. sessionId: sessionId, display: display, ptr: ptr);
  98. /// Init the FFI class, loads the native Rust core library.
  99. Future<void> init(String appType) async {
  100. _appType = appType;
  101. final dylib = isAndroid
  102. ? DynamicLibrary.open('librustdesk.so')
  103. : isLinux
  104. ? DynamicLibrary.open('librustdesk.so')
  105. : isWindows
  106. ? DynamicLibrary.open('librustdesk.dll')
  107. :
  108. // Use executable itself as the dynamic library for MacOS.
  109. // Multiple dylib instances will cause some global instances to be invalid.
  110. // eg. `lazy_static` objects in rust side, will be created more than once, which is not expected.
  111. //
  112. // isMacOS? DynamicLibrary.open("liblibrustdesk.dylib") :
  113. DynamicLibrary.process();
  114. debugPrint('initializing FFI $_appType');
  115. try {
  116. _session_get_rgba = dylib.lookupFunction<F3Dart, F3>("session_get_rgba");
  117. try {
  118. // SYSTEM user failed
  119. _dir = (await getApplicationDocumentsDirectory()).path;
  120. } catch (e) {
  121. debugPrint('Failed to get documents directory: $e');
  122. }
  123. _ffiBind = RustdeskImpl(dylib);
  124. if (isLinux) {
  125. if (isMain) {
  126. // Start a dbus service for uri links, no need to await
  127. _ffiBind.mainStartDbusServer();
  128. }
  129. } else if (isMacOS && isMain) {
  130. // Start ipc service for uri links.
  131. _ffiBind.mainStartIpcUrlServer();
  132. }
  133. _startListenEvent(_ffiBind); // global event
  134. try {
  135. if (isAndroid) {
  136. // only support for android
  137. _homeDir = (await ExternalPath.getExternalStorageDirectories())[0];
  138. } else if (isIOS) {
  139. _homeDir = _ffiBind.mainGetDataDirIos();
  140. } else {
  141. // no need to set home dir
  142. }
  143. } catch (e) {
  144. debugPrintStack(label: 'initialize failed: $e');
  145. }
  146. String id = 'NA';
  147. String name = 'Flutter';
  148. DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
  149. if (isAndroid) {
  150. AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
  151. name = '${androidInfo.brand}-${androidInfo.model}';
  152. id = androidInfo.id.hashCode.toString();
  153. androidVersion = androidInfo.version.sdkInt;
  154. } else if (isIOS) {
  155. IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
  156. name = iosInfo.utsname.machine;
  157. id = iosInfo.identifierForVendor.hashCode.toString();
  158. } else if (isLinux) {
  159. LinuxDeviceInfo linuxInfo = await deviceInfo.linuxInfo;
  160. name = linuxInfo.name;
  161. id = linuxInfo.machineId ?? linuxInfo.id;
  162. } else if (isWindows) {
  163. try {
  164. // request windows build number to fix overflow on win7
  165. windowsBuildNumber = getWindowsTargetBuildNumber();
  166. WindowsDeviceInfo winInfo = await deviceInfo.windowsInfo;
  167. name = winInfo.computerName;
  168. id = winInfo.computerName;
  169. } catch (e) {
  170. debugPrintStack(label: "get windows device info failed: $e");
  171. name = "unknown";
  172. id = "unknown";
  173. }
  174. } else if (isMacOS) {
  175. MacOsDeviceInfo macOsInfo = await deviceInfo.macOsInfo;
  176. name = macOsInfo.computerName;
  177. id = macOsInfo.systemGUID ?? '';
  178. }
  179. if (isAndroid || isIOS) {
  180. debugPrint(
  181. '_appType:$_appType,info1-id:$id,info2-name:$name,dir:$_dir,homeDir:$_homeDir');
  182. } else {
  183. debugPrint(
  184. '_appType:$_appType,info1-id:$id,info2-name:$name,dir:$_dir');
  185. }
  186. if (desktopType == DesktopType.cm) {
  187. await _ffiBind.cmInit();
  188. }
  189. await _ffiBind.mainDeviceId(id: id);
  190. await _ffiBind.mainDeviceName(name: name);
  191. await _ffiBind.mainSetHomeDir(home: _homeDir);
  192. await _ffiBind.mainInit(
  193. appDir: _dir,
  194. customClientConfig: '',
  195. );
  196. } catch (e) {
  197. debugPrintStack(label: 'initialize failed: $e');
  198. }
  199. version = await getVersion();
  200. }
  201. Future<bool> tryHandle(Map<String, dynamic> evt) async {
  202. final name = evt['name'];
  203. if (name != null) {
  204. final handlers = _eventHandlers[name];
  205. if (handlers != null) {
  206. if (handlers.isNotEmpty) {
  207. for (var handler in handlers.values) {
  208. await handler(evt);
  209. }
  210. return true;
  211. }
  212. }
  213. }
  214. return false;
  215. }
  216. /// Start listening to the Rust core's events and frames.
  217. void _startListenEvent(RustdeskImpl rustdeskImpl) {
  218. final appType =
  219. _appType == kAppTypeDesktopRemote ? '$_appType,$kWindowId' : _appType;
  220. var sink = rustdeskImpl.startGlobalEventStream(appType: appType);
  221. sink.listen((message) {
  222. () async {
  223. try {
  224. Map<String, dynamic> event = json.decode(message);
  225. // _tryHandle here may be more flexible than _eventCallback
  226. if (!await tryHandle(event)) {
  227. if (_eventCallback != null) {
  228. await _eventCallback!(event);
  229. }
  230. }
  231. } catch (e) {
  232. debugPrint('json.decode fail(): $e');
  233. }
  234. }();
  235. });
  236. }
  237. void setEventCallback(StreamEventHandler fun) async {
  238. _eventCallback = fun;
  239. }
  240. void setRgbaCallback(void Function(int, Uint8List) fun) async {}
  241. void startDesktopWebListener() {}
  242. void stopDesktopWebListener() {}
  243. void setMethodCallHandler(FMethod callback) {
  244. _toAndroidChannel.setMethodCallHandler((call) async {
  245. callback(call.method, call.arguments);
  246. return null;
  247. });
  248. }
  249. invokeMethod(String method, [dynamic arguments]) async {
  250. if (!isAndroid) return Future<bool>(() => false);
  251. return await _toAndroidChannel.invokeMethod(method, arguments);
  252. }
  253. void syncAndroidServiceAppDirConfigPath() {
  254. invokeMethod(AndroidChannel.kSyncAppDirConfigPath, _dir);
  255. }
  256. void setFullscreenCallback(void Function(bool) fun) {}
  257. }