diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 091e69047..14a6a111c 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -1469,7 +1469,10 @@ class CursorPaint extends StatelessWidget { final ffi = canvas.parent.target!; final peer = ffi.ffiModel; if (ffi.imageModel.useTextureRender || peer.pi.forceTextureRender) { - return null; + // Match Linux's texture origin without rounding the cursor or hotspot. + return isLinux + ? Offset(canvas.x.toInt().toDouble(), canvas.y.toInt().toDouble()) + : null; } var scale = canvas.scale; final displays = peer.pi.getCurDisplays(); diff --git a/flutter/test/cursor_paint_scale_test.dart b/flutter/test/cursor_paint_scale_test.dart index 37424b0f6..857bdf968 100644 --- a/flutter/test/cursor_paint_scale_test.dart +++ b/flutter/test/cursor_paint_scale_test.dart @@ -4,6 +4,7 @@ import 'dart:ui' as ui; import 'package:flutter/widgets.dart'; import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/desktop/pages/remote_page.dart'; +import 'package:flutter_hbb/models/desktop_render_texture.dart'; import 'package:flutter_hbb/models/model.dart'; import 'package:flutter_hbb/utils/image.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -12,7 +13,7 @@ import 'package:provider/provider.dart'; const _hotspot = Offset(4, 9); const _remotePosition = Offset(100.25, 80.75); -const _canvasOffset = Offset(15.125, 10.25); +const _canvasOffset = Offset(15.125, -10.25); const _viewport = Size(200, 160); class _CursorModel extends ChangeNotifier implements CursorModel { @@ -44,11 +45,18 @@ class _Density extends Fake implements CursorData { final double pixelRatio; } -class _ImageModel extends Fake implements ImageModel { +class _ImageModel extends ChangeNotifier implements ImageModel { _ImageModel(this.useTextureRender); @override final bool useTextureRender; + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +} + +class _TextureModel extends Fake implements TextureModel { + @override + RxInt getTextureId(int display) => 0.obs; } class _Display extends Display { @@ -64,7 +72,10 @@ class _Display extends Display { class _Peer extends Fake implements FfiModel { @override - final pi = PeerInfo(); + final pi = PeerInfo() + ..displays.add(Display() + ..width = _viewport.width.toInt() + ..height = _viewport.height.toInt()); @override bool isPeerLinux = false; @override @@ -79,6 +90,8 @@ class _FFI extends Fake implements FFI { @override final _Peer ffiModel = _Peer(); @override + final textureModel = _TextureModel(); + @override late CanvasModel canvasModel; } @@ -151,16 +164,29 @@ Future _paintCursor(WidgetTester tester, CanvasModel canvas, final image = (await tester .runAsync(() => createTestImage(width: source.$1, height: source.$2)))!; addTearDown(image.dispose); - await tester.pumpWidget(MediaQuery( + tester.view.devicePixelRatio = dpr; + tester.view.physicalSize = _viewport * dpr; + addTearDown(tester.view.reset); + await tester.pumpWidget(Directionality(textDirection: TextDirection.ltr, + child: MediaQuery( data: MediaQueryData(devicePixelRatio: dpr), child: MultiProvider( providers: [ + ChangeNotifierProvider.value(value: canvas.parent.target!.imageModel), ChangeNotifierProvider(create: (_) => _CursorModel(image, position, density)), ChangeNotifierProvider(create: (_) => canvas), ], - child: CursorPaint(id: 'cursor-test', zoomCursor: zoom.obs), + child: Stack(fit: StackFit.expand, children: [ + // Measure the video's origin instead of copying its rounding policy. + if (canvas.parent.target!.imageModel.useTextureRender && + canvas.scrollStyle == ScrollStyle.scrollauto) + ImagePaint(ffi: canvas.parent.target!, id: 'cursor-test', + zoomCursor: zoom.obs, cursorOverImage: false.obs, + keyboardEnabled: true.obs, remoteCursorMoved: false.obs), + CursorPaint(id: 'cursor-test', zoomCursor: zoom.obs), + ]), ), - )); + ))); final painter = tester.widget(find.byType(CustomPaint)).painter! as ImagePainter; expect(painter.image, same(image)); @@ -191,7 +217,7 @@ void _linuxDisplayTests() { : _remotePosition); final pixelScale = 2 / displayScale; expect(painter.scale, pixelScale); - final origin = texture ? _canvasOffset + final origin = texture ? tester.getTopLeft(find.byType(Texture).first) : Offset((_canvasOffset.dx / pixelScale).toInt() * pixelScale, (_canvasOffset.dy / pixelScale).toInt() * pixelScale); final position = allDisplays @@ -228,7 +254,8 @@ void main() { tester, _CanvasModel(style, canvasScale, texture), dpr: dpr, zoom: zoom, source: source, density: density); expect(painter.scale, scale); - var imageOrigin = _canvasOffset; + var imageOrigin = texture + ? tester.getTopLeft(find.byType(Texture).first) : _canvasOffset; if (!texture) { final background = _Canvas(); ImagePainter(