diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 249b45a0c..73334baae 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -33,6 +33,8 @@ late final DesktopType? desktopType; typedef F = String Function(String); typedef FMethod = String Function(String, dynamic); +typedef StreamEventHandler = Future Function(Map); + late final iconKeyboard = MemoryImage(Uint8List.fromList(base64Decode( "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAgVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9d3yJTAAAAKnRSTlMA0Gd/0y8ILZgbJffDPUwV2nvzt+TMqZxyU7CMb1pYQyzsvKunkXE4AwJnNC24AAAA+0lEQVQ4y83O2U7DMBCF4ZMxk9rZk26kpQs7nPd/QJy4EiLbLf01N5Y/2YP/qxDFQvGB5NPC/ZpVnfJx4b5xyGfF95rkHvNCWH1u+N6J6T0sC7gqRy8uGPfBLEbozPXUjlkQKwGaFPNizwQbwkx0TDvhCii34ExZCSQVBdzIOEOyeclSHgBGXkpeygXSQgStACtWx4Z8rr8COHOvfEP/IbbsQAToFUAAV1M408IIjIGYAPoCSNRP7DQutfQTqxuAiH7UUg1FaJR2AGrrx52sK2ye28LZ0wBAEyR6y8X+NADhm1B4fgiiHXbRrTrxpwEY9RdM9wsepnvFHfUDwYEeiwAJr/gAAAAASUVORK5CYII="))); late final iconClipboard = MemoryImage(Uint8List.fromList(base64Decode( diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 3d47dd1ce..4321376a2 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -594,8 +594,8 @@ class ImagePaint extends StatelessWidget { } else { final key = cacheLinux.key(scale); cursor.addKeyLinux(key); - // debugPrint( - // 'REMOVE ME ================================= linux curor key: $key'); + debugPrint( + 'REMOVE ME ================================= linux curor key: $key'); return FlutterCustomMemoryImageCursor( pixbuf: cacheLinux.data, key: key, diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 6d8d68281..303157247 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -4,7 +4,6 @@ import 'dart:io'; import 'dart:math'; import 'dart:typed_data'; import 'dart:ui' as ui; -import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -21,6 +20,7 @@ import 'package:flutter_custom_cursor/flutter_custom_cursor.dart'; import '../common.dart'; import '../common/shared_state.dart'; +import '../utils/image.dart' as img; import '../mobile/widgets/dialog.dart'; import 'peer_model.dart'; import 'platform_model.dart'; @@ -127,8 +127,8 @@ class FfiModel with ChangeNotifier { _permissions.clear(); } - void Function(Map) startEventListener(String peerId) { - return (evt) { + StreamEventHandler startEventListener(String peerId) { + return (evt) async { var name = evt['name']; if (name == 'msgbox') { handleMsgBox(evt, peerId); @@ -140,11 +140,11 @@ class FfiModel with ChangeNotifier { } else if (name == 'switch_display') { handleSwitchDisplay(evt); } else if (name == 'cursor_data') { - parent.target?.cursorModel.updateCursorData(evt); + await parent.target?.cursorModel.updateCursorData(evt); } else if (name == 'cursor_id') { - parent.target?.cursorModel.updateCursorId(evt); + await parent.target?.cursorModel.updateCursorId(evt); } else if (name == 'cursor_position') { - parent.target?.cursorModel.updateCursorPosition(evt, peerId); + await parent.target?.cursorModel.updateCursorPosition(evt, peerId); } else if (name == 'clipboard') { Clipboard.setData(ClipboardData(text: evt['content'])); } else if (name == 'permission') { @@ -780,7 +780,7 @@ class CursorModel with ChangeNotifier { notifyListeners(); } - void updateCursorData(Map evt) { + updateCursorData(Map evt) async { var id = int.parse(evt['id']); _hotx = double.parse(evt['hotx']); _hoty = double.parse(evt['hoty']); @@ -789,34 +789,26 @@ class CursorModel with ChangeNotifier { List colors = json.decode(evt['colors']); final rgba = Uint8List.fromList(colors.map((s) => s as int).toList()); var pid = parent.target?.id; - ui.decodeImageFromPixels(rgba, width, height, ui.PixelFormat.rgba8888, - (image) { - () async { - if (parent.target?.id != pid) return; - _image = image; - _images[id] = Tuple3(image, _hotx, _hoty); - _updateCacheLinux(image, id, width, height); - try { - // my throw exception, because the listener maybe already dispose - notifyListeners(); - } catch (e) { - debugPrint('notify cursor: $e'); - } - }(); - }); + + final image = await img.decodeImageFromPixels( + rgba, width, height, ui.PixelFormat.rgba8888); + if (parent.target?.id != pid) return; + _image = image; + _images[id] = Tuple3(image, _hotx, _hoty); + await _updateCacheLinux(image, id, width, height); + try { + // my throw exception, because the listener maybe already dispose + notifyListeners(); + } catch (e) { + debugPrint('notify cursor: $e'); + } } - void _updateCacheLinux(ui.Image image, int id, int w, int h) async { - final data = await image.toByteData(format: ImageByteFormat.png); - late Uint8List? dataLinux; - if (data != null) { - dataLinux = data.buffer.asUint8List(); - } else { - dataLinux = null; - } + _updateCacheLinux(ui.Image image, int id, int w, int h) async { + final data = await image.toByteData(format: ui.ImageByteFormat.png); _cacheLinux = CursorData( peerId: this.id, - data: dataLinux, + data: data?.buffer.asUint8List(), id: id, hotx: _hotx, hoty: _hoty, @@ -826,9 +818,10 @@ class CursorModel with ChangeNotifier { _cacheMapLinux[id] = _cacheLinux!; } - void updateCursorId(Map evt) { - _cacheLinux = _cacheMapLinux[int.parse(evt['id'])]; - final tmp = _images[int.parse(evt['id'])]; + updateCursorId(Map evt) async { + final id = int.parse(evt['id']); + _cacheLinux = _cacheMapLinux[id]; + final tmp = _images[id]; if (tmp != null) { _image = tmp.item1; _hotx = tmp.item2; @@ -838,7 +831,7 @@ class CursorModel with ChangeNotifier { } /// Update the cursor position. - void updateCursorPosition(Map evt, String id) { + updateCursorPosition(Map evt, String id) async { _x = double.parse(evt['x']); _y = double.parse(evt['y']); try { diff --git a/flutter/lib/models/native_model.dart b/flutter/lib/models/native_model.dart index 0d356a7c2..a9081a160 100644 --- a/flutter/lib/models/native_model.dart +++ b/flutter/lib/models/native_model.dart @@ -30,15 +30,15 @@ class PlatformFFI { String _dir = ''; String _homeDir = ''; F2? _translate; - final _eventHandlers = Map>(); + final _eventHandlers = >{}; late RustdeskImpl _ffiBind; late String _appType; - void Function(Map)? _eventCallback; + StreamEventHandler? _eventCallback; PlatformFFI._(); static final PlatformFFI instance = PlatformFFI._(); - final _toAndroidChannel = MethodChannel("mChannel"); + final _toAndroidChannel = const MethodChannel("mChannel"); RustdeskImpl get ffiBind => _ffiBind; @@ -88,7 +88,7 @@ class PlatformFFI { } /// Init the FFI class, loads the native Rust core library. - Future init(String appType) async { + Future init(String appType) async { _appType = appType; // if (isDesktop) { // // TODO @@ -117,7 +117,7 @@ class PlatformFFI { _homeDir = (await getDownloadsDirectory())?.path ?? ""; } } catch (e) { - print("initialize failed: $e"); + debugPrint('initialize failed: $e'); } String id = 'NA'; String name = 'Flutter'; @@ -144,14 +144,14 @@ class PlatformFFI { name = macOsInfo.computerName; id = macOsInfo.systemGUID ?? ""; } - print( - "_appType:$_appType,info1-id:$id,info2-name:$name,dir:$_dir,homeDir:$_homeDir"); + debugPrint( + '_appType:$_appType,info1-id:$id,info2-name:$name,dir:$_dir,homeDir:$_homeDir'); await _ffiBind.mainDeviceId(id: id); await _ffiBind.mainDeviceName(name: name); await _ffiBind.mainSetHomeDir(home: _homeDir); await _ffiBind.mainInit(appDir: _dir); } catch (e) { - print("initialize failed: $e"); + debugPrint('initialize failed: $e'); } version = await getVersion(); } @@ -162,9 +162,9 @@ class PlatformFFI { final handlers = _eventHandlers[name]; if (handlers != null) { if (handlers.isNotEmpty) { - handlers.values.forEach((handler) { + for (var handler in handlers.values) { handler(evt); - }); + } return true; } } @@ -182,17 +182,17 @@ class PlatformFFI { // _tryHandle here may be more flexible than _eventCallback if (!_tryHandle(event)) { if (_eventCallback != null) { - _eventCallback!(event); + await _eventCallback!(event); } } } catch (e) { - print('json.decode fail(): $e'); + debugPrint('json.decode fail(): $e'); } } }(); } - void setEventCallback(void Function(Map) fun) async { + void setEventCallback(StreamEventHandler fun) async { _eventCallback = fun; } diff --git a/flutter/lib/utils/image.dart b/flutter/lib/utils/image.dart new file mode 100644 index 000000000..e92fdc6e5 --- /dev/null +++ b/flutter/lib/utils/image.dart @@ -0,0 +1,49 @@ +import 'dart:typed_data'; +import 'dart:ui' as ui; + +Future decodeImageFromPixels( + Uint8List pixels, + int width, + int height, + ui.PixelFormat format, { + int? rowBytes, + int? targetWidth, + int? targetHeight, + bool allowUpscaling = true, +}) async { + if (targetWidth != null) { + assert(allowUpscaling || targetWidth <= width); + } + if (targetHeight != null) { + assert(allowUpscaling || targetHeight <= height); + } + + final ui.ImmutableBuffer buffer = + await ui.ImmutableBuffer.fromUint8List(pixels); + final ui.ImageDescriptor descriptor = ui.ImageDescriptor.raw( + buffer, + width: width, + height: height, + rowBytes: rowBytes, + pixelFormat: format, + ); + if (!allowUpscaling) { + if (targetWidth != null && targetWidth! > descriptor.width) { + targetWidth = descriptor.width; + } + if (targetHeight != null && targetHeight! > descriptor.height) { + targetHeight = descriptor.height; + } + } + + final ui.Codec codec = await descriptor.instantiateCodec( + targetWidth: targetWidth, + targetHeight: targetHeight, + ); + + final ui.FrameInfo frameInfo = await codec.getNextFrame(); + codec.dispose(); + buffer.dispose(); + descriptor.dispose(); + return frameInfo.image; +}