Merge pull request #1497 from fufesou/flutter_desktop_cursors
Flutter desktop cursors
This commit is contained in:
commit
a0c46c905d
@ -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(
|
||||||
|
@ -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(),
|
||||||
);
|
);
|
||||||
|
@ -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,
|
||||||
|
@ -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();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
49
flutter/lib/utils/image.dart
Normal file
49
flutter/lib/utils/image.dart
Normal 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;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user