Merge pull request #1497 from fufesou/flutter_desktop_cursors

Flutter desktop cursors
This commit is contained in:
RustDesk 2022-09-12 12:12:09 +08:00 committed by GitHub
commit a0c46c905d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 143 additions and 105 deletions

View File

@ -33,6 +33,8 @@ late final DesktopType? desktopType;
typedef F = String Function(String); typedef F = String Function(String);
typedef FMethod = String Function(String, dynamic); typedef FMethod = String Function(String, dynamic);
typedef StreamEventHandler = Future<void> Function(Map<String, dynamic>);
late final iconKeyboard = MemoryImage(Uint8List.fromList(base64Decode( late final iconKeyboard = MemoryImage(Uint8List.fromList(base64Decode(
"iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAgVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9d3yJTAAAAKnRSTlMA0Gd/0y8ILZgbJffDPUwV2nvzt+TMqZxyU7CMb1pYQyzsvKunkXE4AwJnNC24AAAA+0lEQVQ4y83O2U7DMBCF4ZMxk9rZk26kpQs7nPd/QJy4EiLbLf01N5Y/2YP/qxDFQvGB5NPC/ZpVnfJx4b5xyGfF95rkHvNCWH1u+N6J6T0sC7gqRy8uGPfBLEbozPXUjlkQKwGaFPNizwQbwkx0TDvhCii34ExZCSQVBdzIOEOyeclSHgBGXkpeygXSQgStACtWx4Z8rr8COHOvfEP/IbbsQAToFUAAV1M408IIjIGYAPoCSNRP7DQutfQTqxuAiH7UUg1FaJR2AGrrx52sK2ye28LZ0wBAEyR6y8X+NADhm1B4fgiiHXbRrTrxpwEY9RdM9wsepnvFHfUDwYEeiwAJr/gAAAAASUVORK5CYII="))); "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAgVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9d3yJTAAAAKnRSTlMA0Gd/0y8ILZgbJffDPUwV2nvzt+TMqZxyU7CMb1pYQyzsvKunkXE4AwJnNC24AAAA+0lEQVQ4y83O2U7DMBCF4ZMxk9rZk26kpQs7nPd/QJy4EiLbLf01N5Y/2YP/qxDFQvGB5NPC/ZpVnfJx4b5xyGfF95rkHvNCWH1u+N6J6T0sC7gqRy8uGPfBLEbozPXUjlkQKwGaFPNizwQbwkx0TDvhCii34ExZCSQVBdzIOEOyeclSHgBGXkpeygXSQgStACtWx4Z8rr8COHOvfEP/IbbsQAToFUAAV1M408IIjIGYAPoCSNRP7DQutfQTqxuAiH7UUg1FaJR2AGrrx52sK2ye28LZ0wBAEyR6y8X+NADhm1B4fgiiHXbRrTrxpwEY9RdM9wsepnvFHfUDwYEeiwAJr/gAAAAASUVORK5CYII=")));
late final iconClipboard = MemoryImage(Uint8List.fromList(base64Decode( late final iconClipboard = MemoryImage(Uint8List.fromList(base64Decode(

View File

@ -537,8 +537,17 @@ class ImagePaint extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final m = Provider.of<ImageModel>(context); final m = Provider.of<ImageModel>(context);
var c = Provider.of<CanvasModel>(context); var c = Provider.of<CanvasModel>(context);
final s = c.scale; final s = c.scale;
mouseRegion({child}) => Obx(() => MouseRegion(
cursor: (cursorOverImage.isTrue && keyboardEnabled.isTrue)
? (remoteCursorMoved.isTrue
? SystemMouseCursors.none
: _buildCustomCursorLinux(context, s))
: MouseCursor.defer,
onHover: (evt) {},
child: child));
if (c.scrollStyle == ScrollStyle.scrollbar) { if (c.scrollStyle == ScrollStyle.scrollbar) {
final imageWidget = SizedBox( final imageWidget = SizedBox(
width: c.getDisplayWidth() * s, width: c.getDisplayWidth() * s,
@ -547,7 +556,6 @@ class ImagePaint extends StatelessWidget {
painter: ImagePainter(image: m.image, x: 0, y: 0, scale: s), painter: ImagePainter(image: m.image, x: 0, y: 0, scale: s),
)); ));
Rx<Offset> pos = Rx<Offset>(const Offset(0.0, 0.0));
return Center( return Center(
child: NotificationListener<ScrollNotification>( child: NotificationListener<ScrollNotification>(
onNotification: (notification) { onNotification: (notification) {
@ -562,16 +570,8 @@ class ImagePaint extends StatelessWidget {
c.setScrollPercent(percentX, percentY); c.setScrollPercent(percentX, percentY);
return false; return false;
}, },
child: Obx(() => MouseRegion( child: mouseRegion(
cursor: (cursorOverImage.isTrue && keyboardEnabled.isTrue) child: _buildCrossScrollbar(_buildListener(imageWidget))),
? (remoteCursorMoved.isTrue
? SystemMouseCursors.none
: _buildCustomCursorLinux(context, s))
: MouseCursor.defer,
onHover: (evt) {
pos.value = evt.position;
},
child: _buildCrossScrollbar(_buildListener(imageWidget)))),
), ),
); );
} else { } else {
@ -582,7 +582,7 @@ class ImagePaint extends StatelessWidget {
painter: painter:
ImagePainter(image: m.image, x: c.x / s, y: c.y / s, scale: s), ImagePainter(image: m.image, x: c.x / s, y: c.y / s, scale: s),
)); ));
return _buildListener(imageWidget); return mouseRegion(child: _buildListener(imageWidget));
} }
} }
@ -597,8 +597,8 @@ class ImagePaint extends StatelessWidget {
return FlutterCustomMemoryImageCursor( return FlutterCustomMemoryImageCursor(
pixbuf: cacheLinux.data, pixbuf: cacheLinux.data,
key: key, key: key,
hotx: cacheLinux.hotx, hotx: 0.0,
hoty: cacheLinux.hoty, hoty: 0.0,
imageWidth: (cacheLinux.width * scale).toInt(), imageWidth: (cacheLinux.width * scale).toInt(),
imageHeight: (cacheLinux.height * scale).toInt(), imageHeight: (cacheLinux.height * scale).toInt(),
); );

View File

@ -8,7 +8,7 @@ import './material_mod_popup_menu.dart' as mod_menu;
// https://stackoverflow.com/questions/68318314/flutter-popup-menu-inside-popup-menu // https://stackoverflow.com/questions/68318314/flutter-popup-menu-inside-popup-menu
class PopupMenuChildrenItem<T> extends mod_menu.PopupMenuEntry<T> { class PopupMenuChildrenItem<T> extends mod_menu.PopupMenuEntry<T> {
PopupMenuChildrenItem({ const PopupMenuChildrenItem({
key, key,
this.height = kMinInteractiveDimension, this.height = kMinInteractiveDimension,
this.padding, this.padding,

View File

@ -273,13 +273,13 @@ _keepScaleBuilder() {
_registerEventHandler() { _registerEventHandler() {
if (desktopType != DesktopType.main) { if (desktopType != DesktopType.main) {
platformFFI.registerEventHandler('theme', 'theme', (evt) { platformFFI.registerEventHandler('theme', 'theme', (evt) async {
String? dark = evt['dark']; String? dark = evt['dark'];
if (dark != null) { if (dark != null) {
MyTheme.changeTo(dark == 'true'); MyTheme.changeTo(dark == 'true');
} }
}); });
platformFFI.registerEventHandler('language', 'language', (_) { platformFFI.registerEventHandler('language', 'language', (_) async {
Get.forceAppUpdate(); Get.forceAppUpdate();
}); });
} }

View File

@ -1085,9 +1085,8 @@ void showOptions(String id, OverlayDialogManager dialogManager) async {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: displays + children: displays +
<Widget>[ <Widget>[
getRadio('Original', 'original', viewStyle, setViewStyle), getRadio('Scale Original', 'original', viewStyle, setViewStyle),
getRadio('Shrink', 'shrink', viewStyle, setViewStyle), getRadio('Scale adaptive', 'adaptive', viewStyle, setViewStyle),
getRadio('Stretch', 'stretch', viewStyle, setViewStyle),
Divider(color: MyTheme.border), Divider(color: MyTheme.border),
getRadio('Good image quality', 'best', quality, setQuality), getRadio('Good image quality', 'best', quality, setQuality),
getRadio('Balanced', 'balanced', quality, setQuality), getRadio('Balanced', 'balanced', quality, setQuality),

View File

@ -4,7 +4,6 @@ import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'dart:typed_data'; import 'dart:typed_data';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -21,6 +20,7 @@ import 'package:flutter_custom_cursor/flutter_custom_cursor.dart';
import '../common.dart'; import '../common.dart';
import '../common/shared_state.dart'; import '../common/shared_state.dart';
import '../utils/image.dart' as img;
import '../mobile/widgets/dialog.dart'; import '../mobile/widgets/dialog.dart';
import 'peer_model.dart'; import 'peer_model.dart';
import 'platform_model.dart'; import 'platform_model.dart';
@ -127,8 +127,8 @@ class FfiModel with ChangeNotifier {
_permissions.clear(); _permissions.clear();
} }
void Function(Map<String, dynamic>) startEventListener(String peerId) { StreamEventHandler startEventListener(String peerId) {
return (evt) { return (evt) async {
var name = evt['name']; var name = evt['name'];
if (name == 'msgbox') { if (name == 'msgbox') {
handleMsgBox(evt, peerId); handleMsgBox(evt, peerId);
@ -140,11 +140,11 @@ class FfiModel with ChangeNotifier {
} else if (name == 'switch_display') { } else if (name == 'switch_display') {
handleSwitchDisplay(evt); handleSwitchDisplay(evt);
} else if (name == 'cursor_data') { } else if (name == 'cursor_data') {
parent.target?.cursorModel.updateCursorData(evt); await parent.target?.cursorModel.updateCursorData(evt);
} else if (name == 'cursor_id') { } else if (name == 'cursor_id') {
parent.target?.cursorModel.updateCursorId(evt); await parent.target?.cursorModel.updateCursorId(evt);
} else if (name == 'cursor_position') { } else if (name == 'cursor_position') {
parent.target?.cursorModel.updateCursorPosition(evt, peerId); await parent.target?.cursorModel.updateCursorPosition(evt, peerId);
} else if (name == 'clipboard') { } else if (name == 'clipboard') {
Clipboard.setData(ClipboardData(text: evt['content'])); Clipboard.setData(ClipboardData(text: evt['content']));
} else if (name == 'permission') { } else if (name == 'permission') {
@ -358,11 +358,11 @@ class ImageModel with ChangeNotifier {
}); });
} }
void update(ui.Image? image, double tabBarHeight) { void update(ui.Image? image, double tabBarHeight) async {
if (_image == null && image != null) { if (_image == null && image != null) {
if (isWebDesktop || isDesktop) { if (isWebDesktop || isDesktop) {
parent.target?.canvasModel.updateViewStyle(); await parent.target?.canvasModel.updateViewStyle();
parent.target?.canvasModel.updateScrollStyle(); await parent.target?.canvasModel.updateScrollStyle();
} else { } else {
final size = MediaQueryData.fromWindow(ui.window).size; final size = MediaQueryData.fromWindow(ui.window).size;
final canvasWidth = size.width; final canvasWidth = size.width;
@ -372,14 +372,12 @@ class ImageModel with ChangeNotifier {
parent.target?.canvasModel.scale = min(xscale, yscale); parent.target?.canvasModel.scale = min(xscale, yscale);
} }
if (parent.target != null) { if (parent.target != null) {
initializeCursorAndCanvas(parent.target!); await initializeCursorAndCanvas(parent.target!);
}
if (parent.target?.ffiModel.isPeerAndroid ?? false) {
bind.sessionPeerOption(id: _id, name: 'view-style', value: 'adaptive');
parent.target?.canvasModel.updateViewStyle();
} }
Future.delayed(Duration(milliseconds: 1), () {
if (parent.target?.ffiModel.isPeerAndroid ?? false) {
bind.sessionPeerOption(id: _id, name: 'view-style', value: 'shrink');
parent.target?.canvasModel.updateViewStyle();
}
});
} }
_image = image; _image = image;
if (image != null) notifyListeners(); if (image != null) notifyListeners();
@ -445,7 +443,7 @@ class CanvasModel with ChangeNotifier {
double get scrollX => _scrollX; double get scrollX => _scrollX;
double get scrollY => _scrollY; double get scrollY => _scrollY;
void updateViewStyle() async { updateViewStyle() async {
final style = await bind.sessionGetOption(id: id, arg: 'view-style'); final style = await bind.sessionGetOption(id: id, arg: 'view-style');
if (style == null) { if (style == null) {
return; return;
@ -457,7 +455,6 @@ class CanvasModel with ChangeNotifier {
final s2 = size.height / getDisplayHeight(); final s2 = size.height / getDisplayHeight();
_scale = s1 < s2 ? s1 : s2; _scale = s1 < s2 ? s1 : s2;
} }
_x = (size.width - getDisplayWidth() * _scale) / 2; _x = (size.width - getDisplayWidth() * _scale) / 2;
_y = (size.height - getDisplayHeight() * _scale) / 2; _y = (size.height - getDisplayHeight() * _scale) / 2;
notifyListeners(); notifyListeners();
@ -475,7 +472,7 @@ class CanvasModel with ChangeNotifier {
notifyListeners(); notifyListeners();
} }
void update(double x, double y, double scale) { update(double x, double y, double scale) {
_x = x; _x = x;
_y = y; _y = y;
_scale = scale; _scale = scale;
@ -508,19 +505,9 @@ class CanvasModel with ChangeNotifier {
var dxOffset = 0; var dxOffset = 0;
var dyOffset = 0; var dyOffset = 0;
if (dw > size.width) { if (dw > size.width) {
final X_debugNanOrInfinite = x - dw * (x / size.width) - _x;
if (X_debugNanOrInfinite.isInfinite || X_debugNanOrInfinite.isNaN) {
debugPrint(
'REMOVE ME ============================ X_debugNanOrInfinite $x,$dw,$_scale,${size.width},$_x');
}
dxOffset = (x - dw * (x / size.width) - _x).toInt(); dxOffset = (x - dw * (x / size.width) - _x).toInt();
} }
if (dh > size.height) { if (dh > size.height) {
final Y_debugNanOrInfinite = y - dh * (y / size.height) - _y;
if (Y_debugNanOrInfinite.isInfinite || Y_debugNanOrInfinite.isNaN) {
debugPrint(
'REMOVE ME ============================ Y_debugNanOrInfinite $y,$dh,$_scale,${size.height},$_y');
}
dyOffset = (y - dh * (y / size.height) - _y).toInt(); dyOffset = (y - dh * (y / size.height) - _y).toInt();
} }
_x += dxOffset; _x += dxOffset;
@ -619,7 +606,7 @@ class CursorData {
int _doubleToInt(double v) => (v * 10e6).round().toInt(); int _doubleToInt(double v) => (v * 10e6).round().toInt();
String key(double scale) => String key(double scale) =>
'${peerId}_${id}_${_doubleToInt(hotx)}_${_doubleToInt(hoty)}_${_doubleToInt(width * scale)}_${_doubleToInt(height * scale)}'; '${peerId}_${id}_${_doubleToInt(width * scale)}_${_doubleToInt(height * scale)}';
} }
class CursorModel with ChangeNotifier { class CursorModel with ChangeNotifier {
@ -780,7 +767,7 @@ class CursorModel with ChangeNotifier {
notifyListeners(); notifyListeners();
} }
void updateCursorData(Map<String, dynamic> evt) { updateCursorData(Map<String, dynamic> evt) async {
var id = int.parse(evt['id']); var id = int.parse(evt['id']);
_hotx = double.parse(evt['hotx']); _hotx = double.parse(evt['hotx']);
_hoty = double.parse(evt['hoty']); _hoty = double.parse(evt['hoty']);
@ -789,34 +776,25 @@ class CursorModel with ChangeNotifier {
List<dynamic> colors = json.decode(evt['colors']); List<dynamic> colors = json.decode(evt['colors']);
final rgba = Uint8List.fromList(colors.map((s) => s as int).toList()); final rgba = Uint8List.fromList(colors.map((s) => s as int).toList());
var pid = parent.target?.id; var pid = parent.target?.id;
ui.decodeImageFromPixels(rgba, width, height, ui.PixelFormat.rgba8888, final image = await img.decodeImageFromPixels(
(image) { rgba, width, height, ui.PixelFormat.rgba8888);
() async { if (parent.target?.id != pid) return;
if (parent.target?.id != pid) return; _image = image;
_image = image; _images[id] = Tuple3(image, _hotx, _hoty);
_images[id] = Tuple3(image, _hotx, _hoty); await _updateCacheLinux(image, id, width, height);
_updateCacheLinux(image, id, width, height); try {
try { // my throw exception, because the listener maybe already dispose
// my throw exception, because the listener maybe already dispose notifyListeners();
notifyListeners(); } catch (e) {
} catch (e) { debugPrint('notify cursor: $e');
debugPrint('notify cursor: $e'); }
}
}();
});
} }
void _updateCacheLinux(ui.Image image, int id, int w, int h) async { _updateCacheLinux(ui.Image image, int id, int w, int h) async {
final data = await image.toByteData(format: ImageByteFormat.png); final data = await image.toByteData(format: ui.ImageByteFormat.png);
late Uint8List? dataLinux;
if (data != null) {
dataLinux = data.buffer.asUint8List();
} else {
dataLinux = null;
}
_cacheLinux = CursorData( _cacheLinux = CursorData(
peerId: this.id, peerId: this.id,
data: dataLinux, data: data?.buffer.asUint8List(),
id: id, id: id,
hotx: _hotx, hotx: _hotx,
hoty: _hoty, hoty: _hoty,
@ -826,9 +804,10 @@ class CursorModel with ChangeNotifier {
_cacheMapLinux[id] = _cacheLinux!; _cacheMapLinux[id] = _cacheLinux!;
} }
void updateCursorId(Map<String, dynamic> evt) { updateCursorId(Map<String, dynamic> evt) async {
_cacheLinux = _cacheMapLinux[int.parse(evt['id'])]; final id = int.parse(evt['id']);
final tmp = _images[int.parse(evt['id'])]; _cacheLinux = _cacheMapLinux[id];
final tmp = _images[id];
if (tmp != null) { if (tmp != null) {
_image = tmp.item1; _image = tmp.item1;
_hotx = tmp.item2; _hotx = tmp.item2;
@ -838,7 +817,7 @@ class CursorModel with ChangeNotifier {
} }
/// Update the cursor position. /// Update the cursor position.
void updateCursorPosition(Map<String, dynamic> evt, String id) { updateCursorPosition(Map<String, dynamic> evt, String id) async {
_x = double.parse(evt['x']); _x = double.parse(evt['x']);
_y = double.parse(evt['y']); _y = double.parse(evt['y']);
try { try {
@ -1135,9 +1114,9 @@ class FFI {
if (message is Event) { if (message is Event) {
try { try {
Map<String, dynamic> event = json.decode(message.field0); Map<String, dynamic> event = json.decode(message.field0);
cb(event); await cb(event);
} catch (e) { } catch (e) {
print('json.decode fail(): $e'); debugPrint('json.decode fail(): $e');
} }
} else if (message is Rgba) { } else if (message is Rgba) {
imageModel.onRgba(message.field0, tabBarHeight); imageModel.onRgba(message.field0, tabBarHeight);
@ -1299,6 +1278,15 @@ class Display {
double y = 0; double y = 0;
int width = 0; int width = 0;
int height = 0; int height = 0;
Display() {
width = (isDesktop || isWebDesktop)
? kDesktopDefaultDisplayWidth
: kMobileDefaultDisplayWidth;
height = (isDesktop || isWebDesktop)
? kDesktopDefaultDisplayHeight
: kMobileDefaultDisplayHeight;
}
} }
class PeerInfo { class PeerInfo {
@ -1311,8 +1299,8 @@ class PeerInfo {
List<Display> displays = []; List<Display> displays = [];
} }
Future<void> savePreference(String id, double xCursor, double yCursor, savePreference(String id, double xCursor, double yCursor, double xCanvas,
double xCanvas, double yCanvas, double scale, int currentDisplay) async { double yCanvas, double scale, int currentDisplay) async {
SharedPreferences prefs = await SharedPreferences.getInstance(); SharedPreferences prefs = await SharedPreferences.getInstance();
final p = <String, dynamic>{}; final p = <String, dynamic>{};
p['xCursor'] = xCursor; p['xCursor'] = xCursor;
@ -1333,12 +1321,12 @@ Future<Map<String, dynamic>?> getPreference(String id) async {
return m; return m;
} }
void removePreference(String id) async { removePreference(String id) async {
SharedPreferences prefs = await SharedPreferences.getInstance(); SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.remove('peer$id'); prefs.remove('peer$id');
} }
void initializeCursorAndCanvas(FFI ffi) async { initializeCursorAndCanvas(FFI ffi) async {
var p = await getPreference(ffi.id); var p = await getPreference(ffi.id);
int currentDisplay = 0; int currentDisplay = 0;
if (p != null) { if (p != null) {

View File

@ -22,7 +22,7 @@ class RgbaFrame extends Struct {
typedef F2 = Pointer<Utf8> Function(Pointer<Utf8>, Pointer<Utf8>); typedef F2 = Pointer<Utf8> Function(Pointer<Utf8>, Pointer<Utf8>);
typedef F3 = void Function(Pointer<Utf8>, Pointer<Utf8>); typedef F3 = void Function(Pointer<Utf8>, Pointer<Utf8>);
typedef HandleEvent = void Function(Map<String, dynamic> evt); typedef HandleEvent = Future<void> Function(Map<String, dynamic> evt);
/// FFI wrapper around the native Rust core. /// FFI wrapper around the native Rust core.
/// Hides the platform differences. /// Hides the platform differences.
@ -30,15 +30,15 @@ class PlatformFFI {
String _dir = ''; String _dir = '';
String _homeDir = ''; String _homeDir = '';
F2? _translate; F2? _translate;
final _eventHandlers = Map<String, Map<String, HandleEvent>>(); final _eventHandlers = <String, Map<String, HandleEvent>>{};
late RustdeskImpl _ffiBind; late RustdeskImpl _ffiBind;
late String _appType; late String _appType;
void Function(Map<String, dynamic>)? _eventCallback; StreamEventHandler? _eventCallback;
PlatformFFI._(); PlatformFFI._();
static final PlatformFFI instance = PlatformFFI._(); static final PlatformFFI instance = PlatformFFI._();
final _toAndroidChannel = MethodChannel("mChannel"); final _toAndroidChannel = const MethodChannel("mChannel");
RustdeskImpl get ffiBind => _ffiBind; RustdeskImpl get ffiBind => _ffiBind;
@ -88,7 +88,7 @@ class PlatformFFI {
} }
/// Init the FFI class, loads the native Rust core library. /// Init the FFI class, loads the native Rust core library.
Future<Null> init(String appType) async { Future<void> init(String appType) async {
_appType = appType; _appType = appType;
// if (isDesktop) { // if (isDesktop) {
// // TODO // // TODO
@ -117,7 +117,7 @@ class PlatformFFI {
_homeDir = (await getDownloadsDirectory())?.path ?? ""; _homeDir = (await getDownloadsDirectory())?.path ?? "";
} }
} catch (e) { } catch (e) {
print("initialize failed: $e"); debugPrint('initialize failed: $e');
} }
String id = 'NA'; String id = 'NA';
String name = 'Flutter'; String name = 'Flutter';
@ -144,27 +144,27 @@ class PlatformFFI {
name = macOsInfo.computerName; name = macOsInfo.computerName;
id = macOsInfo.systemGUID ?? ""; id = macOsInfo.systemGUID ?? "";
} }
print( debugPrint(
"_appType:$_appType,info1-id:$id,info2-name:$name,dir:$_dir,homeDir:$_homeDir"); '_appType:$_appType,info1-id:$id,info2-name:$name,dir:$_dir,homeDir:$_homeDir');
await _ffiBind.mainDeviceId(id: id); await _ffiBind.mainDeviceId(id: id);
await _ffiBind.mainDeviceName(name: name); await _ffiBind.mainDeviceName(name: name);
await _ffiBind.mainSetHomeDir(home: _homeDir); await _ffiBind.mainSetHomeDir(home: _homeDir);
await _ffiBind.mainInit(appDir: _dir); await _ffiBind.mainInit(appDir: _dir);
} catch (e) { } catch (e) {
print("initialize failed: $e"); debugPrint('initialize failed: $e');
} }
version = await getVersion(); version = await getVersion();
} }
bool _tryHandle(Map<String, dynamic> evt) { Future<bool> _tryHandle(Map<String, dynamic> evt) async {
final name = evt['name']; final name = evt['name'];
if (name != null) { if (name != null) {
final handlers = _eventHandlers[name]; final handlers = _eventHandlers[name];
if (handlers != null) { if (handlers != null) {
if (handlers.isNotEmpty) { if (handlers.isNotEmpty) {
handlers.values.forEach((handler) { for (var handler in handlers.values) {
handler(evt); await handler(evt);
}); }
return true; return true;
} }
} }
@ -180,19 +180,19 @@ class PlatformFFI {
try { try {
Map<String, dynamic> event = json.decode(message); Map<String, dynamic> event = json.decode(message);
// _tryHandle here may be more flexible than _eventCallback // _tryHandle here may be more flexible than _eventCallback
if (!_tryHandle(event)) { if (!await _tryHandle(event)) {
if (_eventCallback != null) { if (_eventCallback != null) {
_eventCallback!(event); await _eventCallback!(event);
} }
} }
} catch (e) { } catch (e) {
print('json.decode fail(): $e'); debugPrint('json.decode fail(): $e');
} }
} }
}(); }();
} }
void setEventCallback(void Function(Map<String, dynamic>) fun) async { void setEventCallback(StreamEventHandler fun) async {
_eventCallback = fun; _eventCallback = fun;
} }

View File

@ -40,10 +40,10 @@ class Peers extends ChangeNotifier {
static const _cbQueryOnlines = 'callback_query_onlines'; static const _cbQueryOnlines = 'callback_query_onlines';
Peers({required this.name, required this.peers, required this.loadEvent}) { Peers({required this.name, required this.peers, required this.loadEvent}) {
platformFFI.registerEventHandler(_cbQueryOnlines, name, (evt) { platformFFI.registerEventHandler(_cbQueryOnlines, name, (evt) async {
_updateOnlineState(evt); _updateOnlineState(evt);
}); });
platformFFI.registerEventHandler(loadEvent, name, (evt) { platformFFI.registerEventHandler(loadEvent, name, (evt) async {
_updatePeers(evt); _updatePeers(evt);
}); });
} }

View File

@ -0,0 +1,49 @@
import 'dart:typed_data';
import 'dart:ui' as ui;
Future<ui.Image> 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;
}