diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bdd394..f50372d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,3 +39,15 @@ ## 0.6.2 - Update `dart_rfb` to version 0.7.0 (refactored read loop) + +## 0.7.0 + +- Use `dart_rfb` 0.9.0 with ZRLE support and widget-side logging +- Fix `Unsupported operation: Infinity or NaN toInt` error in gesture detector +- Add defensive checks for Infinity/NaN values in coordinate calculations +- Fix mouse pointer coordinate mapping to correctly handle different aspect ratios +- Fix coordinate calculation to account for BoxFit.contain scaling and centering offsets +- Fix widget size tracking to update correctly when window is resized +- Use ValueListenableBuilder to react to size changes in real-time +- Improve SizeTrackingWidget to use LayoutBuilder for accurate size updates +- Thanks @Spokplacenta ! diff --git a/example/.gitignore b/example/.gitignore index 24476c5..6c31954 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -5,9 +5,11 @@ *.swp .DS_Store .atom/ +.build/ .buildlog/ .history .svn/ +.swiftpm/ migrate_working_dir/ # IntelliJ related diff --git a/example/macos/Runner.xcodeproj/project.pbxproj b/example/macos/Runner.xcodeproj/project.pbxproj index b302054..2f15bb9 100644 --- a/example/macos/Runner.xcodeproj/project.pbxproj +++ b/example/macos/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 54; objects = { /* Begin PBXAggregateTarget section */ @@ -182,7 +182,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { @@ -235,6 +235,7 @@ /* Begin PBXShellScriptBuildPhase section */ 3399D490228B24CF009A79C7 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -344,7 +345,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; @@ -423,7 +424,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -470,7 +471,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; diff --git a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index b84f54e..57ceab4 100644 --- a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ diff --git a/example/macos/Runner/AppDelegate.swift b/example/macos/Runner/AppDelegate.swift index d53ef64..b3c1761 100644 --- a/example/macos/Runner/AppDelegate.swift +++ b/example/macos/Runner/AppDelegate.swift @@ -1,9 +1,13 @@ import Cocoa import FlutterMacOS -@NSApplicationMain +@main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } diff --git a/example/pubspec.lock b/example/pubspec.lock index ddcf30f..382702c 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,72 +5,81 @@ packages: dependency: transitive description: name: async - url: "https://pub.dartlang.org" + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" source: hosted - version: "2.9.0" + version: "2.13.0" binary: dependency: transitive description: name: binary - url: "https://pub.dartlang.org" + sha256: "9e095f2f1c94f06501352c8621f12d055da1c945dffae933ed0ae7fa5eeba046" + url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "4.0.0" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "5bbf32bc9e518d41ec49718e2931cd4527292c9b0c6d2dffcf7fe6b9a8a8cf72" + url: "https://pub.dev" source: hosted version: "2.1.0" characters: dependency: transitive description: name: characters - url: "https://pub.dartlang.org" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.4.0" clock: dependency: transitive description: name: clock - url: "https://pub.dartlang.org" + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.19.1" cupertino_icons: dependency: "direct main" description: name: cupertino_icons - url: "https://pub.dartlang.org" + sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be + url: "https://pub.dev" source: hosted version: "1.0.5" dart_des: dependency: transitive description: name: dart_des - url: "https://pub.dartlang.org" + sha256: "0a66afb8883368c824497fd2a1fd67bdb1a785965a3956728382c03d40747c33" + url: "https://pub.dev" source: hosted version: "1.0.2" dart_rfb: dependency: transitive description: - name: dart_rfb - url: "https://pub.dartlang.org" - source: hosted - version: "0.7.0" + path: "../../dart-rfb" + relative: true + source: path + version: "0.9.0" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.dartlang.org" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.3" flutter: dependency: "direct main" description: flutter @@ -80,7 +89,8 @@ packages: dependency: "direct dev" description: name: flutter_lints - url: "https://pub.dartlang.org" + sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + url: "https://pub.dev" source: hosted version: "2.0.1" flutter_rfb: @@ -89,7 +99,7 @@ packages: path: ".." relative: true source: path - version: "0.6.1" + version: "0.7.2" flutter_test: dependency: "direct dev" description: flutter @@ -99,133 +109,175 @@ packages: dependency: transitive description: name: fpdart - url: "https://pub.dartlang.org" + sha256: "19db038cf3bb49bfe28b2a0b363ce84cf2e6a0f471a93ae09271cfef03dae935" + url: "https://pub.dev" source: hosted version: "0.4.0" freezed_annotation: dependency: transitive description: name: freezed_annotation - url: "https://pub.dartlang.org" + sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 + url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.4.4" json_annotation: dependency: transitive description: name: json_annotation - url: "https://pub.dartlang.org" + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" + url: "https://pub.dev" + source: hosted + version: "11.0.2" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + url: "https://pub.dev" source: hosted - version: "4.7.0" + version: "3.0.10" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + url: "https://pub.dev" + source: hosted + version: "3.0.2" lints: dependency: transitive description: name: lints - url: "https://pub.dartlang.org" + sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + url: "https://pub.dev" source: hosted version: "2.0.1" logging: dependency: "direct main" description: name: logging - url: "https://pub.dartlang.org" + sha256: c0bbfe94d46aedf9b8b3e695cf3bd48c8e14b35e3b2c639e0aa7755d589ba946 + url: "https://pub.dev" source: hosted version: "1.1.0" - matan: - dependency: transitive - description: - name: matan - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.0" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" source: hosted - version: "0.12.12" + version: "0.12.17" material_color_utilities: dependency: transitive description: name: material_color_utilities - url: "https://pub.dartlang.org" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" source: hosted - version: "0.1.5" + version: "0.11.1" meta: dependency: transitive description: name: meta - url: "https://pub.dartlang.org" + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.17.0" path: dependency: transitive description: name: path - url: "https://pub.dartlang.org" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.9.1" sky_engine: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_span: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: e3320978e3715725e62f04358fd249c1efe5999297b2c6acd626a817593281b0 + url: "https://pub.dev" source: hosted version: "1.9.0" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.4" stream_transform: dependency: transitive description: name: stream_transform - url: "https://pub.dartlang.org" + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" source: hosted version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: "862015c5db1f3f3c4ea3b94dc2490363a84262994b88902315ed74be1155612f" + url: "https://pub.dev" source: hosted version: "1.1.1" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" source: hosted version: "1.2.1" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 + url: "https://pub.dev" source: hosted - version: "0.4.12" + version: "0.7.7" vector_math: dependency: transitive description: name: vector_math - url: "https://pub.dartlang.org" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + url: "https://pub.dev" + source: hosted + version: "2.2.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" + url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "15.0.2" sdks: - dart: ">=2.18.6 <3.0.0" - flutter: ">=1.17.0" + dart: ">=3.8.0-0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/lib/src/child_size_notifier_widget.dart b/lib/src/child_size_notifier_widget.dart index ea93a07..1e44b15 100644 --- a/lib/src/child_size_notifier_widget.dart +++ b/lib/src/child_size_notifier_widget.dart @@ -2,6 +2,7 @@ import 'package:flutter/widgets.dart'; /// Widget that exposes its child's size via a [ValueNotifier]. /// +/// The size is updated whenever the widget is resized. /// Inspired by: https://stackoverflow.com/a/58004112/373138 class SizeTrackingWidget extends StatefulWidget { final Widget _child; @@ -19,15 +20,40 @@ class SizeTrackingWidget extends StatefulWidget { } class _SizeTackingState extends State { + Size? _lastSize; + + void _updateSize() { + final RenderBox? renderBox = context.findRenderObject() as RenderBox?; + if (renderBox != null) { + final Size currentSize = renderBox.size; + if (_lastSize != currentSize) { + _lastSize = currentSize; + widget._sizeValueNotifier.value = currentSize; + } + } + } + @override void initState() { super.initState(); - WidgetsBinding.instance.addPostFrameCallback((final _) { - widget._sizeValueNotifier.value = - (context.findRenderObject() as RenderBox?)!.size; - }); + WidgetsBinding.instance.addPostFrameCallback((final _) => _updateSize()); } @override - Widget build(final BuildContext context) => widget._child; + void didUpdateWidget(final SizeTrackingWidget oldWidget) { + super.didUpdateWidget(oldWidget); + WidgetsBinding.instance.addPostFrameCallback((final _) => _updateSize()); + } + + @override + Widget build(final BuildContext context) { + // Use LayoutBuilder to detect size changes + return LayoutBuilder( + builder: (final BuildContext context, final BoxConstraints constraints) { + // Schedule size update after layout + WidgetsBinding.instance.addPostFrameCallback((final _) => _updateSize()); + return widget._child; + }, + ); + } } diff --git a/lib/src/extensions/coordinate_conversion_extensions.dart b/lib/src/extensions/coordinate_conversion_extensions.dart new file mode 100644 index 0000000..50557ea --- /dev/null +++ b/lib/src/extensions/coordinate_conversion_extensions.dart @@ -0,0 +1,92 @@ +import 'dart:ui'; + +import 'package:flutter/widgets.dart' hide Image; + +/// Extension to convert local widget coordinates to remote framebuffer coordinates. +/// +/// With BoxFit.contain, the image is scaled to fit while preserving aspect ratio. +/// We need to calculate the actual scaling and offsets (centering) to map +/// widget coordinates to image coordinates correctly. +extension CoordinateConversion on double { + /// Convert local X coordinate to remote framebuffer X coordinate. + int toRemoteX({ + required final Size widgetSize, + required final int imageWidth, + required final int imageHeight, + }) { + // Check if sizes are valid + if (widgetSize.width <= 0 || + widgetSize.height <= 0 || + imageWidth <= 0 || + imageHeight <= 0) { + return 0; + } + // Additional defensive checks for Infinity/NaN + if (!isFinite || + !widgetSize.width.isFinite || + !imageWidth.isFinite || + widgetSize.width == 0 || + imageWidth == 0) { + return 0; + } + // Calculate scaling factors for both dimensions + final double scaleX = widgetSize.width / imageWidth; + final double scaleY = widgetSize.height / imageHeight; + // With BoxFit.contain, the actual scale is the minimum to preserve aspect ratio + final double actualScale = scaleX < scaleY ? scaleX : scaleY; + // Calculate the displayed image size + final double displayedWidth = imageWidth * actualScale; + // Calculate offset (centering) - area where the image doesn't fill the widget horizontally + final double offsetX = (widgetSize.width - displayedWidth) / 2; + // Adjust local coordinates by subtracting the offset + final double adjustedX = this - offsetX; + // Convert to image coordinates using the actual scale + final double result = adjustedX / actualScale; + if (!result.isFinite) { + return 0; + } + // Clamp to valid image bounds + return result.clamp(0, imageWidth - 1).toInt(); + } + + /// Convert local Y coordinate to remote framebuffer Y coordinate. + int toRemoteY({ + required final Size widgetSize, + required final int imageWidth, + required final int imageHeight, + }) { + // Check if sizes are valid + if (widgetSize.width <= 0 || + widgetSize.height <= 0 || + imageWidth <= 0 || + imageHeight <= 0) { + return 0; + } + // Additional defensive checks for Infinity/NaN + if (!isFinite || + !widgetSize.height.isFinite || + !imageHeight.isFinite || + widgetSize.height == 0 || + imageHeight == 0) { + return 0; + } + // Calculate scaling factors for both dimensions + final double scaleX = widgetSize.width / imageWidth; + final double scaleY = widgetSize.height / imageHeight; + // With BoxFit.contain, the actual scale is the minimum to preserve aspect ratio + final double actualScale = scaleX < scaleY ? scaleX : scaleY; + // Calculate the displayed image size + final double displayedHeight = imageHeight * actualScale; + // Calculate offset (centering) - area where the image doesn't fill the widget vertically + final double offsetY = (widgetSize.height - displayedHeight) / 2; + // Adjust local coordinates by subtracting the offset + final double adjustedY = this - offsetY; + // Convert to image coordinates using the actual scale + final double result = adjustedY / actualScale; + if (!result.isFinite) { + return 0; + } + // Clamp to valid image bounds + return result.clamp(0, imageHeight - 1).toInt(); + } +} diff --git a/lib/src/remote_frame_buffer_gesture_detector.dart b/lib/src/remote_frame_buffer_gesture_detector.dart index c924ab7..9c73d43 100644 --- a/lib/src/remote_frame_buffer_gesture_detector.dart +++ b/lib/src/remote_frame_buffer_gesture_detector.dart @@ -2,6 +2,7 @@ import 'dart:isolate'; import 'dart:ui'; import 'package:flutter/widgets.dart' hide Image; +import 'package:flutter_rfb/src/extensions/coordinate_conversion_extensions.dart'; import 'package:flutter_rfb/src/remote_frame_buffer_isolate_messages.dart'; import 'package:fpdart/fpdart.dart'; @@ -20,9 +21,20 @@ class RemoteFrameBufferGestureDetector extends GestureDetector { _remoteFrameBufferWidgetSize = remoteFrameBufferWidgetSize, _sendPort = sendPort; + /// Check if the widget and image sizes are valid for coordinate calculations. + bool get _hasValidSize => + _remoteFrameBufferWidgetSize.width > 0 && + _remoteFrameBufferWidgetSize.height > 0 && + _image.width > 0 && + _image.height > 0; + @override GestureTapDownCallback? get onSecondaryTapDown => - (final TapDownDetails details) => _sendPort.match( + (final TapDownDetails details) { + if (!_hasValidSize) { + return; + } + _sendPort.match( () {}, (final SendPort sendPort) => sendPort.send( RemoteFrameBufferIsolateSendMessage.pointerEvent( @@ -34,21 +46,27 @@ class RemoteFrameBufferGestureDetector extends GestureDetector { button6Down: false, button7Down: false, button8Down: false, - x: (details.localPosition.dx / - _remoteFrameBufferWidgetSize.width * - _image.width) - .toInt(), - y: (details.localPosition.dy / - _remoteFrameBufferWidgetSize.height * - _image.height) - .toInt(), + x: details.localPosition.dx.toRemoteX( + widgetSize: _remoteFrameBufferWidgetSize, + imageWidth: _image.width, + imageHeight: _image.height, + ), + y: details.localPosition.dy.toRemoteY( + widgetSize: _remoteFrameBufferWidgetSize, + imageWidth: _image.width, + imageHeight: _image.height, + ), ), ), ); + }; @override - GestureTapUpCallback? get onSecondaryTapUp => - (final TapUpDetails details) => _sendPort.match( + GestureTapUpCallback? get onSecondaryTapUp => (final TapUpDetails details) { + if (!_hasValidSize) { + return; + } + _sendPort.match( () {}, (final SendPort sendPort) => sendPort.send( RemoteFrameBufferIsolateSendMessage.pointerEvent( @@ -60,21 +78,27 @@ class RemoteFrameBufferGestureDetector extends GestureDetector { button6Down: false, button7Down: false, button8Down: false, - x: (details.localPosition.dx / - _remoteFrameBufferWidgetSize.width * - _image.width) - .toInt(), - y: (details.localPosition.dy / - _remoteFrameBufferWidgetSize.height * - _image.height) - .toInt(), + x: details.localPosition.dx.toRemoteX( + widgetSize: _remoteFrameBufferWidgetSize, + imageWidth: _image.width, + imageHeight: _image.height, + ), + y: details.localPosition.dy.toRemoteY( + widgetSize: _remoteFrameBufferWidgetSize, + imageWidth: _image.width, + imageHeight: _image.height, + ), ), ), ); + }; @override - GestureTapDownCallback? get onTapDown => - (final TapDownDetails details) => _sendPort.match( + GestureTapDownCallback? get onTapDown => (final TapDownDetails details) { + if (!_hasValidSize) { + return; + } + _sendPort.match( () {}, (final SendPort sendPort) => sendPort.send( RemoteFrameBufferIsolateSendMessage.pointerEvent( @@ -86,21 +110,27 @@ class RemoteFrameBufferGestureDetector extends GestureDetector { button6Down: false, button7Down: false, button8Down: false, - x: (details.localPosition.dx / - _remoteFrameBufferWidgetSize.width * - _image.width) - .toInt(), - y: (details.localPosition.dy / - _remoteFrameBufferWidgetSize.height * - _image.height) - .toInt(), + x: details.localPosition.dx.toRemoteX( + widgetSize: _remoteFrameBufferWidgetSize, + imageWidth: _image.width, + imageHeight: _image.height, + ), + y: details.localPosition.dy.toRemoteY( + widgetSize: _remoteFrameBufferWidgetSize, + imageWidth: _image.width, + imageHeight: _image.height, + ), ), ), ); + }; @override - GestureTapUpCallback? get onTapUp => - (final TapUpDetails details) => _sendPort.match( + GestureTapUpCallback? get onTapUp => (final TapUpDetails details) { + if (!_hasValidSize) { + return; + } + _sendPort.match( () {}, (final SendPort sendPort) => sendPort.send( RemoteFrameBufferIsolateSendMessage.pointerEvent( @@ -112,15 +142,18 @@ class RemoteFrameBufferGestureDetector extends GestureDetector { button6Down: false, button7Down: false, button8Down: false, - x: (details.localPosition.dx / - _remoteFrameBufferWidgetSize.width * - _image.width) - .toInt(), - y: (details.localPosition.dy / - _remoteFrameBufferWidgetSize.height * - _image.height) - .toInt(), + x: details.localPosition.dx.toRemoteX( + widgetSize: _remoteFrameBufferWidgetSize, + imageWidth: _image.width, + imageHeight: _image.height, + ), + y: details.localPosition.dy.toRemoteY( + widgetSize: _remoteFrameBufferWidgetSize, + imageWidth: _image.width, + imageHeight: _image.height, + ), ), ), ); + }; } diff --git a/lib/src/remote_frame_buffer_widget.dart b/lib/src/remote_frame_buffer_widget.dart index be3507e..14d4a32 100644 --- a/lib/src/remote_frame_buffer_widget.dart +++ b/lib/src/remote_frame_buffer_widget.dart @@ -112,11 +112,18 @@ class RemoteFrameBufferWidgetState extends State { SizeTrackingWidget _buildImage({required final Image image}) => SizeTrackingWidget( sizeValueNotifier: _sizeValueNotifier, - child: RemoteFrameBufferGestureDetector( - image: image, - remoteFrameBufferWidgetSize: _sizeValueNotifier.value, - sendPort: _isolateSendPort, - child: RawImage(image: image), + child: ValueListenableBuilder( + valueListenable: _sizeValueNotifier, + builder: (final BuildContext context, final Size size, final Widget? child) => + RemoteFrameBufferGestureDetector( + image: image, + remoteFrameBufferWidgetSize: size, + sendPort: _isolateSendPort, + child: RawImage( + image: image, + fit: BoxFit.contain, // Preserve aspect ratio, coordinates are calculated correctly + ), + ), ), ); @@ -226,6 +233,9 @@ class RemoteFrameBufferWidgetState extends State { print('Error updating frame buffer: $error'), (final _) {}, ), + zrle: () async => _logger.warning( + 'ZRLE rectangle received but not decoded upstream, dart_rfb should have decoded this and provided it here as a raw rectangle.', + ), unsupported: (final ByteData bytes) async {}, ); } diff --git a/pubspec.lock b/pubspec.lock index 850d65e..3d0da3f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,189 +5,215 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - url: "https://pub.dartlang.org" + sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a + url: "https://pub.dev" source: hosted - version: "50.0.0" + version: "61.0.0" analyzer: dependency: transitive description: name: analyzer - url: "https://pub.dartlang.org" + sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 + url: "https://pub.dev" source: hosted - version: "5.2.0" + version: "5.13.0" args: dependency: transitive description: name: args - url: "https://pub.dartlang.org" + sha256: b003c3098049a51720352d219b0bb5f219b60fbfb68e7a4748139a06a5676515 + url: "https://pub.dev" source: hosted version: "2.3.1" async: dependency: transitive description: name: async - url: "https://pub.dartlang.org" + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" source: hosted - version: "2.9.0" + version: "2.13.0" binary: dependency: transitive description: name: binary - url: "https://pub.dartlang.org" + sha256: "9e095f2f1c94f06501352c8621f12d055da1c945dffae933ed0ae7fa5eeba046" + url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "4.0.0" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "5bbf32bc9e518d41ec49718e2931cd4527292c9b0c6d2dffcf7fe6b9a8a8cf72" + url: "https://pub.dev" source: hosted version: "2.1.0" build: dependency: transitive description: name: build - url: "https://pub.dartlang.org" + sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777" + url: "https://pub.dev" source: hosted version: "2.3.1" build_config: dependency: transitive description: name: build_config - url: "https://pub.dartlang.org" + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + url: "https://pub.dev" source: hosted version: "1.1.1" build_daemon: dependency: transitive description: name: build_daemon - url: "https://pub.dartlang.org" + sha256: "6bc5544ea6ce4428266e7ea680e945c68806c4aae2da0eb5e9ccf38df8d6acbf" + url: "https://pub.dev" source: hosted version: "3.1.0" build_resolvers: dependency: transitive description: name: build_resolvers - url: "https://pub.dartlang.org" + sha256: "7c35a3a7868626257d8aee47b51c26b9dba11eaddf3431117ed2744951416aab" + url: "https://pub.dev" source: hosted version: "2.1.0" build_runner: dependency: "direct dev" description: name: build_runner - url: "https://pub.dartlang.org" + sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727 + url: "https://pub.dev" source: hosted version: "2.3.3" build_runner_core: dependency: transitive description: name: build_runner_core - url: "https://pub.dartlang.org" + sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292" + url: "https://pub.dev" source: hosted version: "7.2.7" built_collection: dependency: transitive description: name: built_collection - url: "https://pub.dartlang.org" + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" source: hosted version: "5.1.1" built_value: dependency: transitive description: name: built_value - url: "https://pub.dartlang.org" + sha256: "59e08b0079bb75f7e27392498e26339387c1089c6bd58525a14eb8508637277b" + url: "https://pub.dev" source: hosted version: "8.4.2" characters: dependency: transitive description: name: characters - url: "https://pub.dartlang.org" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.4.0" checked_yaml: dependency: transitive description: name: checked_yaml - url: "https://pub.dartlang.org" + sha256: dd007e4fb8270916820a0d66e24f619266b60773cddd082c6439341645af2659 + url: "https://pub.dev" source: hosted version: "2.0.1" clock: dependency: transitive description: name: clock - url: "https://pub.dartlang.org" + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" code_builder: dependency: transitive description: name: code_builder - url: "https://pub.dartlang.org" + sha256: "02ce3596b459c666530f045ad6f96209474e8fee6e4855940a3cee65fb872ec5" + url: "https://pub.dev" source: hosted version: "4.3.0" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.19.1" convert: dependency: transitive description: name: convert - url: "https://pub.dartlang.org" + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" source: hosted version: "3.1.1" crypto: dependency: transitive description: name: crypto - url: "https://pub.dartlang.org" + sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + url: "https://pub.dev" source: hosted version: "3.0.2" dart_des: dependency: transitive description: name: dart_des - url: "https://pub.dartlang.org" + sha256: "0a66afb8883368c824497fd2a1fd67bdb1a785965a3956728382c03d40747c33" + url: "https://pub.dev" source: hosted version: "1.0.2" dart_rfb: dependency: "direct main" description: - name: dart_rfb - url: "https://pub.dartlang.org" - source: hosted - version: "0.7.0" + path: "../dart-rfb" + relative: true + source: path + version: "0.9.0" dart_style: dependency: transitive description: name: dart_style - url: "https://pub.dartlang.org" + sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4" + url: "https://pub.dev" source: hosted version: "2.2.4" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.dartlang.org" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.3" file: dependency: transitive description: name: file - url: "https://pub.dartlang.org" + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" source: hosted version: "6.1.4" fixnum: dependency: transitive description: name: fixnum - url: "https://pub.dartlang.org" + sha256: "04be3e934c52e082558cc9ee21f42f5c1cd7a1262f4c63cd0357c08d5bba81ec" + url: "https://pub.dev" source: hosted version: "1.0.1" flutter: @@ -199,7 +225,8 @@ packages: dependency: "direct dev" description: name: flutter_lints - url: "https://pub.dartlang.org" + sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + url: "https://pub.dev" source: hosted version: "2.0.1" flutter_test: @@ -211,280 +238,343 @@ packages: dependency: "direct main" description: name: fpdart - url: "https://pub.dartlang.org" + sha256: "19db038cf3bb49bfe28b2a0b363ce84cf2e6a0f471a93ae09271cfef03dae935" + url: "https://pub.dev" source: hosted version: "0.4.0" freezed: dependency: "direct dev" description: name: freezed - url: "https://pub.dartlang.org" + sha256: a434911f643466d78462625df76fd9eb13e57348ff43fe1f77bbe909522c67a1 + url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.5.2" freezed_annotation: dependency: "direct main" description: name: freezed_annotation - url: "https://pub.dartlang.org" + sha256: f54946fdb1fa7b01f780841937b1a80783a20b393485f3f6cdf336fd6f4705f2 + url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.4.2" frontend_server_client: dependency: transitive description: name: frontend_server_client - url: "https://pub.dartlang.org" + sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + url: "https://pub.dev" source: hosted version: "3.2.0" glob: dependency: transitive description: name: glob - url: "https://pub.dartlang.org" + sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c" + url: "https://pub.dev" source: hosted version: "2.1.1" graphs: dependency: transitive description: name: graphs - url: "https://pub.dartlang.org" + sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2 + url: "https://pub.dev" source: hosted version: "2.2.0" http_multi_server: dependency: transitive description: name: http_multi_server - url: "https://pub.dartlang.org" + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" source: hosted version: "3.2.1" http_parser: dependency: transitive description: name: http_parser - url: "https://pub.dartlang.org" + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" source: hosted version: "4.0.2" io: dependency: transitive description: name: io - url: "https://pub.dartlang.org" + sha256: "0d4c73c3653ab85bf696d51a9657604c900a370549196a91f33e4c39af760852" + url: "https://pub.dev" source: hosted version: "1.0.3" js: dependency: transitive description: name: js - url: "https://pub.dartlang.org" + sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + url: "https://pub.dev" source: hosted version: "0.6.5" json_annotation: dependency: transitive description: name: json_annotation - url: "https://pub.dartlang.org" + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" + url: "https://pub.dev" + source: hosted + version: "11.0.2" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + url: "https://pub.dev" + source: hosted + version: "3.0.10" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + url: "https://pub.dev" source: hosted - version: "4.7.0" + version: "3.0.2" lints: dependency: transitive description: name: lints - url: "https://pub.dartlang.org" + sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + url: "https://pub.dev" source: hosted version: "2.0.1" logging: dependency: "direct main" description: name: logging - url: "https://pub.dartlang.org" + sha256: c0bbfe94d46aedf9b8b3e695cf3bd48c8e14b35e3b2c639e0aa7755d589ba946 + url: "https://pub.dev" source: hosted version: "1.1.0" - matan: - dependency: transitive - description: - name: matan - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.0" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" source: hosted - version: "0.12.12" + version: "0.12.17" material_color_utilities: dependency: transitive description: name: material_color_utilities - url: "https://pub.dartlang.org" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" source: hosted - version: "0.1.5" + version: "0.11.1" meta: dependency: transitive description: name: meta - url: "https://pub.dartlang.org" + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.17.0" mime: dependency: transitive description: name: mime - url: "https://pub.dartlang.org" + sha256: "52e38f7e1143ef39daf532117d6b8f8f617bf4bcd6044ed8c29040d20d269630" + url: "https://pub.dev" source: hosted version: "1.0.3" package_config: dependency: transitive description: name: package_config - url: "https://pub.dartlang.org" + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" source: hosted version: "2.1.0" path: dependency: transitive description: name: path - url: "https://pub.dartlang.org" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.9.1" pool: dependency: transitive description: name: pool - url: "https://pub.dartlang.org" + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" source: hosted version: "1.5.1" pub_semver: dependency: transitive description: name: pub_semver - url: "https://pub.dartlang.org" + sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17" + url: "https://pub.dev" source: hosted version: "2.1.3" pubspec_parse: dependency: transitive description: name: pubspec_parse - url: "https://pub.dartlang.org" + sha256: "75f6614d6dde2dc68948dffbaa4fe5dae32cd700eb9fb763fe11dfb45a3c4d0a" + url: "https://pub.dev" source: hosted version: "1.2.1" shelf: dependency: transitive description: name: shelf - url: "https://pub.dartlang.org" + sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c + url: "https://pub.dev" source: hosted version: "1.4.0" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - url: "https://pub.dartlang.org" + sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8 + url: "https://pub.dev" source: hosted version: "1.0.3" sky_engine: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_gen: dependency: transitive description: name: source_gen - url: "https://pub.dartlang.org" + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" + url: "https://pub.dev" source: hosted - version: "1.2.6" + version: "1.5.0" source_span: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: e3320978e3715725e62f04358fd249c1efe5999297b2c6acd626a817593281b0 + url: "https://pub.dev" source: hosted version: "1.9.0" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.4" stream_transform: dependency: "direct main" description: name: stream_transform - url: "https://pub.dartlang.org" + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" source: hosted version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: "862015c5db1f3f3c4ea3b94dc2490363a84262994b88902315ed74be1155612f" + url: "https://pub.dev" source: hosted version: "1.1.1" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" source: hosted version: "1.2.1" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 + url: "https://pub.dev" source: hosted - version: "0.4.12" + version: "0.7.7" timing: dependency: transitive description: name: timing - url: "https://pub.dartlang.org" + sha256: c386d07d7f5efc613479a7c4d9d64b03710b03cfaa7e8ad5f2bfb295a1f0dfad + url: "https://pub.dev" source: hosted version: "1.0.0" typed_data: dependency: transitive description: name: typed_data - url: "https://pub.dartlang.org" + sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + url: "https://pub.dev" source: hosted version: "1.3.1" vector_math: dependency: transitive description: name: vector_math - url: "https://pub.dartlang.org" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + url: "https://pub.dev" + source: hosted + version: "2.2.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" + url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "15.0.2" watcher: dependency: transitive description: name: watcher - url: "https://pub.dartlang.org" + sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0" + url: "https://pub.dev" source: hosted version: "1.0.2" web_socket_channel: dependency: transitive description: name: web_socket_channel - url: "https://pub.dartlang.org" + sha256: "3a969ddcc204a3e34e863d204b29c0752716f78b6f9cc8235083208d268a4ccd" + url: "https://pub.dev" source: hosted version: "2.2.0" yaml: dependency: transitive description: name: yaml - url: "https://pub.dartlang.org" + sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370" + url: "https://pub.dev" source: hosted version: "3.1.1" sdks: - dart: ">=2.18.6 <3.0.0" - flutter: ">=1.17.0" + dart: ">=3.8.0-0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.yaml b/pubspec.yaml index 0638006..f259103 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,15 +2,14 @@ description: A VNC / Remote Framebuffer / RFC 6143 client purely written in Dart homepage: https://github.com/Goddchen/flutter-vnc name: flutter_rfb repository: https://github.com/Goddchen/flutter-vnc -version: 0.6.2 +version: 0.7.0 environment: sdk: ">=2.18.6 <3.0.0" flutter: ">=1.17.0" dependencies: - dart_rfb: ^0.7.0 - # dart_rfb: + dart_rfb: ^0.9.0 # git: # path: dart-rfb/ # ref: main @@ -19,7 +18,7 @@ dependencies: flutter: sdk: flutter fpdart: ^0.4.0 - freezed_annotation: ^2.2.0 + freezed_annotation: ^2.4.1 logging: ^1.1.0 stream_transform: ^2.1.0 @@ -28,6 +27,6 @@ dev_dependencies: flutter_lints: ^2.0.0 flutter_test: sdk: flutter - freezed: ^2.3.2 + freezed: ^2.5.2 flutter: