mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-17 18:01:06 +03:00
fix(cursor): align painted and native minimum sizes
This commit is contained in:
@@ -1423,10 +1423,14 @@ class CursorPaint extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final image = m.image ?? preDefaultCursor.image;
|
final image = m.image ?? preDefaultCursor.image;
|
||||||
final nativePixels = isWindows ? MediaQuery.devicePixelRatioOf(context) : 1.0;
|
// Match native registration's logical minimum for density-aware scaled views.
|
||||||
|
final logicalMinimum = (m.cache?.pixelRatio ?? 0) > 0 &&
|
||||||
|
c.viewStyle.style != kRemoteViewStyleOriginal;
|
||||||
|
final nativePixels = isWindows && !logicalMinimum
|
||||||
|
? MediaQuery.devicePixelRatioOf(context) : 1.0;
|
||||||
// Show remote cursor follows the image scale, independently of Zoom cursor.
|
// Show remote cursor follows the image scale, independently of Zoom cursor.
|
||||||
double scale = _cursorImageScale(c.parent.target!, m);
|
double scale = _cursorImageScale(c.parent.target!, m);
|
||||||
if (image != null && scale * nativePixels != 1.0) {
|
if (image != null && (logicalMinimum || scale * nativePixels != 1.0)) {
|
||||||
final sx = kMinCursorSize / (image.width * nativePixels);
|
final sx = kMinCursorSize / (image.width * nativePixels);
|
||||||
final sy = kMinCursorSize / (image.height * nativePixels);
|
final sy = kMinCursorSize / (image.height * nativePixels);
|
||||||
// Preserve Original's short-edge minimum; scaled views use the long edge.
|
// Preserve Original's short-edge minimum; scaled views use the long edge.
|
||||||
|
|||||||
@@ -16,9 +16,12 @@ const _canvasOffset = Offset(15.125, 10.25);
|
|||||||
const _viewport = Size(200, 160);
|
const _viewport = Size(200, 160);
|
||||||
|
|
||||||
class _CursorModel extends ChangeNotifier implements CursorModel {
|
class _CursorModel extends ChangeNotifier implements CursorModel {
|
||||||
_CursorModel(this.image, this.position);
|
_CursorModel(this.image, this.position, this.density);
|
||||||
|
|
||||||
final Offset position;
|
final Offset position;
|
||||||
|
final double density;
|
||||||
|
@override
|
||||||
|
CursorData get cache => _Density(density);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final ui.Image image;
|
final ui.Image image;
|
||||||
@@ -35,6 +38,12 @@ class _CursorModel extends ChangeNotifier implements CursorModel {
|
|||||||
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _Density extends Fake implements CursorData {
|
||||||
|
_Density(this.pixelRatio);
|
||||||
|
@override
|
||||||
|
final double pixelRatio;
|
||||||
|
}
|
||||||
|
|
||||||
class _ImageModel extends Fake implements ImageModel {
|
class _ImageModel extends Fake implements ImageModel {
|
||||||
_ImageModel(this.useTextureRender);
|
_ImageModel(this.useTextureRender);
|
||||||
|
|
||||||
@@ -136,7 +145,7 @@ class _Canvas extends Fake implements Canvas {
|
|||||||
|
|
||||||
Future<ImagePainter> _paintCursor(WidgetTester tester, CanvasModel canvas,
|
Future<ImagePainter> _paintCursor(WidgetTester tester, CanvasModel canvas,
|
||||||
{double dpr = 2, bool zoom = true, (int, int) source = (48, 64),
|
{double dpr = 2, bool zoom = true, (int, int) source = (48, 64),
|
||||||
Offset position = _remotePosition}) async {
|
Offset position = _remotePosition, double density = 0}) async {
|
||||||
final image = (await tester
|
final image = (await tester
|
||||||
.runAsync(() => createTestImage(width: source.$1, height: source.$2)))!;
|
.runAsync(() => createTestImage(width: source.$1, height: source.$2)))!;
|
||||||
addTearDown(image.dispose);
|
addTearDown(image.dispose);
|
||||||
@@ -144,7 +153,7 @@ Future<ImagePainter> _paintCursor(WidgetTester tester, CanvasModel canvas,
|
|||||||
data: MediaQueryData(devicePixelRatio: dpr),
|
data: MediaQueryData(devicePixelRatio: dpr),
|
||||||
child: MultiProvider(
|
child: MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
ChangeNotifierProvider<CursorModel>(create: (_) => _CursorModel(image, position)),
|
ChangeNotifierProvider<CursorModel>(create: (_) => _CursorModel(image, position, density)),
|
||||||
ChangeNotifierProvider<CanvasModel>(create: (_) => canvas),
|
ChangeNotifierProvider<CanvasModel>(create: (_) => canvas),
|
||||||
],
|
],
|
||||||
child: CursorPaint(id: 'cursor-test', zoomCursor: zoom.obs),
|
child: CursorPaint(id: 'cursor-test', zoomCursor: zoom.obs),
|
||||||
@@ -156,7 +165,7 @@ Future<ImagePainter> _paintCursor(WidgetTester tester, CanvasModel canvas,
|
|||||||
return painter;
|
return painter;
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void _linuxDisplayTests() {
|
||||||
for (final (texture, displayScale, allDisplays) in [
|
for (final (texture, displayScale, allDisplays) in [
|
||||||
(false, 2.0, false), (true, 2.0, false), (true, 2.0, true),
|
(false, 2.0, false), (true, 2.0, false), (true, 2.0, true),
|
||||||
(false, 1.0, false), (true, 1.0, false),
|
(false, 1.0, false), (true, 1.0, false),
|
||||||
@@ -175,7 +184,7 @@ void main() {
|
|||||||
: peer.pi.displays.length - 1;
|
: peer.pi.displays.length - 1;
|
||||||
peer.rect = Rect.fromLTWH(allDisplays ? -960 : 0, 0, 3840, 2160);
|
peer.rect = Rect.fromLTWH(allDisplays ? -960 : 0, 0, 3840, 2160);
|
||||||
final painter = await _paintCursor(tester, canvas,
|
final painter = await _paintCursor(tester, canvas,
|
||||||
dpr: 1, zoom: false, source: (64, 64),
|
dpr: 1, zoom: false, source: (64, 64), density: 2,
|
||||||
position: allDisplays ? _remotePosition + const Offset(960, 0)
|
position: allDisplays ? _remotePosition + const Offset(960, 0)
|
||||||
: _remotePosition);
|
: _remotePosition);
|
||||||
final pixelScale = 2 / displayScale;
|
final pixelScale = 2 / displayScale;
|
||||||
@@ -189,6 +198,35 @@ void main() {
|
|||||||
position * 2 + origin);
|
position * 2 + origin);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _minimumTests() {
|
||||||
|
for (final (style, dpr, source, viewScale, expected) in [
|
||||||
|
(kRemoteViewStyleAdaptive, 1.0, (48, 64), 0.05, 0.1875),
|
||||||
|
(kRemoteViewStyleAdaptive, 2.0, (48, 64), 0.05, 0.1875),
|
||||||
|
(kRemoteViewStyleCustom, 1.0, (48, 64), 0.05, 0.1875),
|
||||||
|
(kRemoteViewStyleCustom, 2.0, (48, 64), 0.05, 0.1875),
|
||||||
|
(kRemoteViewStyleCustom, 2.0, (8, 10), 1.0, 1.2),
|
||||||
|
(kRemoteViewStyleOriginal, 1.0, (48, 64), 0.05, 0.25),
|
||||||
|
(kRemoteViewStyleOriginal, 2.0, (48, 64), 0.05, Platform.isWindows ? 0.125 : 0.25),
|
||||||
|
]) {
|
||||||
|
testWidgets('density-aware $style DPR=$dpr source=$source minimum',
|
||||||
|
(tester) async {
|
||||||
|
final painter = await _paintCursor(tester,
|
||||||
|
_CanvasModel(style, viewScale, true),
|
||||||
|
dpr: dpr, source: source, density: 2);
|
||||||
|
expect(painter.scale, expected);
|
||||||
|
final hotspot = (Offset(painter.x, painter.y) + _hotspot) * painter.scale;
|
||||||
|
final target = _remotePosition * viewScale + _canvasOffset;
|
||||||
|
expect(hotspot.dx, closeTo(target.dx, 1e-9));
|
||||||
|
expect(hotspot.dy, closeTo(target.dy, 1e-9));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
_linuxDisplayTests();
|
||||||
|
_minimumTests();
|
||||||
final minimumScale = Platform.isWindows ? 1 / 3 : 2 / 3;
|
final minimumScale = Platform.isWindows ? 1 / 3 : 2 / 3;
|
||||||
for (final (style, zoom, dpr, source, canvasScale, scale, texture) in [
|
for (final (style, zoom, dpr, source, canvasScale, scale, texture) in [
|
||||||
(kRemoteViewStyleAdaptive, false, 2.0, (48, 64), 0.375, 0.375, true),
|
(kRemoteViewStyleAdaptive, false, 2.0, (48, 64), 0.375, 0.375, true),
|
||||||
|
|||||||
Reference in New Issue
Block a user