diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 4cd520742..86851fa26 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -1425,8 +1425,9 @@ class CursorPaint extends StatelessWidget { } } - double cx = c.x; - double cy = c.y; + final imageOffset = _softwareImageOffset(c); + double cx = imageOffset?.dx ?? c.x; + double cy = imageOffset?.dy ?? c.y; if (c.viewStyle.style == kRemoteViewStyleOriginal && c.scrollStyle == ScrollStyle.scrollbar) { final rect = c.parent.target!.ffiModel.rect; @@ -1467,4 +1468,22 @@ class CursorPaint extends StatelessWidget { ), ); } + + Offset? _softwareImageOffset(CanvasModel canvas) { + if (canvas.imageOverflow.isTrue && + canvas.scrollStyle != ScrollStyle.scrollauto) { + return null; + } + final ffi = canvas.parent.target!; + final peer = ffi.ffiModel; + if (ffi.imageModel.useTextureRender || peer.pi.forceTextureRender) { + return null; + } + var scale = canvas.scale; + final displays = peer.pi.getCurDisplays(); + if (peer.isPeerLinux && displays.isNotEmpty) scale /= displays[0].scale; + // Match the origin used by _buildScrollAutoNonTextureRender's ImagePainter. + return Offset( + (canvas.x / scale).toInt() * scale, (canvas.y / scale).toInt() * scale); + } } diff --git a/flutter/test/cursor_paint_scale_test.dart b/flutter/test/cursor_paint_scale_test.dart index 3e902113b..7b393aaed 100644 --- a/flutter/test/cursor_paint_scale_test.dart +++ b/flutter/test/cursor_paint_scale_test.dart @@ -33,9 +33,33 @@ class _CursorModel extends ChangeNotifier implements CursorModel { dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); } +class _ImageModel extends Fake implements ImageModel { + _ImageModel(this.useTextureRender); + + @override + final bool useTextureRender; +} + +class _Peer extends Fake implements FfiModel { + @override + final pi = PeerInfo(); + @override + bool get isPeerLinux => false; +} + +class _FFI extends Fake implements FFI { + _FFI(bool useTexture) : imageModel = _ImageModel(useTexture); + + @override + final ImageModel imageModel; + @override + final ffiModel = _Peer(); +} + class _CanvasModel extends ChangeNotifier implements CanvasModel { - _CanvasModel(String style, this.scale) - : viewStyle = ViewStyle( + _CanvasModel(String style, this.scale, bool useTexture) + : _ffi = _FFI(useTexture), + viewStyle = ViewStyle( style: style, width: _viewport.width, height: _viewport.height, @@ -43,6 +67,11 @@ class _CanvasModel extends ChangeNotifier implements CanvasModel { displayHeight: _viewport.height.toInt(), ); + final FFI _ffi; + @override + WeakReference get parent => WeakReference(_ffi); + @override + final imageOverflow = false.obs; @override final ViewStyle viewStyle; @override @@ -72,19 +101,20 @@ class _Canvas extends Fake implements Canvas { } void main() { - for (final (style, zoom, dpr, source, canvasScale, scale) in [ - (kRemoteViewStyleAdaptive, false, 2.0, (48, 64), 0.375, 0.375), - (kRemoteViewStyleAdaptive, false, 3.0, (48, 64), 0.25, 0.25), - (kRemoteViewStyleAdaptive, true, 3.0, (48, 64), 0.375, 0.375), - (kRemoteViewStyleOriginal, false, 2.0, (48, 64), 0.5, 0.5), - (kRemoteViewStyleOriginal, true, 2.0, (48, 64), 0.5, 0.5), + for (final (style, zoom, dpr, source, canvasScale, scale, texture) in [ + (kRemoteViewStyleAdaptive, false, 2.0, (48, 64), 0.375, 0.375, true), + (kRemoteViewStyleAdaptive, false, 3.0, (48, 64), 0.25, 0.25, true), + (kRemoteViewStyleAdaptive, true, 3.0, (48, 64), 0.375, 0.375, true), + (kRemoteViewStyleOriginal, false, 2.0, (48, 64), 0.5, 0.5, true), + (kRemoteViewStyleOriginal, true, 2.0, (48, 64), 0.5, 0.5, true), ( kRemoteViewStyleAdaptive, false, 2.0, (9, 18), 0.1, - Platform.isWindows ? 2 / 3 : 4 / 3 + Platform.isWindows ? 2 / 3 : 4 / 3, + true ), ( kRemoteViewStyleAdaptive, @@ -92,11 +122,22 @@ void main() { 2.0, (9, 18), 0.1, - Platform.isWindows ? 2 / 3 : 4 / 3 + Platform.isWindows ? 2 / 3 : 4 / 3, + true + ), + (kRemoteViewStyleAdaptive, false, 2.25, (48, 48), 0.375, 0.375, false), + ( + kRemoteViewStyleAdaptive, + true, + 2.0, + (9, 18), + 0.1, + Platform.isWindows ? 2 / 3 : 4 / 3, + false ), ]) { testWidgets( - '$style zoom=$zoom dpr=$dpr source=$source keeps remote geometry', + '$style zoom=$zoom dpr=$dpr source=$source texture=$texture keeps remote geometry', (tester) async { final image = (await tester.runAsync( () => createTestImage(width: source.$1, height: source.$2)))!; @@ -108,7 +149,7 @@ void main() { ChangeNotifierProvider( create: (_) => _CursorModel(image)), ChangeNotifierProvider( - create: (_) => _CanvasModel(style, canvasScale)), + create: (_) => _CanvasModel(style, canvasScale, texture)), ], child: CursorPaint(id: 'cursor-test', zoomCursor: zoom.obs), ), @@ -119,12 +160,22 @@ void main() { expect(painter.image, same(image)); expect(painter.scale, scale); - expect((Offset(painter.x, painter.y) + _hotspot) * scale, - _remotePosition * canvasScale + _canvasOffset); + var imageOrigin = _canvasOffset; + if (!texture) { + final background = _Canvas(); + ImagePainter( + image: image, + x: _canvasOffset.dx / canvasScale, + y: _canvasOffset.dy / canvasScale, + scale: canvasScale, + ).paint(background, _viewport); + imageOrigin = background.position!; + } + final target = _remotePosition * canvasScale + imageOrigin; + expect((Offset(painter.x, painter.y) + _hotspot) * scale, target); final canvas = _Canvas(); painter.paint(canvas, _viewport); final position = canvas.position! + _hotspot * canvas.factor; - final target = _remotePosition * canvasScale + _canvasOffset; expect(position.dx, closeTo(target.dx, 1e-9)); expect(position.dy, closeTo(target.dy, 1e-9)); });