From 8b807d7b5048ce9f94c923e54f92ffe18c9044af Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 3 Oct 2023 21:16:12 +0800 Subject: [PATCH] fix, cursor (hotx,hoty) mismatch sometimes Signed-off-by: fufesou --- flutter/lib/desktop/pages/remote_page.dart | 2 +- flutter/lib/models/model.dart | 73 +++++++++++++--------- 2 files changed, 46 insertions(+), 29 deletions(-) diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 13271378d..fee5bd8f4 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -614,7 +614,7 @@ class _ImagePaintState extends State { } else { final key = cache.updateGetKey(scale); if (!cursor.cachedKeys.contains(key)) { - debugPrint("Register custom cursor with key $key"); + debugPrint("Register custom cursor with key $key (${cache.hotx},${cache.hoty})"); // [Safety] // It's ok to call async registerCursor in current synchronous context, // because activating the cursor is also an async call and will always diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index a7082eb4b..d8ef19e08 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -1279,6 +1279,7 @@ class CursorModel with ChangeNotifier { final _cacheKeys = {}; double _x = -10000; double _y = -10000; + int _id = -1; double _hotx = 0; double _hoty = 0; double _displayOriginX = 0; @@ -1287,7 +1288,7 @@ class CursorModel with ChangeNotifier { bool gotMouseControl = true; DateTime _lastPeerMouse = DateTime.now() .subtract(Duration(milliseconds: 3000 * kMouseControlTimeoutMSec)); - String id = ''; + String peerId = ''; WeakReference parent; ui.Image? get image => _image; @@ -1439,32 +1440,35 @@ class CursorModel with ChangeNotifier { } updateCursorData(Map evt) async { - var id = int.parse(evt['id']); - _hotx = double.parse(evt['hotx']); - _hoty = double.parse(evt['hoty']); - var width = int.parse(evt['width']); - var height = int.parse(evt['height']); + final id = int.parse(evt['id']); + // Update last cursor id. + _id = id; + final hotx = double.parse(evt['hotx']); + final hoty = double.parse(evt['hoty']); + final width = int.parse(evt['width']); + final height = int.parse(evt['height']); List colors = json.decode(evt['colors']); final rgba = Uint8List.fromList(colors.map((s) => s as int).toList()); final image = await img.decodeImageFromPixels( rgba, width, height, ui.PixelFormat.rgba8888); - _image = image; - if (await _updateCache(rgba, image, id, width, height)) { - _images[id] = Tuple3(image, _hotx, _hoty); - } else { - _hotx = 0; - _hoty = 0; - } - try { - // my throw exception, because the listener maybe already dispose - notifyListeners(); - } catch (e) { - debugPrint('WARNING: updateCursorId $id, without notifyListeners(). $e'); + if (await _updateCache(rgba, image, id, hotx, hoty, width, height)) { + _images[id] = Tuple3(image, hotx, hoty); } + + // Update last cursor data. + // Do not use the previous `image` and `id`, because `_id` may be changed. + _updateCursorIdData(_id); } Future _updateCache( - Uint8List rgba, ui.Image image, int id, int w, int h) async { + Uint8List rgba, + ui.Image image, + int id, + double hotx, + double hoty, + int w, + int h, + ) async { Uint8List? data; img2.Image imgOrigin = img2.Image.fromBytes( width: w, height: h, bytes: rgba.buffer, order: img2.ChannelOrder.rgba); @@ -1478,31 +1482,44 @@ class CursorModel with ChangeNotifier { } data = imgBytes.buffer.asUint8List(); } - _cache = CursorData( - peerId: this.id, + final cache = CursorData( + peerId: peerId, id: id, image: imgOrigin, scale: 1.0, data: data, - hotxOrigin: _hotx, - hotyOrigin: _hoty, + hotxOrigin: hotx, + hotyOrigin: hoty, width: w, height: h, ); - _cacheMap[id] = _cache!; + _cacheMap[id] = cache; return true; } - updateCursorId(Map evt) async { - final id = int.parse(evt['id']); + bool _updateCursorIdData(int id) { _cache = _cacheMap[id]; final tmp = _images[id]; if (tmp != null) { _image = tmp.item1; _hotx = tmp.item2; _hoty = tmp.item3; - notifyListeners(); + try { + // may throw exception, because the listener maybe already dispose + notifyListeners(); + } catch (e) { + debugPrint( + 'WARNING: updateCursorId $id, without notifyListeners(). $e'); + } + return true; } else { + return false; + } + } + + updateCursorId(Map evt) async { + final id = int.parse(evt['id']); + if (!_updateCursorIdData(id)) { debugPrint( 'WARNING: updateCursorId $id, cache is ${_cache == null ? "null" : "not null"}. without notifyListeners()'); } @@ -1748,7 +1765,7 @@ class FFI { connType = ConnType.defaultConn; canvasModel.id = id; imageModel.id = id; - cursorModel.id = id; + cursorModel.peerId = id; } // If tabWindowId != null, this session is a "tab -> window" one. // Else this session is a new one.