From f5b98b32f2c8bb4fbb3ac461e943cf7bb254751c Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 10 Sep 2026 14:25:51 +0800 Subject: [PATCH] fix(flutter): read the live DPR and clamp the painted cursor like the native one CanvasModel caches devicePixelRatio and only refreshes it when the view style changes, so after the window moves to a monitor with a different DPR the unzoomed cursor kept the previous monitor's scale. Read it from MediaQuery instead, which also rebuilds the cursor when it changes. The native path clamps the scaled bitmap to kMinCursorSize; apply the same clamp to the painted cursor so a small cursor does not change size when the peer moves the mouse. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01K8HSHTEHo27mDcJXVyVfSP --- flutter/lib/desktop/pages/remote_page.dart | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 4be4430e9..78612f48e 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -1101,6 +1101,9 @@ class _ImagePaintState extends State { final m = Provider.of(context); var c = Provider.of(context); final s = c.scale; + // CanvasModel caches the DPR and only refreshes it when the view style + // changes, so read it live to follow the window across monitors. + final dpr = MediaQuery.devicePixelRatioOf(context); bool isViewAdaptive() => c.viewStyle.style == kRemoteViewStyleAdaptive; bool isViewOriginal() => c.viewStyle.style == kRemoteViewStyleOriginal; @@ -1122,7 +1125,7 @@ class _ImagePaintState extends State { // pixels, so an unzoomed cursor must be shrunk by the DPR to // keep 1 remote px == 1 physical px, the size Original view // already renders it at. - cursorScale = 1.0 / c.devicePixelRatio; + cursorScale = 1.0 / dpr; } } return cursorScale; @@ -1420,8 +1423,17 @@ class CursorPaint extends StatelessWidget { scale = c.scale; } else if (!isWindows) { // Keep the painted cursor the same physical size as the native one - // built by getCursorScale() above. - scale = 1.0 / c.devicePixelRatio; + // built by getCursorScale() above, including its min-size clamp. + scale = 1.0 / MediaQuery.devicePixelRatioOf(context); + final image = m.image ?? preDefaultCursor.image; + if (scale != 1.0 && + image != null && + ((image.width * scale).toInt() < kMinCursorSize || + (image.height * scale).toInt() < kMinCursorSize)) { + final sw = kMinCursorSize / image.width; + final sh = kMinCursorSize / image.height; + scale = sw < sh ? sh : sw; + } x = (m.x * c.scale + cx) / scale - hotx; y = (m.y * c.scale + cy) / scale - hoty; }