From f1b037fe5999681158c1e7971a9bca8ff3ad0aed Mon Sep 17 00:00:00 2001 From: Anthony Marchand Date: Thu, 27 Nov 2025 11:17:29 +0100 Subject: [PATCH 01/14] Adapt Flutter widget for ZRLE --- CHANGELOG.md | 4 + example/pubspec.lock | 168 +++++++++----- lib/src/remote_frame_buffer_widget.dart | 3 + pubspec.lock | 290 ++++++++++++++++-------- pubspec.yaml | 16 +- 5 files changed, 313 insertions(+), 168 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bdd394..3a05236 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,3 +39,7 @@ ## 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 diff --git a/example/pubspec.lock b/example/pubspec.lock index ddcf30f..0ba786b 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.0" 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: "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" 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/remote_frame_buffer_widget.dart b/lib/src/remote_frame_buffer_widget.dart index be3507e..37c694a 100644 --- a/lib/src/remote_frame_buffer_widget.dart +++ b/lib/src/remote_frame_buffer_widget.dart @@ -226,6 +226,9 @@ class RemoteFrameBufferWidgetState extends State { print('Error updating frame buffer: $error'), (final _) {}, ), + zrle: () async => _logger.warning( + 'ZRLE rectangle received but not decoded upstream', + ), unsupported: (final ByteData bytes) async {}, ); } diff --git a/pubspec.lock b/pubspec.lock index 850d65e..2a3ef5e 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 + dependency: "direct main" 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" 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..a3565e8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,24 +2,20 @@ 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: - # git: - # path: dart-rfb/ - # ref: main - # url: https://github.com/Goddchen/dart-rfb - # path: ../dart-rfb + dart_rfb: + path: ../dart-rfb flutter: sdk: flutter fpdart: ^0.4.0 - freezed_annotation: ^2.2.0 + freezed_annotation: ^2.4.1 + json_annotation: ^4.8.1 logging: ^1.1.0 stream_transform: ^2.1.0 @@ -28,6 +24,6 @@ dev_dependencies: flutter_lints: ^2.0.0 flutter_test: sdk: flutter - freezed: ^2.3.2 + freezed: ^2.5.2 flutter: From dfff479eef63f3f5e0cb9635310a9f4bcecb429b Mon Sep 17 00:00:00 2001 From: Anthony Marchand Date: Fri, 28 Nov 2025 20:57:36 +0100 Subject: [PATCH 02/14] fix: prevent Infinity/NaN errors in gesture detector - Add defensive checks for Infinity/NaN values in coordinate calculations - Add _hasValidSize check to prevent errors when widget or image size is invalid - Extract coordinate conversion to helper methods with proper validation --- CHANGELOG.md | 5 + .../remote_frame_buffer_gesture_detector.dart | 222 +++++++++++------- pubspec.yaml | 2 +- 3 files changed, 137 insertions(+), 92 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a05236..0fa3ed6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,3 +43,8 @@ ## 0.7.0 - Use `dart_rfb` 0.9.0 with ZRLE support and widget-side logging + +## 0.7.1 + +- Fix `Unsupported operation: Infinity or NaN toInt` error in gesture detector +- Add defensive checks for Infinity/NaN values in coordinate calculations diff --git a/lib/src/remote_frame_buffer_gesture_detector.dart b/lib/src/remote_frame_buffer_gesture_detector.dart index c924ab7..7adaaf8 100644 --- a/lib/src/remote_frame_buffer_gesture_detector.dart +++ b/lib/src/remote_frame_buffer_gesture_detector.dart @@ -20,107 +20,147 @@ 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; + + /// Convert local position to remote framebuffer coordinates. + int _toRemoteX(final double localX) { + if (!_hasValidSize) { + return 0; + } + // Additional defensive checks for Infinity/NaN + if (!localX.isFinite || + !_remoteFrameBufferWidgetSize.width.isFinite || + !_image.width.isFinite || + _remoteFrameBufferWidgetSize.width == 0 || + _image.width == 0) { + return 0; + } + final double result = localX / _remoteFrameBufferWidgetSize.width * _image.width; + if (!result.isFinite) { + return 0; + } + return result.toInt(); + } + + int _toRemoteY(final double localY) { + if (!_hasValidSize) { + return 0; + } + // Additional defensive checks for Infinity/NaN + if (!localY.isFinite || + !_remoteFrameBufferWidgetSize.height.isFinite || + !_image.height.isFinite || + _remoteFrameBufferWidgetSize.height == 0 || + _image.height == 0) { + return 0; + } + final double result = localY / _remoteFrameBufferWidgetSize.height * _image.height; + if (!result.isFinite) { + return 0; + } + return result.toInt(); + } + @override GestureTapDownCallback? get onSecondaryTapDown => - (final TapDownDetails details) => _sendPort.match( - () {}, - (final SendPort sendPort) => sendPort.send( - RemoteFrameBufferIsolateSendMessage.pointerEvent( - button1Down: false, - button2Down: false, - button3Down: true, - button4Down: false, - button5Down: false, - button6Down: false, - button7Down: false, - button8Down: false, - x: (details.localPosition.dx / - _remoteFrameBufferWidgetSize.width * - _image.width) - .toInt(), - y: (details.localPosition.dy / - _remoteFrameBufferWidgetSize.height * - _image.height) - .toInt(), - ), + (final TapDownDetails details) { + if (!_hasValidSize) { + return; + } + _sendPort.match( + () {}, + (final SendPort sendPort) => sendPort.send( + RemoteFrameBufferIsolateSendMessage.pointerEvent( + button1Down: false, + button2Down: false, + button3Down: true, + button4Down: false, + button5Down: false, + button6Down: false, + button7Down: false, + button8Down: false, + x: _toRemoteX(details.localPosition.dx), + y: _toRemoteY(details.localPosition.dy), ), - ); + ), + ); + }; @override - GestureTapUpCallback? get onSecondaryTapUp => - (final TapUpDetails details) => _sendPort.match( - () {}, - (final SendPort sendPort) => sendPort.send( - RemoteFrameBufferIsolateSendMessage.pointerEvent( - button1Down: false, - button2Down: false, - button3Down: false, - button4Down: false, - button5Down: false, - button6Down: false, - button7Down: false, - button8Down: false, - x: (details.localPosition.dx / - _remoteFrameBufferWidgetSize.width * - _image.width) - .toInt(), - y: (details.localPosition.dy / - _remoteFrameBufferWidgetSize.height * - _image.height) - .toInt(), - ), + GestureTapUpCallback? get onSecondaryTapUp => (final TapUpDetails details) { + if (!_hasValidSize) { + return; + } + _sendPort.match( + () {}, + (final SendPort sendPort) => sendPort.send( + RemoteFrameBufferIsolateSendMessage.pointerEvent( + button1Down: false, + button2Down: false, + button3Down: false, + button4Down: false, + button5Down: false, + button6Down: false, + button7Down: false, + button8Down: false, + x: _toRemoteX(details.localPosition.dx), + y: _toRemoteY(details.localPosition.dy), ), - ); + ), + ); + }; @override - GestureTapDownCallback? get onTapDown => - (final TapDownDetails details) => _sendPort.match( - () {}, - (final SendPort sendPort) => sendPort.send( - RemoteFrameBufferIsolateSendMessage.pointerEvent( - button1Down: true, - button2Down: false, - button3Down: false, - button4Down: false, - button5Down: false, - button6Down: false, - button7Down: false, - button8Down: false, - x: (details.localPosition.dx / - _remoteFrameBufferWidgetSize.width * - _image.width) - .toInt(), - y: (details.localPosition.dy / - _remoteFrameBufferWidgetSize.height * - _image.height) - .toInt(), - ), + GestureTapDownCallback? get onTapDown => (final TapDownDetails details) { + if (!_hasValidSize) { + return; + } + _sendPort.match( + () {}, + (final SendPort sendPort) => sendPort.send( + RemoteFrameBufferIsolateSendMessage.pointerEvent( + button1Down: true, + button2Down: false, + button3Down: false, + button4Down: false, + button5Down: false, + button6Down: false, + button7Down: false, + button8Down: false, + x: _toRemoteX(details.localPosition.dx), + y: _toRemoteY(details.localPosition.dy), ), - ); + ), + ); + }; @override - GestureTapUpCallback? get onTapUp => - (final TapUpDetails details) => _sendPort.match( - () {}, - (final SendPort sendPort) => sendPort.send( - RemoteFrameBufferIsolateSendMessage.pointerEvent( - button1Down: false, - button2Down: false, - button3Down: false, - button4Down: false, - button5Down: false, - button6Down: false, - button7Down: false, - button8Down: false, - x: (details.localPosition.dx / - _remoteFrameBufferWidgetSize.width * - _image.width) - .toInt(), - y: (details.localPosition.dy / - _remoteFrameBufferWidgetSize.height * - _image.height) - .toInt(), - ), + GestureTapUpCallback? get onTapUp => (final TapUpDetails details) { + if (!_hasValidSize) { + return; + } + _sendPort.match( + () {}, + (final SendPort sendPort) => sendPort.send( + RemoteFrameBufferIsolateSendMessage.pointerEvent( + button1Down: false, + button2Down: false, + button3Down: false, + button4Down: false, + button5Down: false, + button6Down: false, + button7Down: false, + button8Down: false, + x: _toRemoteX(details.localPosition.dx), + y: _toRemoteY(details.localPosition.dy), ), - ); + ), + ); + }; } + diff --git a/pubspec.yaml b/pubspec.yaml index a3565e8..2fd4738 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ 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.7.0 +version: 0.7.1 environment: sdk: ">=2.18.6 <3.0.0" From e89bb7267801eec8c5d3a4625b02d9fc3b9722d1 Mon Sep 17 00:00:00 2001 From: Anthony Marchand Date: Fri, 28 Nov 2025 20:58:12 +0100 Subject: [PATCH 03/14] fix: improve mouse coordinate mapping and widget size tracking - Fix coordinate calculation to account for BoxFit.contain scaling and centering offsets - Handle different aspect ratios correctly by calculating actual scale and 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 --- CHANGELOG.md | 8 ++++ lib/src/child_size_notifier_widget.dart | 36 +++++++++++++++--- .../remote_frame_buffer_gesture_detector.dart | 38 +++++++++++++++++-- lib/src/remote_frame_buffer_widget.dart | 17 ++++++--- pubspec.yaml | 2 +- 5 files changed, 86 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fa3ed6..91cd234 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,3 +48,11 @@ - Fix `Unsupported operation: Infinity or NaN toInt` error in gesture detector - Add defensive checks for Infinity/NaN values in coordinate calculations + +## 0.7.2 + +- 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 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/remote_frame_buffer_gesture_detector.dart b/lib/src/remote_frame_buffer_gesture_detector.dart index 7adaaf8..c708624 100644 --- a/lib/src/remote_frame_buffer_gesture_detector.dart +++ b/lib/src/remote_frame_buffer_gesture_detector.dart @@ -28,6 +28,10 @@ class RemoteFrameBufferGestureDetector extends GestureDetector { _image.height > 0; /// Convert local position 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. int _toRemoteX(final double localX) { if (!_hasValidSize) { return 0; @@ -40,11 +44,24 @@ class RemoteFrameBufferGestureDetector extends GestureDetector { _image.width == 0) { return 0; } - final double result = localX / _remoteFrameBufferWidgetSize.width * _image.width; + // Calculate scaling factors for both dimensions + final double scaleX = _remoteFrameBufferWidgetSize.width / _image.width; + final double scaleY = _remoteFrameBufferWidgetSize.height / _image.height; + // 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 = _image.width * actualScale; + // Calculate offset (centering) - area where the image doesn't fill the widget horizontally + final double offsetX = (_remoteFrameBufferWidgetSize.width - displayedWidth) / 2; + // Adjust local coordinates by subtracting the offset + final double adjustedX = localX - offsetX; + // Convert to image coordinates using the actual scale + final double result = adjustedX / actualScale; if (!result.isFinite) { return 0; } - return result.toInt(); + // Clamp to valid image bounds + return result.clamp(0, _image.width - 1).toInt(); } int _toRemoteY(final double localY) { @@ -59,11 +76,24 @@ class RemoteFrameBufferGestureDetector extends GestureDetector { _image.height == 0) { return 0; } - final double result = localY / _remoteFrameBufferWidgetSize.height * _image.height; + // Calculate scaling factors for both dimensions + final double scaleX = _remoteFrameBufferWidgetSize.width / _image.width; + final double scaleY = _remoteFrameBufferWidgetSize.height / _image.height; + // 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 = _image.height * actualScale; + // Calculate offset (centering) - area where the image doesn't fill the widget vertically + final double offsetY = (_remoteFrameBufferWidgetSize.height - displayedHeight) / 2; + // Adjust local coordinates by subtracting the offset + final double adjustedY = localY - offsetY; + // Convert to image coordinates using the actual scale + final double result = adjustedY / actualScale; if (!result.isFinite) { return 0; } - return result.toInt(); + // Clamp to valid image bounds + return result.clamp(0, _image.height - 1).toInt(); } @override diff --git a/lib/src/remote_frame_buffer_widget.dart b/lib/src/remote_frame_buffer_widget.dart index 37c694a..11d8b76 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 + ), + ), ), ); diff --git a/pubspec.yaml b/pubspec.yaml index 2fd4738..03989b9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ 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.7.1 +version: 0.7.2 environment: sdk: ">=2.18.6 <3.0.0" From 544787cca7b293b46632ac0ff7eeaaa15f8cab7a Mon Sep 17 00:00:00 2001 From: Anthony Marchand Date: Mon, 1 Dec 2025 09:26:53 +0100 Subject: [PATCH 04/14] chore: update project configuration and improve AppDelegate - Update .gitignore to include new build-related directories - Change AppDelegate annotation from @NSApplicationMain to @main - Increment object version and last upgrade check in project.pbxproj - Update MACOSX_DEPLOYMENT_TARGET to 10.15 in project settings - Add GPU validation mode in Runner.xcscheme --- example/.gitignore | 2 ++ example/macos/Runner.xcodeproj/project.pbxproj | 11 ++++++----- .../xcshareddata/xcschemes/Runner.xcscheme | 3 ++- example/macos/Runner/AppDelegate.swift | 6 +++++- 4 files changed, 15 insertions(+), 7 deletions(-) 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 + } } From 2212ddbec00f6cb8dc41d8b38474bc672414a84d Mon Sep 17 00:00:00 2001 From: Anthony Marchand Date: Wed, 7 Jan 2026 18:22:30 +0100 Subject: [PATCH 05/14] refactor: convert coordinate conversion methods to extension functions - Extract _toRemoteX and _toRemoteY methods to CoordinateConversion extension on double - Improve code readability by using extension methods instead of private methods - Make coordinate conversion logic reusable across the codebase --- .../coordinate_conversion_extensions.dart | 92 ++++++++ .../remote_frame_buffer_gesture_detector.dart | 223 ++++++++---------- 2 files changed, 185 insertions(+), 130 deletions(-) create mode 100644 lib/src/extensions/coordinate_conversion_extensions.dart 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 c708624..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'; @@ -27,75 +28,6 @@ class RemoteFrameBufferGestureDetector extends GestureDetector { _image.width > 0 && _image.height > 0; - /// Convert local position 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. - int _toRemoteX(final double localX) { - if (!_hasValidSize) { - return 0; - } - // Additional defensive checks for Infinity/NaN - if (!localX.isFinite || - !_remoteFrameBufferWidgetSize.width.isFinite || - !_image.width.isFinite || - _remoteFrameBufferWidgetSize.width == 0 || - _image.width == 0) { - return 0; - } - // Calculate scaling factors for both dimensions - final double scaleX = _remoteFrameBufferWidgetSize.width / _image.width; - final double scaleY = _remoteFrameBufferWidgetSize.height / _image.height; - // 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 = _image.width * actualScale; - // Calculate offset (centering) - area where the image doesn't fill the widget horizontally - final double offsetX = (_remoteFrameBufferWidgetSize.width - displayedWidth) / 2; - // Adjust local coordinates by subtracting the offset - final double adjustedX = localX - 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, _image.width - 1).toInt(); - } - - int _toRemoteY(final double localY) { - if (!_hasValidSize) { - return 0; - } - // Additional defensive checks for Infinity/NaN - if (!localY.isFinite || - !_remoteFrameBufferWidgetSize.height.isFinite || - !_image.height.isFinite || - _remoteFrameBufferWidgetSize.height == 0 || - _image.height == 0) { - return 0; - } - // Calculate scaling factors for both dimensions - final double scaleX = _remoteFrameBufferWidgetSize.width / _image.width; - final double scaleY = _remoteFrameBufferWidgetSize.height / _image.height; - // 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 = _image.height * actualScale; - // Calculate offset (centering) - area where the image doesn't fill the widget vertically - final double offsetY = (_remoteFrameBufferWidgetSize.height - displayedHeight) / 2; - // Adjust local coordinates by subtracting the offset - final double adjustedY = localY - 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, _image.height - 1).toInt(); - } - @override GestureTapDownCallback? get onSecondaryTapDown => (final TapDownDetails details) { @@ -103,22 +35,30 @@ class RemoteFrameBufferGestureDetector extends GestureDetector { return; } _sendPort.match( - () {}, - (final SendPort sendPort) => sendPort.send( - RemoteFrameBufferIsolateSendMessage.pointerEvent( - button1Down: false, - button2Down: false, - button3Down: true, - button4Down: false, - button5Down: false, - button6Down: false, - button7Down: false, - button8Down: false, - x: _toRemoteX(details.localPosition.dx), - y: _toRemoteY(details.localPosition.dy), + () {}, + (final SendPort sendPort) => sendPort.send( + RemoteFrameBufferIsolateSendMessage.pointerEvent( + button1Down: false, + button2Down: false, + button3Down: true, + button4Down: false, + button5Down: false, + button6Down: false, + button7Down: false, + button8Down: false, + 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 @@ -127,22 +67,30 @@ class RemoteFrameBufferGestureDetector extends GestureDetector { return; } _sendPort.match( - () {}, - (final SendPort sendPort) => sendPort.send( - RemoteFrameBufferIsolateSendMessage.pointerEvent( - button1Down: false, - button2Down: false, - button3Down: false, - button4Down: false, - button5Down: false, - button6Down: false, - button7Down: false, - button8Down: false, - x: _toRemoteX(details.localPosition.dx), - y: _toRemoteY(details.localPosition.dy), + () {}, + (final SendPort sendPort) => sendPort.send( + RemoteFrameBufferIsolateSendMessage.pointerEvent( + button1Down: false, + button2Down: false, + button3Down: false, + button4Down: false, + button5Down: false, + button6Down: false, + button7Down: false, + button8Down: false, + 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 @@ -151,22 +99,30 @@ class RemoteFrameBufferGestureDetector extends GestureDetector { return; } _sendPort.match( - () {}, - (final SendPort sendPort) => sendPort.send( - RemoteFrameBufferIsolateSendMessage.pointerEvent( - button1Down: true, - button2Down: false, - button3Down: false, - button4Down: false, - button5Down: false, - button6Down: false, - button7Down: false, - button8Down: false, - x: _toRemoteX(details.localPosition.dx), - y: _toRemoteY(details.localPosition.dy), + () {}, + (final SendPort sendPort) => sendPort.send( + RemoteFrameBufferIsolateSendMessage.pointerEvent( + button1Down: true, + button2Down: false, + button3Down: false, + button4Down: false, + button5Down: false, + button6Down: false, + button7Down: false, + button8Down: false, + 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 @@ -175,22 +131,29 @@ class RemoteFrameBufferGestureDetector extends GestureDetector { return; } _sendPort.match( - () {}, - (final SendPort sendPort) => sendPort.send( - RemoteFrameBufferIsolateSendMessage.pointerEvent( - button1Down: false, - button2Down: false, - button3Down: false, - button4Down: false, - button5Down: false, - button6Down: false, - button7Down: false, - button8Down: false, - x: _toRemoteX(details.localPosition.dx), - y: _toRemoteY(details.localPosition.dy), + () {}, + (final SendPort sendPort) => sendPort.send( + RemoteFrameBufferIsolateSendMessage.pointerEvent( + button1Down: false, + button2Down: false, + button3Down: false, + button4Down: false, + button5Down: false, + button6Down: false, + button7Down: false, + button8Down: false, + 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, + ), + ), ), - ), - ); + ); }; } - From 3cc4352eb2624a418ebc5113c7f6100d6c09e6a1 Mon Sep 17 00:00:00 2001 From: Spokplacenta <71837821+Spokplacenta@users.noreply.github.com> Date: Wed, 7 Jan 2026 18:24:41 +0100 Subject: [PATCH 06/14] Update pubspec.yaml Co-authored-by: Goddchen --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 03989b9..b3d8af0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,7 +9,7 @@ environment: flutter: ">=1.17.0" dependencies: - dart_rfb: + dart_rfb: ^0.9.0 path: ../dart-rfb flutter: sdk: flutter From ec148bfab9bd58c169c7c1dab90e6c54af110175 Mon Sep 17 00:00:00 2001 From: Spokplacenta <71837821+Spokplacenta@users.noreply.github.com> Date: Wed, 7 Jan 2026 18:25:04 +0100 Subject: [PATCH 07/14] Update pubspec.yaml Co-authored-by: Goddchen --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index b3d8af0..852a9b0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ 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.7.2 +version: 0.7.0 environment: sdk: ">=2.18.6 <3.0.0" From 1ac64c24835c14e64f6d0f06d5b4ab827c2676ee Mon Sep 17 00:00:00 2001 From: Spokplacenta <71837821+Spokplacenta@users.noreply.github.com> Date: Wed, 7 Jan 2026 18:26:08 +0100 Subject: [PATCH 08/14] Update pubspec.yaml Co-authored-by: Goddchen --- pubspec.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 852a9b0..b9bf1ef 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,7 +10,11 @@ environment: dependencies: dart_rfb: ^0.9.0 - path: ../dart-rfb + # git: + # path: dart-rfb/ + # ref: main + # url: https://github.com/Goddchen/dart-rfb + # path: ../dart-rfb flutter: sdk: flutter fpdart: ^0.4.0 From 8636b02c5d83cee41f65da1b0876ee15f88b538c Mon Sep 17 00:00:00 2001 From: Spokplacenta <71837821+Spokplacenta@users.noreply.github.com> Date: Wed, 7 Jan 2026 18:27:41 +0100 Subject: [PATCH 09/14] Update lib/src/remote_frame_buffer_widget.dart Co-authored-by: Goddchen --- lib/src/remote_frame_buffer_widget.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/remote_frame_buffer_widget.dart b/lib/src/remote_frame_buffer_widget.dart index 11d8b76..14d4a32 100644 --- a/lib/src/remote_frame_buffer_widget.dart +++ b/lib/src/remote_frame_buffer_widget.dart @@ -234,7 +234,7 @@ class RemoteFrameBufferWidgetState extends State { (final _) {}, ), zrle: () async => _logger.warning( - 'ZRLE rectangle received but not decoded upstream', + '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 {}, ); From 015f868eea86e3d76024942e986c8e749c307acc Mon Sep 17 00:00:00 2001 From: Anthony Marchand Date: Wed, 7 Jan 2026 18:52:50 +0100 Subject: [PATCH 10/14] chore: remove unnecessary json_annotation dependency json_annotation is already provided as a transitive dependency by freezed_annotation, so it doesn't need to be declared explicitly. --- example/pubspec.lock | 2 +- pubspec.lock | 2 +- pubspec.yaml | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 0ba786b..382702c 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -99,7 +99,7 @@ packages: path: ".." relative: true source: path - version: "0.7.0" + version: "0.7.2" flutter_test: dependency: "direct dev" description: flutter diff --git a/pubspec.lock b/pubspec.lock index 2a3ef5e..3d0da3f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -315,7 +315,7 @@ packages: source: hosted version: "0.6.5" json_annotation: - dependency: "direct main" + dependency: transitive description: name: json_annotation sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" diff --git a/pubspec.yaml b/pubspec.yaml index b9bf1ef..f259103 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,7 +19,6 @@ dependencies: sdk: flutter fpdart: ^0.4.0 freezed_annotation: ^2.4.1 - json_annotation: ^4.8.1 logging: ^1.1.0 stream_transform: ^2.1.0 From c3c56ba60e083871597f18748b3602eb902c3aff Mon Sep 17 00:00:00 2001 From: Spokplacenta <71837821+Spokplacenta@users.noreply.github.com> Date: Thu, 8 Jan 2026 00:04:42 +0100 Subject: [PATCH 11/14] Update CHANGELOG.md Co-authored-by: Goddchen --- CHANGELOG.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91cd234..f50372d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,16 +43,11 @@ ## 0.7.0 - Use `dart_rfb` 0.9.0 with ZRLE support and widget-side logging - -## 0.7.1 - - Fix `Unsupported operation: Infinity or NaN toInt` error in gesture detector - Add defensive checks for Infinity/NaN values in coordinate calculations - -## 0.7.2 - - 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 ! From d424e2ad022b0ee3540720fcd5a710b830de1117 Mon Sep 17 00:00:00 2001 From: Anthony Marchand Date: Fri, 24 Apr 2026 11:07:24 +0200 Subject: [PATCH 12/14] chore: update dependencies and improve error handling - Upgrade various dependencies in pubspec.yaml and pubspec.lock for better compatibility and features. - Refactor dart_rfb dependency to use a fork of dart_rfb v0.9.0. - Enhance error handling in remote_frame_buffer_client_isolate.dart to manage connection failures gracefully. --- example/pubspec.lock | 32 ++++--- lib/src/child_size_notifier_widget.dart | 4 +- .../remote_frame_buffer_client_isolate.dart | 17 +++- pubspec.lock | 94 ++++++++++--------- pubspec.yaml | 25 +++-- 5 files changed, 91 insertions(+), 81 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 382702c..897fec8 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -29,10 +29,10 @@ packages: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.4.1" clock: dependency: transitive description: @@ -68,9 +68,11 @@ packages: dart_rfb: dependency: transitive description: - path: "../../dart-rfb" - relative: true - source: path + path: "." + ref: main + resolved-ref: c49e335fc3840bbc2a3e12b08162ea1147a8feda + url: "https://github.com/Spokplacenta/dart-rfb.git" + source: git version: "0.9.0" fake_async: dependency: transitive @@ -99,7 +101,7 @@ packages: path: ".." relative: true source: path - version: "0.7.2" + version: "0.7.0" flutter_test: dependency: "direct dev" description: flutter @@ -165,26 +167,26 @@ packages: dependency: "direct main" description: name: logging - sha256: c0bbfe94d46aedf9b8b3e695cf3bd48c8e14b35e3b2c639e0aa7755d589ba946 + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.3.0" matcher: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.19" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.13.0" meta: dependency: transitive description: @@ -258,10 +260,10 @@ packages: dependency: transitive description: name: test_api - sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 + sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" url: "https://pub.dev" source: hosted - version: "0.7.7" + version: "0.7.10" vector_math: dependency: transitive description: @@ -279,5 +281,5 @@ packages: source: hosted version: "15.0.2" sdks: - dart: ">=3.8.0-0 <4.0.0" + dart: ">=3.9.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 1e44b15..28d6322 100644 --- a/lib/src/child_size_notifier_widget.dart +++ b/lib/src/child_size_notifier_widget.dart @@ -16,10 +16,10 @@ class SizeTrackingWidget extends StatefulWidget { _sizeValueNotifier = sizeValueNotifier; @override - State createState() => _SizeTackingState(); + State createState() => _SizeTrackingState(); } -class _SizeTackingState extends State { +class _SizeTrackingState extends State { Size? _lastSize; void _updateSize() { diff --git a/lib/src/remote_frame_buffer_client_isolate.dart b/lib/src/remote_frame_buffer_client_isolate.dart index d08bb52..d3e8753 100644 --- a/lib/src/remote_frame_buffer_client_isolate.dart +++ b/lib/src/remote_frame_buffer_client_isolate.dart @@ -82,11 +82,18 @@ Future startRemoteFrameBufferClient( frameBufferUpdateRequest: (final _) => client.requestUpdate(), ); }); - await client.connect( - hostname: initMessage.hostName, - password: initMessage.password.toNullable(), - port: initMessage.port, - ); + try { + await client.connect( + hostname: initMessage.hostName, + password: initMessage.password.toNullable(), + port: initMessage.port, + ); + } on Exception catch (e, st) { + // Même format que [Isolate.spawn] `onError` : évite une erreur « non gérée » + // dans l’isolate pour un refus TCP / handshake attendu côté UI. + initMessage.sendPort.send([e, st]); + return; + } client ..handleIncomingMessages() ..requestUpdate(); diff --git a/pubspec.lock b/pubspec.lock index 3d0da3f..ddf23cf 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a + sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f url: "https://pub.dev" source: hosted - version: "61.0.0" + version: "85.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 + sha256: "974859dc0ff5f37bc4313244b3218c791810d03ab3470a579580279ba971a48d" url: "https://pub.dev" source: hosted - version: "5.13.0" + version: "7.7.1" args: dependency: transitive description: @@ -53,10 +53,10 @@ packages: dependency: transitive description: name: build - sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777" + sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0 url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.4.2" build_config: dependency: transitive description: @@ -69,34 +69,34 @@ packages: dependency: transitive description: name: build_daemon - sha256: "6bc5544ea6ce4428266e7ea680e945c68806c4aae2da0eb5e9ccf38df8d6acbf" + sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "4.0.0" build_resolvers: dependency: transitive description: name: build_resolvers - sha256: "7c35a3a7868626257d8aee47b51c26b9dba11eaddf3431117ed2744951416aab" + sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0 url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.4.4" build_runner: dependency: "direct dev" description: name: build_runner - sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727 + sha256: "74691599a5bc750dc96a6b4bfd48f7d9d66453eab04c7f4063134800d6a5c573" url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.4.14" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292" + sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021" url: "https://pub.dev" source: hosted - version: "7.2.7" + version: "8.0.0" built_collection: dependency: transitive description: @@ -117,10 +117,10 @@ packages: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.4.1" checked_yaml: dependency: transitive description: @@ -180,18 +180,20 @@ packages: dart_rfb: dependency: "direct main" description: - path: "../dart-rfb" - relative: true - source: path + path: "." + ref: main + resolved-ref: c49e335fc3840bbc2a3e12b08162ea1147a8feda + url: "https://github.com/Spokplacenta/dart-rfb.git" + source: git version: "0.9.0" dart_style: dependency: transitive description: name: dart_style - sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4" + sha256: "8a0e5fba27e8ee025d2ffb4ee820b4e6e2cf5e4246a6b1a477eb66866947e0bb" url: "https://pub.dev" source: hosted - version: "2.2.4" + version: "3.1.1" fake_async: dependency: transitive description: @@ -225,10 +227,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "5.0.0" flutter_test: dependency: "direct dev" description: flutter @@ -246,18 +248,18 @@ packages: dependency: "direct dev" description: name: freezed - sha256: a434911f643466d78462625df76fd9eb13e57348ff43fe1f77bbe909522c67a1 + sha256: "59a584c24b3acdc5250bb856d0d3e9c0b798ed14a4af1ddb7dc1c7b41df91c9c" url: "https://pub.dev" source: hosted - version: "2.5.2" + version: "2.5.8" freezed_annotation: dependency: "direct main" description: name: freezed_annotation - sha256: f54946fdb1fa7b01f780841937b1a80783a20b393485f3f6cdf336fd6f4705f2 + sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.4.4" frontend_server_client: dependency: transitive description: @@ -350,34 +352,34 @@ packages: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "5.1.1" logging: dependency: "direct main" description: name: logging - sha256: c0bbfe94d46aedf9b8b3e695cf3bd48c8e14b35e3b2c639e0aa7755d589ba946 + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.3.0" matcher: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.19" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.13.0" meta: dependency: transitive description: @@ -422,10 +424,10 @@ packages: dependency: transitive description: name: pub_semver - sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17" + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.2.0" pubspec_parse: dependency: transitive description: @@ -459,10 +461,10 @@ packages: dependency: transitive description: name: source_gen - sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" + sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b" url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "2.0.0" source_span: dependency: transitive description: @@ -515,10 +517,10 @@ packages: dependency: transitive description: name: test_api - sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 + sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" url: "https://pub.dev" source: hosted - version: "0.7.7" + version: "0.7.10" timing: dependency: transitive description: @@ -555,10 +557,10 @@ packages: dependency: transitive description: name: watcher - sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0" + sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.2.1" web_socket_channel: dependency: transitive description: @@ -571,10 +573,10 @@ packages: dependency: transitive description: name: yaml - sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370" + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.2" sdks: - dart: ">=3.8.0-0 <4.0.0" + dart: ">=3.9.0-0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.yaml b/pubspec.yaml index f259103..83e62ed 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,28 +5,27 @@ repository: https://github.com/Goddchen/flutter-vnc version: 0.7.0 environment: - sdk: ">=2.18.6 <3.0.0" - flutter: ">=1.17.0" + sdk: ^3.5.0 + flutter: ">=3.16.0" dependencies: - dart_rfb: ^0.9.0 - # git: - # path: dart-rfb/ - # ref: main - # url: https://github.com/Goddchen/dart-rfb - # path: ../dart-rfb + dart_rfb: + git: + url: https://github.com/Spokplacenta/dart-rfb.git + ref: main flutter: sdk: flutter + # Même contrainte que dart_rfb (dépendance git ci-dessus) pour une seule résolution. fpdart: ^0.4.0 - freezed_annotation: ^2.4.1 - logging: ^1.1.0 + freezed_annotation: ^2.4.4 + logging: ^1.2.0 stream_transform: ^2.1.0 dev_dependencies: - build_runner: ^2.3.3 - flutter_lints: ^2.0.0 + build_runner: ^2.4.13 + flutter_lints: ^5.0.0 flutter_test: sdk: flutter - freezed: ^2.5.2 + freezed: ^2.5.7 flutter: From f09abe5a3165a4674697ee0b073c629b3c8995ce Mon Sep 17 00:00:00 2001 From: Anthony Marchand Date: Tue, 19 May 2026 17:28:05 +0200 Subject: [PATCH 13/14] feat(widget): add clipboard toggle, onFirstFrame, and ZRLE display fixes - Add syncLocalClipboardToRemote (default true) to disable local-to-remote clipboard sync when the host app must not push local clipboard content - Add onFirstFrame callback invoked after the first decoded framebuffer - Fix perpetual loading spinner when ZRLE leaves the internal buffer at zero - Harden framebuffer decode with dimension and buffer length validation - Migrate keyboard handling from deprecated RawKeyboard to HardwareKeyboard - Bump version to 0.8.0 and resolve dart_rfb via local path for workspace dev Co-authored-by: Cursor --- CHANGELOG.md | 8 ++ example/pubspec.lock | 10 +- lib/src/child_size_notifier_widget.dart | 16 +-- lib/src/remote_frame_buffer_widget.dart | 167 +++++++++++++++--------- pubspec.lock | 8 +- pubspec.yaml | 10 +- 6 files changed, 129 insertions(+), 90 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f50372d..d885df1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,3 +51,11 @@ - Use ValueListenableBuilder to react to size changes in real-time - Improve SizeTrackingWidget to use LayoutBuilder for accurate size updates - Thanks @Spokplacenta ! + +## 0.8.0 + +- Add [RemoteFrameBufferWidget.syncLocalClipboardToRemote] (default `true`) to disable pushing the local clipboard to the remote VNC server +- Add [RemoteFrameBufferWidget.onFirstFrame] callback after the first decoded framebuffer is shown +- Fix perpetual loading spinner when ZRLE leaves the internal buffer at zero but a decoded [Image] exists +- Harden [RemoteFrameBufferWidget._decodeAndUpdateImage] with dimension and buffer length checks +- Migrate keyboard handling from deprecated `RawKeyboard` to `HardwareKeyboard` diff --git a/example/pubspec.lock b/example/pubspec.lock index 897fec8..8bc8896 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -68,11 +68,9 @@ packages: dart_rfb: dependency: transitive description: - path: "." - ref: main - resolved-ref: c49e335fc3840bbc2a3e12b08162ea1147a8feda - url: "https://github.com/Spokplacenta/dart-rfb.git" - source: git + path: "../../dart-rfb" + relative: true + source: path version: "0.9.0" fake_async: dependency: transitive @@ -101,7 +99,7 @@ packages: path: ".." relative: true source: path - version: "0.7.0" + version: "0.8.0" flutter_test: dependency: "direct dev" description: flutter diff --git a/lib/src/child_size_notifier_widget.dart b/lib/src/child_size_notifier_widget.dart index 28d6322..43e760a 100644 --- a/lib/src/child_size_notifier_widget.dart +++ b/lib/src/child_size_notifier_widget.dart @@ -46,14 +46,10 @@ class _SizeTrackingState extends State { } @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; - }, - ); - } + Widget build(final BuildContext context) => LayoutBuilder( + builder: (final BuildContext context, final BoxConstraints constraints) { + WidgetsBinding.instance.addPostFrameCallback((final _) => _updateSize()); + return widget._child; + }, + ); } diff --git a/lib/src/remote_frame_buffer_widget.dart b/lib/src/remote_frame_buffer_widget.dart index 14d4a32..fbb356a 100644 --- a/lib/src/remote_frame_buffer_widget.dart +++ b/lib/src/remote_frame_buffer_widget.dart @@ -6,6 +6,7 @@ import 'dart:ui'; import 'package:dart_rfb/dart_rfb.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart' hide Image; +import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart'; import 'package:flutter_rfb/src/child_size_notifier_widget.dart'; import 'package:flutter_rfb/src/extensions/logical_keyboard_key_extensions.dart'; @@ -24,23 +25,36 @@ class RemoteFrameBufferWidget extends StatefulWidget { final Option _connectingWidget; final String _hostName; final Option _onError; + final Option _onFirstFrame; final Option _password; final int _port; + final bool _syncLocalClipboardToRemote; /// Immediately tries to establish a connection to a remote server at /// [hostName]:[port], optionally using [password]. + /// + /// Set [syncLocalClipboardToRemote] to `false` to avoid pushing the local + /// clipboard to the remote server (useful when the host app copies logs or + /// console text that must not be sent to the VNC session). + /// + /// [onFirstFrame] is called once after the first decoded framebuffer image + /// is displayed. RemoteFrameBufferWidget({ super.key, final Widget? connectingWidget, required final String hostName, final void Function(Object error)? onError, + final VoidCallback? onFirstFrame, final String? password, final int port = 5900, + final bool syncLocalClipboardToRemote = true, }) : _connectingWidget = optionOf(connectingWidget), _hostName = hostName, _onError = optionOf(onError), + _onFirstFrame = optionOf(onFirstFrame), _password = optionOf(password), - _port = port; + _port = port, + _syncLocalClipboardToRemote = syncLocalClipboardToRemote; @override State createState() => @@ -49,7 +63,7 @@ class RemoteFrameBufferWidget extends StatefulWidget { @visibleForTesting class RemoteFrameBufferWidgetState extends State { - late Timer _clipBoardMonitorTimer; + Timer? _clipBoardMonitorTimer; Option _frameBuffer = none(); Option _image = none(); Option _isolate = none(); @@ -58,26 +72,14 @@ class RemoteFrameBufferWidgetState extends State { Option> _streamSubscription = none(); @override - Widget build(final BuildContext context) => _frameBuffer - .flatMap( - (final ByteData frameBuffer) => frameBuffer.buffer - .asUint8List( - frameBuffer.offsetInBytes, - frameBuffer.lengthInBytes, - ) - .where((final int byte) => byte != 0) - .isNotEmpty - ? _image - : none(), - ) - .match( + Widget build(final BuildContext context) => _image.match( _buildConnecting, (final Image image) => _buildImage(image: image), ); @override void dispose() { - _clipBoardMonitorTimer.cancel(); + _clipBoardMonitorTimer?.cancel(); _streamSubscription.match( () {}, (final StreamSubscription subscription) => @@ -91,15 +93,17 @@ class RemoteFrameBufferWidgetState extends State { () {}, (final Isolate isolate) => isolate.kill(), ); - RawKeyboard.instance.removeListener(_rawKeyEventListener); + HardwareKeyboard.instance.removeHandler(_keyEventHandler); super.dispose(); } @override void initState() { super.initState(); - _monitorClipBoard(); - RawKeyboard.instance.addListener(_rawKeyEventListener); + if (widget._syncLocalClipboardToRemote) { + _monitorClipBoard(); + } + HardwareKeyboard.instance.addHandler(_keyEventHandler); unawaited(_initAsync()); } @@ -121,7 +125,7 @@ class RemoteFrameBufferWidgetState extends State { sendPort: _isolateSendPort, child: RawImage( image: image, - fit: BoxFit.contain, // Preserve aspect ratio, coordinates are calculated correctly + fit: BoxFit.contain, ), ), ), @@ -131,33 +135,63 @@ class RemoteFrameBufferWidgetState extends State { required final ByteData frameBuffer, required final RemoteFrameBufferIsolateReceiveMessageFrameBufferUpdate message, - }) => - decodeImageFromPixels( - frameBuffer.buffer.asUint8List(), - message.frameBufferWidth, - message.frameBufferHeight, - PixelFormat.bgra8888, - (final Image result) { - if (mounted) { - setState( - () { - _image.match( - () {}, - (final Image image) => image.dispose(), - ); - _image = some(result); - }, - ); - _isolateSendPort.match( - () {}, - (final SendPort sendPort) => sendPort.send( - const RemoteFrameBufferIsolateSendMessage - .frameBufferUpdateRequest(), - ), - ); - } - }, + }) { + final int w = message.frameBufferWidth; + final int h = message.frameBufferHeight; + if (w <= 0 || h <= 0) { + _logger.warning('Framebuffer update: invalid dimensions (${w}x$h).'); + return; + } + final int expectedBytes = w * h * 4; + if (frameBuffer.lengthInBytes < expectedBytes) { + _logger.warning( + 'Framebuffer update: buffer too short (${frameBuffer.lengthInBytes} < $expectedBytes).', ); + return; + } + final Uint8List pixels = frameBuffer.buffer.asUint8List( + frameBuffer.offsetInBytes, + expectedBytes, + ); + decodeImageFromPixels( + pixels, + w, + h, + PixelFormat.bgra8888, + (final Image result) { + if (!mounted) { + result.dispose(); + return; + } + final bool isFirstFrame = _image.isNone(); + setState(() { + _image.match( + () {}, + (final Image image) => image.dispose(), + ); + _image = some(result); + }); + if (isFirstFrame) { + widget._onFirstFrame.match( + () {}, + (final VoidCallback callback) { + SchedulerBinding.instance.addPostFrameCallback((final _) { + if (mounted) { + callback(); + } + }); + }, + ); + } + _isolateSendPort.match( + () {}, + (final SendPort sendPort) => sendPort.send( + const RemoteFrameBufferIsolateSendMessage.frameBufferUpdateRequest(), + ), + ); + }, + ); + } Task _handleFrameBufferUpdateMessage({ required final RemoteFrameBufferIsolateReceiveMessageFrameBufferUpdate @@ -213,9 +247,9 @@ class RemoteFrameBufferWidgetState extends State { ), ).run()) .match( - (final Object error) => - // ignore: avoid_print - print('Error updating frame buffer: $error'), + (final Object error) => debugPrint( + 'RemoteFrameBufferWidget: updateFrameBuffer $error', + ), (final _) {}, ); }, @@ -228,13 +262,13 @@ class RemoteFrameBufferWidgetState extends State { rectangle: rectangle, ).run()) .match( - (final Object error) => - // ignore: avoid_print - print('Error updating frame buffer: $error'), + (final Object error) => debugPrint( + 'RemoteFrameBufferWidget: updateFrameBuffer $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.', + 'ZRLE rectangle received — decoding should happen upstream in dart_rfb.', ), unsupported: (final ByteData bytes) async {}, ); @@ -254,7 +288,6 @@ class RemoteFrameBufferWidgetState extends State { _streamSubscription = some( receivePort.listen( (final Object? message) { - // Error, first is error, second is stacktrace or null if (message is List) { widget._onError.match( () {}, @@ -267,12 +300,14 @@ class RemoteFrameBufferWidgetState extends State { final RemoteFrameBufferIsolateReceiveMessageClipBoardUpdate update, ) => - Clipboard.setData(ClipboardData(text: update.text)), + unawaited( + Clipboard.setData(ClipboardData(text: update.text)), + ), frameBufferUpdate: ( final RemoteFrameBufferIsolateReceiveMessageFrameBufferUpdate update, ) { - _handleFrameBufferUpdateMessage(update: update).run(); + unawaited(_handleFrameBufferUpdateMessage(update: update).run()); }, ); } @@ -326,16 +361,18 @@ class RemoteFrameBufferWidgetState extends State { ); } - void _rawKeyEventListener(final RawKeyEvent rawKeyEvent) => - _isolateSendPort.match( - () {}, - (final SendPort sendPort) => sendPort.send( - RemoteFrameBufferIsolateSendMessage.keyEvent( - down: rawKeyEvent.isKeyPressed(rawKeyEvent.logicalKey), - key: rawKeyEvent.logicalKey.asXWindowSystemKey(), - ), + bool _keyEventHandler(final KeyEvent event) { + _isolateSendPort.match( + () {}, + (final SendPort sendPort) => sendPort.send( + RemoteFrameBufferIsolateSendMessage.keyEvent( + down: event is KeyDownEvent, + key: event.logicalKey.asXWindowSystemKey(), ), - ); + ), + ); + return false; + } /// Updates [frameBuffer] with the given [rectangle]s. @visibleForTesting diff --git a/pubspec.lock b/pubspec.lock index ddf23cf..58dd9af 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -180,11 +180,9 @@ packages: dart_rfb: dependency: "direct main" description: - path: "." - ref: main - resolved-ref: c49e335fc3840bbc2a3e12b08162ea1147a8feda - url: "https://github.com/Spokplacenta/dart-rfb.git" - source: git + path: "../dart-rfb" + relative: true + source: path version: "0.9.0" dart_style: dependency: transitive diff --git a/pubspec.yaml b/pubspec.yaml index 83e62ed..fde02b5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,8 @@ 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.7.0 +version: 0.8.0 +publish_to: 'none' environment: sdk: ^3.5.0 @@ -10,9 +11,10 @@ environment: dependencies: dart_rfb: - git: - url: https://github.com/Spokplacenta/dart-rfb.git - ref: main + path: ../dart-rfb + # git: + # url: https://github.com/Spokplacenta/dart-rfb.git + # ref: main flutter: sdk: flutter # Même contrainte que dart_rfb (dépendance git ci-dessus) pour une seule résolution. From 04e7096846f005c798738f557ec230aa9c63eabf Mon Sep 17 00:00:00 2001 From: Anthony Marchand Date: Wed, 20 May 2026 18:18:19 +0200 Subject: [PATCH 14/14] chore(deps): resolve dart_rfb from Spokplacenta GitHub instead of local path Switch dart_rfb dependency from ../dart-rfb path to the main branch of https://github.com/Spokplacenta/dart-rfb.git and refresh lockfiles. Co-authored-by: Cursor --- example/pubspec.lock | 8 +++++--- pubspec.lock | 8 +++++--- pubspec.yaml | 7 +++---- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 8bc8896..fbc267b 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -68,9 +68,11 @@ packages: dart_rfb: dependency: transitive description: - path: "../../dart-rfb" - relative: true - source: path + path: "." + ref: main + resolved-ref: "4321ffb3199ecb47c429b725be6a1c2472b15a1d" + url: "https://github.com/Spokplacenta/dart-rfb.git" + source: git version: "0.9.0" fake_async: dependency: transitive diff --git a/pubspec.lock b/pubspec.lock index 58dd9af..e18e1e2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -180,9 +180,11 @@ packages: dart_rfb: dependency: "direct main" description: - path: "../dart-rfb" - relative: true - source: path + path: "." + ref: main + resolved-ref: "4321ffb3199ecb47c429b725be6a1c2472b15a1d" + url: "https://github.com/Spokplacenta/dart-rfb.git" + source: git version: "0.9.0" dart_style: dependency: transitive diff --git a/pubspec.yaml b/pubspec.yaml index fde02b5..9b79a6f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,10 +11,9 @@ environment: dependencies: dart_rfb: - path: ../dart-rfb - # git: - # url: https://github.com/Spokplacenta/dart-rfb.git - # ref: main + git: + url: https://github.com/Spokplacenta/dart-rfb.git + ref: main flutter: sdk: flutter # Même contrainte que dart_rfb (dépendance git ci-dessus) pour une seule résolution.