From 8b807d7b5048ce9f94c923e54f92ffe18c9044af Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 3 Oct 2023 21:16:12 +0800 Subject: [PATCH 1/3] 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. From 197a9330df3a5297a8b0ad634026443534883b10 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 3 Oct 2023 21:18:53 +0800 Subject: [PATCH 2/3] fix, cursor hotxy, use _id Signed-off-by: fufesou --- flutter/lib/models/model.dart | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index d8ef19e08..7bdfa9b3d 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -1457,7 +1457,7 @@ class CursorModel with ChangeNotifier { // Update last cursor data. // Do not use the previous `image` and `id`, because `_id` may be changed. - _updateCursorIdData(_id); + _updateCurData(); } Future _updateCache( @@ -1497,9 +1497,9 @@ class CursorModel with ChangeNotifier { return true; } - bool _updateCursorIdData(int id) { - _cache = _cacheMap[id]; - final tmp = _images[id]; + bool _updateCurData() { + _cache = _cacheMap[_id]; + final tmp = _images[_id]; if (tmp != null) { _image = tmp.item1; _hotx = tmp.item2; @@ -1509,7 +1509,7 @@ class CursorModel with ChangeNotifier { notifyListeners(); } catch (e) { debugPrint( - 'WARNING: updateCursorId $id, without notifyListeners(). $e'); + 'WARNING: updateCursorId $_id, without notifyListeners(). $e'); } return true; } else { @@ -1518,10 +1518,10 @@ class CursorModel with ChangeNotifier { } updateCursorId(Map evt) async { - final id = int.parse(evt['id']); - if (!_updateCursorIdData(id)) { + _id = int.parse(evt['id']); + if (!_updateCurData()) { debugPrint( - 'WARNING: updateCursorId $id, cache is ${_cache == null ? "null" : "not null"}. without notifyListeners()'); + 'WARNING: updateCursorId $_id, cache is ${_cache == null ? "null" : "not null"}. without notifyListeners()'); } } From 5d6d8e68ed9b2517b3198a482dfef485cb85b205 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 3 Oct 2023 22:15:58 +0800 Subject: [PATCH 3/3] Update cursor id immediately after the cursor event Signed-off-by: fufesou --- flutter/lib/models/model.dart | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 7bdfa9b3d..3c42e02a3 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -203,10 +203,12 @@ class FfiModel with ChangeNotifier { updatePrivacyMode(data.updatePrivacyMode, sessionId, peerId); setConnectionType(peerId, data.secure, data.direct); await handlePeerInfo(data.peerInfo, peerId); - for (var element in data.cursorDataList) { + for (final element in data.cursorDataList) { + updateLastCursorId(element); await handleCursorData(element); } - await handleCursorId(data.lastCursorId); + updateLastCursorId(data.lastCursorId); + handleCursorId(data.lastCursorId); } // todo: why called by two position @@ -225,9 +227,11 @@ class FfiModel with ChangeNotifier { } else if (name == 'switch_display') { handleSwitchDisplay(evt, sessionId, peerId); } else if (name == 'cursor_data') { + updateLastCursorId(evt); await handleCursorData(evt); } else if (name == 'cursor_id') { - await handleCursorId(evt); + updateLastCursorId(evt); + handleCursorId(evt); } else if (name == 'cursor_position') { await parent.target?.cursorModel.updateCursorPosition(evt, peerId); } else if (name == 'clipboard') { @@ -651,9 +655,13 @@ class FfiModel with ChangeNotifier { return d; } - handleCursorId(Map evt) async { + updateLastCursorId(Map evt) { + parent.target?.cursorModel.id = int.parse(evt['id']); + } + + handleCursorId(Map evt) { cachedPeerData.lastCursorId = evt; - await parent.target?.cursorModel.updateCursorId(evt); + parent.target?.cursorModel.updateCursorId(evt); } handleCursorData(Map evt) async { @@ -1302,6 +1310,8 @@ class CursorModel with ChangeNotifier { double get hotx => _hotx; double get hoty => _hoty; + set id(int id) => _id = id; + bool get isPeerControlProtected => DateTime.now().difference(_lastPeerMouse).inMilliseconds < kMouseControlTimeoutMSec; @@ -1441,8 +1451,6 @@ class CursorModel with ChangeNotifier { updateCursorData(Map evt) async { 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']); @@ -1517,8 +1525,7 @@ class CursorModel with ChangeNotifier { } } - updateCursorId(Map evt) async { - _id = int.parse(evt['id']); + updateCursorId(Map evt) { if (!_updateCurData()) { debugPrint( 'WARNING: updateCursorId $_id, cache is ${_cache == null ? "null" : "not null"}. without notifyListeners()');