From 2351cfbec678b3b708db2121d218da00809f89f5 Mon Sep 17 00:00:00 2001 From: Craftplacer <22963120+Craftplacer@users.noreply.github.com> Date: Thu, 7 Nov 2024 01:36:36 +0100 Subject: [PATCH 1/4] Rename files --- packages/sane/lib/sane.dart | 4 ++-- packages/sane/lib/src/isolate_messages/cancel.dart | 2 +- .../sane/lib/src/isolate_messages/control_button_option.dart | 2 +- packages/sane/lib/src/isolate_messages/control_option.dart | 2 +- packages/sane/lib/src/isolate_messages/exit.dart | 2 +- .../lib/src/isolate_messages/get_all_option_descriptors.dart | 2 +- packages/sane/lib/src/isolate_messages/get_devices.dart | 2 +- .../sane/lib/src/isolate_messages/get_option_descriptor.dart | 2 +- packages/sane/lib/src/isolate_messages/get_parameters.dart | 2 +- packages/sane/lib/src/isolate_messages/init.dart | 2 +- packages/sane/lib/src/isolate_messages/interface.dart | 2 +- packages/sane/lib/src/isolate_messages/open.dart | 2 +- packages/sane/lib/src/isolate_messages/read.dart | 2 +- packages/sane/lib/src/isolate_messages/set_io_mode.dart | 2 +- packages/sane/lib/src/isolate_messages/start.dart | 2 +- packages/sane/lib/src/{sane_isolate.dart => sane_native.dart} | 0 packages/sane/lib/src/{sane.dart => sane_sync.dart} | 0 packages/sane/test/sane_test.dart | 2 +- 18 files changed, 17 insertions(+), 17 deletions(-) rename packages/sane/lib/src/{sane_isolate.dart => sane_native.dart} (100%) rename packages/sane/lib/src/{sane.dart => sane_sync.dart} (100%) diff --git a/packages/sane/lib/sane.dart b/packages/sane/lib/sane.dart index 7769469..5dcce32 100644 --- a/packages/sane/lib/sane.dart +++ b/packages/sane/lib/sane.dart @@ -1,8 +1,8 @@ library; export 'src/exceptions.dart'; -export 'src/sane.dart'; export 'src/sane_dev.dart'; -export 'src/sane_isolate.dart'; +export 'src/sane_native.dart'; +export 'src/sane_sync.dart'; export 'src/structures.dart'; export 'src/utils.dart'; diff --git a/packages/sane/lib/src/isolate_messages/cancel.dart b/packages/sane/lib/src/isolate_messages/cancel.dart index 4b5d65b..dbe9b3c 100644 --- a/packages/sane/lib/src/isolate_messages/cancel.dart +++ b/packages/sane/lib/src/isolate_messages/cancel.dart @@ -1,5 +1,5 @@ import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane.dart'; +import 'package:sane/src/sane_sync.dart'; import 'package:sane/src/structures.dart'; class CancelMessage implements IsolateMessage { diff --git a/packages/sane/lib/src/isolate_messages/control_button_option.dart b/packages/sane/lib/src/isolate_messages/control_button_option.dart index 8176f48..ba7bb6a 100644 --- a/packages/sane/lib/src/isolate_messages/control_button_option.dart +++ b/packages/sane/lib/src/isolate_messages/control_button_option.dart @@ -1,5 +1,5 @@ import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane.dart'; +import 'package:sane/src/sane_sync.dart'; import 'package:sane/src/structures.dart'; class ControlButtonOptionMessage diff --git a/packages/sane/lib/src/isolate_messages/control_option.dart b/packages/sane/lib/src/isolate_messages/control_option.dart index 99f6942..b184e31 100644 --- a/packages/sane/lib/src/isolate_messages/control_option.dart +++ b/packages/sane/lib/src/isolate_messages/control_option.dart @@ -1,5 +1,5 @@ import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane.dart'; +import 'package:sane/src/sane_sync.dart'; import 'package:sane/src/structures.dart'; class ControlValueOptionMessage diff --git a/packages/sane/lib/src/isolate_messages/exit.dart b/packages/sane/lib/src/isolate_messages/exit.dart index 0e797e2..a9f4115 100644 --- a/packages/sane/lib/src/isolate_messages/exit.dart +++ b/packages/sane/lib/src/isolate_messages/exit.dart @@ -1,5 +1,5 @@ import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane.dart'; +import 'package:sane/src/sane_sync.dart'; class ExitMessage implements IsolateMessage { @override diff --git a/packages/sane/lib/src/isolate_messages/get_all_option_descriptors.dart b/packages/sane/lib/src/isolate_messages/get_all_option_descriptors.dart index 8412f13..9d38540 100644 --- a/packages/sane/lib/src/isolate_messages/get_all_option_descriptors.dart +++ b/packages/sane/lib/src/isolate_messages/get_all_option_descriptors.dart @@ -1,5 +1,5 @@ import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane.dart'; +import 'package:sane/src/sane_sync.dart'; import 'package:sane/src/structures.dart'; class GetAllOptionDescriptorsMessage diff --git a/packages/sane/lib/src/isolate_messages/get_devices.dart b/packages/sane/lib/src/isolate_messages/get_devices.dart index d54bf17..b71b278 100644 --- a/packages/sane/lib/src/isolate_messages/get_devices.dart +++ b/packages/sane/lib/src/isolate_messages/get_devices.dart @@ -1,5 +1,5 @@ import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane.dart'; +import 'package:sane/src/sane_sync.dart'; import 'package:sane/src/structures.dart'; class GetDevicesMessage implements IsolateMessage { diff --git a/packages/sane/lib/src/isolate_messages/get_option_descriptor.dart b/packages/sane/lib/src/isolate_messages/get_option_descriptor.dart index ea02661..ce12306 100644 --- a/packages/sane/lib/src/isolate_messages/get_option_descriptor.dart +++ b/packages/sane/lib/src/isolate_messages/get_option_descriptor.dart @@ -1,5 +1,5 @@ import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane.dart'; +import 'package:sane/src/sane_sync.dart'; import 'package:sane/src/structures.dart'; class GetOptionDescriptorMessage diff --git a/packages/sane/lib/src/isolate_messages/get_parameters.dart b/packages/sane/lib/src/isolate_messages/get_parameters.dart index f152386..73b6528 100644 --- a/packages/sane/lib/src/isolate_messages/get_parameters.dart +++ b/packages/sane/lib/src/isolate_messages/get_parameters.dart @@ -1,5 +1,5 @@ import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane.dart'; +import 'package:sane/src/sane_sync.dart'; import 'package:sane/src/structures.dart'; class GetParametersMessage implements IsolateMessage { diff --git a/packages/sane/lib/src/isolate_messages/init.dart b/packages/sane/lib/src/isolate_messages/init.dart index df035db..b614a8e 100644 --- a/packages/sane/lib/src/isolate_messages/init.dart +++ b/packages/sane/lib/src/isolate_messages/init.dart @@ -1,5 +1,5 @@ import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane.dart'; +import 'package:sane/src/sane_sync.dart'; class InitMessage implements IsolateMessage { @override diff --git a/packages/sane/lib/src/isolate_messages/interface.dart b/packages/sane/lib/src/isolate_messages/interface.dart index 2515622..cd01c9e 100644 --- a/packages/sane/lib/src/isolate_messages/interface.dart +++ b/packages/sane/lib/src/isolate_messages/interface.dart @@ -1,4 +1,4 @@ -import 'package:sane/src/sane.dart'; +import 'package:sane/src/sane_sync.dart'; abstract interface class IsolateMessage { Future handle(Sane sane); diff --git a/packages/sane/lib/src/isolate_messages/open.dart b/packages/sane/lib/src/isolate_messages/open.dart index 13ca782..1fa727e 100644 --- a/packages/sane/lib/src/isolate_messages/open.dart +++ b/packages/sane/lib/src/isolate_messages/open.dart @@ -1,5 +1,5 @@ import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane.dart'; +import 'package:sane/src/sane_sync.dart'; import 'package:sane/src/structures.dart'; class OpenMessage implements IsolateMessage { diff --git a/packages/sane/lib/src/isolate_messages/read.dart b/packages/sane/lib/src/isolate_messages/read.dart index e9ad229..c424f06 100644 --- a/packages/sane/lib/src/isolate_messages/read.dart +++ b/packages/sane/lib/src/isolate_messages/read.dart @@ -1,7 +1,7 @@ import 'dart:typed_data'; import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane.dart'; +import 'package:sane/src/sane_sync.dart'; import 'package:sane/src/structures.dart'; class ReadMessage implements IsolateMessage { diff --git a/packages/sane/lib/src/isolate_messages/set_io_mode.dart b/packages/sane/lib/src/isolate_messages/set_io_mode.dart index 7478351..e8e2e27 100644 --- a/packages/sane/lib/src/isolate_messages/set_io_mode.dart +++ b/packages/sane/lib/src/isolate_messages/set_io_mode.dart @@ -1,5 +1,5 @@ import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane.dart'; +import 'package:sane/src/sane_sync.dart'; import 'package:sane/src/structures.dart'; class SetIOModeMessage implements IsolateMessage { diff --git a/packages/sane/lib/src/isolate_messages/start.dart b/packages/sane/lib/src/isolate_messages/start.dart index fe961b2..29e70c0 100644 --- a/packages/sane/lib/src/isolate_messages/start.dart +++ b/packages/sane/lib/src/isolate_messages/start.dart @@ -1,5 +1,5 @@ import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane.dart'; +import 'package:sane/src/sane_sync.dart'; import 'package:sane/src/structures.dart'; class StartMessage implements IsolateMessage { diff --git a/packages/sane/lib/src/sane_isolate.dart b/packages/sane/lib/src/sane_native.dart similarity index 100% rename from packages/sane/lib/src/sane_isolate.dart rename to packages/sane/lib/src/sane_native.dart diff --git a/packages/sane/lib/src/sane.dart b/packages/sane/lib/src/sane_sync.dart similarity index 100% rename from packages/sane/lib/src/sane.dart rename to packages/sane/lib/src/sane_sync.dart diff --git a/packages/sane/test/sane_test.dart b/packages/sane/test/sane_test.dart index 0081b25..2d41dd4 100644 --- a/packages/sane/test/sane_test.dart +++ b/packages/sane/test/sane_test.dart @@ -1,4 +1,4 @@ -import 'package:sane/src/sane.dart'; +import 'package:sane/src/sane_sync.dart'; import 'package:test/test.dart'; void main() { From 1f569f3f827f1a74c1a4950a0be9b77bfdd1eb43 Mon Sep 17 00:00:00 2001 From: Craftplacer <22963120+Craftplacer@users.noreply.github.com> Date: Thu, 7 Nov 2024 01:39:41 +0100 Subject: [PATCH 2/4] Move implementations --- packages/sane/example/main.dart | 2 +- packages/sane/lib/sane.dart | 6 +++--- packages/sane/lib/src/{ => impl}/sane_dev.dart | 0 packages/sane/lib/src/{ => impl}/sane_native.dart | 4 ++-- packages/sane/lib/src/{ => impl}/sane_sync.dart | 0 packages/sane/lib/src/isolate_messages/cancel.dart | 2 +- .../lib/src/isolate_messages/control_button_option.dart | 2 +- packages/sane/lib/src/isolate_messages/control_option.dart | 2 +- packages/sane/lib/src/isolate_messages/exit.dart | 2 +- .../src/isolate_messages/get_all_option_descriptors.dart | 2 +- packages/sane/lib/src/isolate_messages/get_devices.dart | 2 +- .../lib/src/isolate_messages/get_option_descriptor.dart | 2 +- packages/sane/lib/src/isolate_messages/get_parameters.dart | 2 +- packages/sane/lib/src/isolate_messages/init.dart | 2 +- packages/sane/lib/src/isolate_messages/interface.dart | 2 +- packages/sane/lib/src/isolate_messages/open.dart | 2 +- packages/sane/lib/src/isolate_messages/read.dart | 2 +- packages/sane/lib/src/isolate_messages/set_io_mode.dart | 2 +- packages/sane/lib/src/isolate_messages/start.dart | 2 +- packages/sane/test/sane_test.dart | 5 ++--- 20 files changed, 22 insertions(+), 23 deletions(-) rename packages/sane/lib/src/{ => impl}/sane_dev.dart (100%) rename packages/sane/lib/src/{ => impl}/sane_native.dart (99%) rename packages/sane/lib/src/{ => impl}/sane_sync.dart (100%) diff --git a/packages/sane/example/main.dart b/packages/sane/example/main.dart index d93c5a7..5ff456b 100644 --- a/packages/sane/example/main.dart +++ b/packages/sane/example/main.dart @@ -12,7 +12,7 @@ void main(List args) async { print('${record.level.name}: ${record.time}: ${record.message}'); }); - final sane = SaneIsolate(sane: SaneDev()); + final sane = SaneNative(sane: SaneDev()); await sane.spawn(); await sane.init(); diff --git a/packages/sane/lib/sane.dart b/packages/sane/lib/sane.dart index 5dcce32..c0c0092 100644 --- a/packages/sane/lib/sane.dart +++ b/packages/sane/lib/sane.dart @@ -1,8 +1,8 @@ library; export 'src/exceptions.dart'; -export 'src/sane_dev.dart'; -export 'src/sane_native.dart'; -export 'src/sane_sync.dart'; +export 'src/impl/sane_dev.dart'; +export 'src/impl/sane_native.dart'; +export 'src/impl/sane_sync.dart'; export 'src/structures.dart'; export 'src/utils.dart'; diff --git a/packages/sane/lib/src/sane_dev.dart b/packages/sane/lib/src/impl/sane_dev.dart similarity index 100% rename from packages/sane/lib/src/sane_dev.dart rename to packages/sane/lib/src/impl/sane_dev.dart diff --git a/packages/sane/lib/src/sane_native.dart b/packages/sane/lib/src/impl/sane_native.dart similarity index 99% rename from packages/sane/lib/src/sane_native.dart rename to packages/sane/lib/src/impl/sane_native.dart index e666c59..65a674a 100644 --- a/packages/sane/lib/src/sane_native.dart +++ b/packages/sane/lib/src/impl/sane_native.dart @@ -19,8 +19,8 @@ import 'package:sane/src/isolate_messages/read.dart'; import 'package:sane/src/isolate_messages/set_io_mode.dart'; import 'package:sane/src/isolate_messages/start.dart'; -class SaneIsolate implements Sane { - SaneIsolate({ +class SaneNative implements Sane { + SaneNative({ required Sane sane, }) : _sane = sane; diff --git a/packages/sane/lib/src/sane_sync.dart b/packages/sane/lib/src/impl/sane_sync.dart similarity index 100% rename from packages/sane/lib/src/sane_sync.dart rename to packages/sane/lib/src/impl/sane_sync.dart diff --git a/packages/sane/lib/src/isolate_messages/cancel.dart b/packages/sane/lib/src/isolate_messages/cancel.dart index dbe9b3c..a4bb836 100644 --- a/packages/sane/lib/src/isolate_messages/cancel.dart +++ b/packages/sane/lib/src/isolate_messages/cancel.dart @@ -1,5 +1,5 @@ +import 'package:sane/src/impl/sane_sync.dart'; import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane_sync.dart'; import 'package:sane/src/structures.dart'; class CancelMessage implements IsolateMessage { diff --git a/packages/sane/lib/src/isolate_messages/control_button_option.dart b/packages/sane/lib/src/isolate_messages/control_button_option.dart index ba7bb6a..2f6c8ad 100644 --- a/packages/sane/lib/src/isolate_messages/control_button_option.dart +++ b/packages/sane/lib/src/isolate_messages/control_button_option.dart @@ -1,5 +1,5 @@ +import 'package:sane/src/impl/sane_sync.dart'; import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane_sync.dart'; import 'package:sane/src/structures.dart'; class ControlButtonOptionMessage diff --git a/packages/sane/lib/src/isolate_messages/control_option.dart b/packages/sane/lib/src/isolate_messages/control_option.dart index b184e31..d886e36 100644 --- a/packages/sane/lib/src/isolate_messages/control_option.dart +++ b/packages/sane/lib/src/isolate_messages/control_option.dart @@ -1,5 +1,5 @@ +import 'package:sane/src/impl/sane_sync.dart'; import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane_sync.dart'; import 'package:sane/src/structures.dart'; class ControlValueOptionMessage diff --git a/packages/sane/lib/src/isolate_messages/exit.dart b/packages/sane/lib/src/isolate_messages/exit.dart index a9f4115..cf2d7f7 100644 --- a/packages/sane/lib/src/isolate_messages/exit.dart +++ b/packages/sane/lib/src/isolate_messages/exit.dart @@ -1,5 +1,5 @@ +import 'package:sane/src/impl/sane_sync.dart'; import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane_sync.dart'; class ExitMessage implements IsolateMessage { @override diff --git a/packages/sane/lib/src/isolate_messages/get_all_option_descriptors.dart b/packages/sane/lib/src/isolate_messages/get_all_option_descriptors.dart index 9d38540..e1c2c8e 100644 --- a/packages/sane/lib/src/isolate_messages/get_all_option_descriptors.dart +++ b/packages/sane/lib/src/isolate_messages/get_all_option_descriptors.dart @@ -1,5 +1,5 @@ +import 'package:sane/src/impl/sane_sync.dart'; import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane_sync.dart'; import 'package:sane/src/structures.dart'; class GetAllOptionDescriptorsMessage diff --git a/packages/sane/lib/src/isolate_messages/get_devices.dart b/packages/sane/lib/src/isolate_messages/get_devices.dart index b71b278..c48d7d7 100644 --- a/packages/sane/lib/src/isolate_messages/get_devices.dart +++ b/packages/sane/lib/src/isolate_messages/get_devices.dart @@ -1,5 +1,5 @@ +import 'package:sane/src/impl/sane_sync.dart'; import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane_sync.dart'; import 'package:sane/src/structures.dart'; class GetDevicesMessage implements IsolateMessage { diff --git a/packages/sane/lib/src/isolate_messages/get_option_descriptor.dart b/packages/sane/lib/src/isolate_messages/get_option_descriptor.dart index ce12306..8d6347d 100644 --- a/packages/sane/lib/src/isolate_messages/get_option_descriptor.dart +++ b/packages/sane/lib/src/isolate_messages/get_option_descriptor.dart @@ -1,5 +1,5 @@ +import 'package:sane/src/impl/sane_sync.dart'; import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane_sync.dart'; import 'package:sane/src/structures.dart'; class GetOptionDescriptorMessage diff --git a/packages/sane/lib/src/isolate_messages/get_parameters.dart b/packages/sane/lib/src/isolate_messages/get_parameters.dart index 73b6528..009a81a 100644 --- a/packages/sane/lib/src/isolate_messages/get_parameters.dart +++ b/packages/sane/lib/src/isolate_messages/get_parameters.dart @@ -1,5 +1,5 @@ +import 'package:sane/src/impl/sane_sync.dart'; import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane_sync.dart'; import 'package:sane/src/structures.dart'; class GetParametersMessage implements IsolateMessage { diff --git a/packages/sane/lib/src/isolate_messages/init.dart b/packages/sane/lib/src/isolate_messages/init.dart index b614a8e..a480352 100644 --- a/packages/sane/lib/src/isolate_messages/init.dart +++ b/packages/sane/lib/src/isolate_messages/init.dart @@ -1,5 +1,5 @@ +import 'package:sane/src/impl/sane_sync.dart'; import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane_sync.dart'; class InitMessage implements IsolateMessage { @override diff --git a/packages/sane/lib/src/isolate_messages/interface.dart b/packages/sane/lib/src/isolate_messages/interface.dart index cd01c9e..1e09181 100644 --- a/packages/sane/lib/src/isolate_messages/interface.dart +++ b/packages/sane/lib/src/isolate_messages/interface.dart @@ -1,4 +1,4 @@ -import 'package:sane/src/sane_sync.dart'; +import 'package:sane/src/impl/sane_sync.dart'; abstract interface class IsolateMessage { Future handle(Sane sane); diff --git a/packages/sane/lib/src/isolate_messages/open.dart b/packages/sane/lib/src/isolate_messages/open.dart index 1fa727e..81f25e9 100644 --- a/packages/sane/lib/src/isolate_messages/open.dart +++ b/packages/sane/lib/src/isolate_messages/open.dart @@ -1,5 +1,5 @@ +import 'package:sane/src/impl/sane_sync.dart'; import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane_sync.dart'; import 'package:sane/src/structures.dart'; class OpenMessage implements IsolateMessage { diff --git a/packages/sane/lib/src/isolate_messages/read.dart b/packages/sane/lib/src/isolate_messages/read.dart index c424f06..cef282d 100644 --- a/packages/sane/lib/src/isolate_messages/read.dart +++ b/packages/sane/lib/src/isolate_messages/read.dart @@ -1,7 +1,7 @@ import 'dart:typed_data'; +import 'package:sane/src/impl/sane_sync.dart'; import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane_sync.dart'; import 'package:sane/src/structures.dart'; class ReadMessage implements IsolateMessage { diff --git a/packages/sane/lib/src/isolate_messages/set_io_mode.dart b/packages/sane/lib/src/isolate_messages/set_io_mode.dart index e8e2e27..802caaa 100644 --- a/packages/sane/lib/src/isolate_messages/set_io_mode.dart +++ b/packages/sane/lib/src/isolate_messages/set_io_mode.dart @@ -1,5 +1,5 @@ +import 'package:sane/src/impl/sane_sync.dart'; import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane_sync.dart'; import 'package:sane/src/structures.dart'; class SetIOModeMessage implements IsolateMessage { diff --git a/packages/sane/lib/src/isolate_messages/start.dart b/packages/sane/lib/src/isolate_messages/start.dart index 29e70c0..1212705 100644 --- a/packages/sane/lib/src/isolate_messages/start.dart +++ b/packages/sane/lib/src/isolate_messages/start.dart @@ -1,5 +1,5 @@ +import 'package:sane/src/impl/sane_sync.dart'; import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/sane_sync.dart'; import 'package:sane/src/structures.dart'; class StartMessage implements IsolateMessage { diff --git a/packages/sane/test/sane_test.dart b/packages/sane/test/sane_test.dart index 2d41dd4..e05952a 100644 --- a/packages/sane/test/sane_test.dart +++ b/packages/sane/test/sane_test.dart @@ -1,9 +1,8 @@ -import 'package:sane/src/sane_sync.dart'; +import 'package:sane/src/impl/sane_sync.dart'; import 'package:test/test.dart'; void main() { test('Sane init test', () { - final sane = Sane(); - expect(sane.init, returnsNormally); + expect(Sane.new, returnsNormally); }); } From 096808534ee75cd2e7e75b833757137dc0eafb58 Mon Sep 17 00:00:00 2001 From: Craftplacer <22963120+Craftplacer@users.noreply.github.com> Date: Thu, 7 Nov 2024 03:29:02 +0100 Subject: [PATCH 3/4] Rearchitect library --- .gitignore | 3 + packages/sane/example/main.dart | 35 +- packages/sane/lib/sane.dart | 5 +- packages/sane/lib/src/bindings.g.dart | 11 + packages/sane/lib/src/extensions.dart | 2 + packages/sane/lib/src/impl/sane_dev.dart | 257 ++++--- packages/sane/lib/src/impl/sane_native.dart | 420 ++++++------ packages/sane/lib/src/impl/sane_sync.dart | 632 ++++++++---------- packages/sane/lib/src/isolate.dart | 117 ++++ .../sane/lib/src/isolate_messages/cancel.dart | 10 +- .../sane/lib/src/isolate_messages/close.dart | 7 +- .../control_button_option.dart | 20 +- .../src/isolate_messages/control_option.dart | 61 +- .../sane/lib/src/isolate_messages/exit.dart | 4 +- .../get_all_option_descriptors.dart | 15 +- .../lib/src/isolate_messages/get_devices.dart | 3 +- .../get_option_descriptor.dart | 13 +- .../src/isolate_messages/get_parameters.dart | 15 +- .../sane/lib/src/isolate_messages/init.dart | 11 +- .../lib/src/isolate_messages/interface.dart | 4 +- .../sane/lib/src/isolate_messages/open.dart | 22 - .../sane/lib/src/isolate_messages/read.dart | 20 +- .../lib/src/isolate_messages/set_io_mode.dart | 21 - .../sane/lib/src/isolate_messages/start.dart | 10 +- packages/sane/lib/src/logger.dart | 2 + packages/sane/lib/src/sane.dart | 210 ++++++ packages/sane/lib/src/structures.dart | 34 - packages/sane/lib/src/type_conversion.dart | 52 +- packages/sane/lib/src/utils.dart | 17 - packages/sane/pubspec.yaml | 4 + packages/sane/test/sane_singleton_test.dart | 2 +- packages/sane/test/sane_test.dart | 2 +- 32 files changed, 1079 insertions(+), 962 deletions(-) create mode 100644 packages/sane/lib/src/isolate.dart delete mode 100644 packages/sane/lib/src/isolate_messages/open.dart delete mode 100644 packages/sane/lib/src/isolate_messages/set_io_mode.dart create mode 100644 packages/sane/lib/src/sane.dart delete mode 100644 packages/sane/lib/src/utils.dart diff --git a/.gitignore b/.gitignore index cdd93e9..af7e6a5 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,6 @@ migrate_working_dir/ /build/ /coverage/ pubspec.lock + +# Runtime output +output.ppm \ No newline at end of file diff --git a/packages/sane/example/main.dart b/packages/sane/example/main.dart index 5ff456b..b6d5de2 100644 --- a/packages/sane/example/main.dart +++ b/packages/sane/example/main.dart @@ -5,6 +5,8 @@ import 'dart:typed_data'; import 'package:logging/logging.dart'; import 'package:sane/sane.dart'; +import 'package:sane/src/impl/sane_dev.dart'; +import 'package:sane/src/impl/sane_native.dart'; void main(List args) async { Logger.root.level = Level.ALL; @@ -12,10 +14,11 @@ void main(List args) async { print('${record.level.name}: ${record.time}: ${record.message}'); }); - final sane = SaneNative(sane: SaneDev()); - await sane.spawn(); + final sane = NativeSane(MockSane()); - await sane.init(); + final version = await sane.initialize(); + + print(version); final devices = await sane.getDevices(localOnly: false); for (final device in devices) { @@ -26,40 +29,36 @@ void main(List args) async { return; } - final handle = await sane.openDevice(devices.first); + final device = devices.first; - final optionDescriptors = await sane.getAllOptionDescriptors(handle); + final optionDescriptors = await device.getAllOptionDescriptors(); for (final optionDescriptor in optionDescriptors) { if (optionDescriptor.name == 'mode') { - await sane.controlStringOption( - handle: handle, - index: optionDescriptor.index, - action: SaneAction.setValue, - value: 'Color', + await device.controlStringOption( + optionDescriptor.index, + SaneAction.setValue, + 'Color', ); break; } } - await sane.start(handle); + await device.start(); - final parameters = await sane.getParameters(handle); + final parameters = await device.getParameters(); print('Parameters: format(${parameters.format}), depth(${parameters.depth})'); final rawPixelDataList = []; Uint8List? bytes; while (true) { - bytes = await sane.read(handle, parameters.bytesPerLine); + bytes = await device.read(bufferSize: parameters.bytesPerLine); if (bytes.isEmpty) break; rawPixelDataList.add(bytes); } - await sane.cancel(handle); - await sane.close(handle); - await sane.exit(); - - sane.kill(); + await device.cancel(); + await device.close(); Uint8List mergeUint8Lists(List lists) { final totalLength = lists.fold(0, (length, list) => length + list.length); diff --git a/packages/sane/lib/sane.dart b/packages/sane/lib/sane.dart index c0c0092..decfc3f 100644 --- a/packages/sane/lib/sane.dart +++ b/packages/sane/lib/sane.dart @@ -1,8 +1,5 @@ library; export 'src/exceptions.dart'; -export 'src/impl/sane_dev.dart'; -export 'src/impl/sane_native.dart'; -export 'src/impl/sane_sync.dart'; +export 'src/sane.dart'; export 'src/structures.dart'; -export 'src/utils.dart'; diff --git a/packages/sane/lib/src/bindings.g.dart b/packages/sane/lib/src/bindings.g.dart index c25efa9..6adf3ff 100644 --- a/packages/sane/lib/src/bindings.g.dart +++ b/packages/sane/lib/src/bindings.g.dart @@ -250,6 +250,17 @@ class LibSane { 'sane_strstatus'); late final _sane_strstatus = _sane_strstatusPtr.asFunction(); + + late final addresses = _SymbolAddresses(this); +} + +class _SymbolAddresses { + final LibSane _library; + + _SymbolAddresses(this._library); + + ffi.Pointer> + get sane_close => _library._sane_closePtr; } enum SANE_Status { diff --git a/packages/sane/lib/src/extensions.dart b/packages/sane/lib/src/extensions.dart index 85bae1f..0f0d5fb 100644 --- a/packages/sane/lib/src/extensions.dart +++ b/packages/sane/lib/src/extensions.dart @@ -1,6 +1,8 @@ +import 'package:meta/meta.dart'; import 'package:sane/src/bindings.g.dart'; import 'package:sane/src/exceptions.dart'; +@internal extension SaneStatusExtension on SANE_Status { /// Throws [SaneException] if the status is not [SANE_Status.STATUS_GOOD]. @pragma('vm:prefer-inline') diff --git a/packages/sane/lib/src/impl/sane_dev.dart b/packages/sane/lib/src/impl/sane_dev.dart index 8e39549..bf5d78f 100644 --- a/packages/sane/lib/src/impl/sane_dev.dart +++ b/packages/sane/lib/src/impl/sane_dev.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:typed_data'; import 'package:logging/logging.dart'; @@ -5,139 +6,78 @@ import 'package:sane/sane.dart'; final _logger = Logger('sane.dev'); -class SaneDev implements Sane { +class MockSane implements Sane { @override - Future cancel(SaneHandle handle) { - return Future.delayed(const Duration(seconds: 1), () { - _logger.finest('sane_cancel()'); - }); - } + void dispose() => _logger.finest('disposed'); @override - Future close(SaneHandle handle) { - return Future.delayed(const Duration(seconds: 1), () { - _logger.finest('sane_close()'); - }); + Future> getDevices({required bool localOnly}) { + _logger.finest('sane_get_devices()'); + return Future.delayed( + const Duration(seconds: 1), + () => List.generate(3, SaneDevDevice.new), + ); } @override - Future> controlBoolOption({ - required SaneHandle handle, - required int index, - required SaneAction action, - bool? value, - }) { - return Future.delayed(const Duration(seconds: 1), () { - _logger.finest('sane_controlBoolOption()'); - return SaneOptionResult(result: value ?? true, infos: []); - }); + SaneVersion initialize([AuthCallback? authCallback]) { + _logger.finest('initialized'); + return const SaneVersion.fromCode(13371337); } +} + +class SaneDevDevice implements SaneDevice { + const SaneDevDevice(this.index); + + final int index; @override - Future> controlButtonOption({ - required SaneHandle handle, - required int index, - }) { - return Future.delayed(const Duration(seconds: 1), () { - _logger.finest('sane_controlButtonOption()'); - return SaneOptionResult(result: null, infos: []); - }); + Future cancel() { + _logger.finest('sane_cancel()'); + return Future.delayed(const Duration(seconds: 1)); } @override - Future> controlFixedOption({ - required SaneHandle handle, - required int index, - required SaneAction action, - double? value, - }) { - return Future.delayed(const Duration(seconds: 1), () { - _logger.finest('sane_controlFixedOption()'); - return SaneOptionResult(result: value ?? .1, infos: []); - }); + Future close() { + _logger.finest('sane_close()'); + return Future.delayed(const Duration(seconds: 1)); } @override - Future> controlIntOption({ - required SaneHandle handle, - required int index, - required SaneAction action, - int? value, - }) { - return Future.delayed(const Duration(seconds: 1), () { - _logger.finest('sane_controlIntOption()'); - return SaneOptionResult(result: value ?? 1, infos: []); - }); - } + String get model => 'Model $index'; @override - Future> controlStringOption({ - required SaneHandle handle, - required int index, - required SaneAction action, - String? value, - }) { - return Future.delayed(const Duration(seconds: 1), () { - _logger.finest('sane_controlStringOption()'); - return SaneOptionResult(result: value ?? 'value', infos: []); - }); - } + String get name => 'Name $index'; @override - Future exit() { - return Future(() { - _logger.finest('sane_exit()'); - }); + Future read({required int bufferSize}) { + _logger.finest('sane_read()'); + return Future.delayed( + const Duration(seconds: 1), + () => Uint8List.fromList([]), + ); } @override - Future> getAllOptionDescriptors( - SaneHandle handle, - ) { - return Future.delayed(const Duration(seconds: 1), () { - _logger.finest('sane_getAllOptionDescriptors()'); - return [ - SaneOptionDescriptor( - index: 0, - name: 'name', - title: 'title', - desc: 'desc', - type: SaneOptionValueType.int, - unit: SaneOptionUnit.none, - size: 1, - capabilities: [], - constraint: null, - ), - ]; - }); + Future start() { + _logger.finest('sane_start()'); + return Future.delayed(const Duration(seconds: 1)); } @override - Future> getDevices({ - required bool localOnly, - }) { - return Future.delayed(const Duration(seconds: 1), () { - _logger.finest('sane_getDevices()'); - return [ - for (var i = 0; i < 3; i++) - SaneDevice( - name: 'name $i', - vendor: 'Vendor$i', - model: 'Model$i', - type: 'Type$i', - ), - ]; - }); - } + String get type => 'Type $index'; + + @override + String? get vendor => 'Vendor $index'; @override Future getOptionDescriptor( - SaneHandle handle, int index, ) { - return Future.delayed(const Duration(seconds: 1), () { - _logger.finest('sane_getOptionDescriptor()'); - return SaneOptionDescriptor( + _logger.finest('sane_getOptionDescriptor()'); + return Future.delayed( + const Duration(seconds: 1), + () => SaneOptionDescriptor( index: index, name: 'name', title: 'title', @@ -147,70 +87,105 @@ class SaneDev implements Sane { size: 1, capabilities: [], constraint: null, - ); - }); + ), + ); } @override - Future getParameters(SaneHandle handle) { - return Future.delayed(const Duration(seconds: 1), () { - _logger.finest('sane_getParameters()'); - return SaneParameters( + Future getParameters() { + _logger.finest('sane_getParameters()'); + return Future.delayed( + const Duration(seconds: 1), + () => SaneParameters( format: SaneFrameFormat.gray, lastFrame: true, bytesPerLine: 800, pixelsPerLine: 100, lines: 100, depth: 8, - ); - }); + ), + ); } @override - Future init({ - AuthCallback? authCallback, - }) { - return Future(() { - _logger.finest('sane_init()'); - return 1; - }); + Future> getAllOptionDescriptors() { + _logger.finest('sane_getAllOptionDescriptors()'); + return Future.delayed( + const Duration(seconds: 1), + () => [ + SaneOptionDescriptor( + index: 0, + name: 'name', + title: 'title', + desc: 'desc', + type: SaneOptionValueType.int, + unit: SaneOptionUnit.none, + size: 1, + capabilities: [], + constraint: null, + ), + ], + ); } @override - Future open(String deviceName) { - return Future.delayed(const Duration(seconds: 1), () { - _logger.finest('sane_open()'); - return SaneHandle(deviceName: deviceName); - }); + Future> controlBoolOption( + int index, + SaneAction action, [ + bool? value, + ]) { + _logger.finest('sane_controlBoolOption()'); + return Future.delayed( + const Duration(seconds: 1), + () => SaneOptionResult(result: value ?? true, infos: []), + ); } @override - Future openDevice(SaneDevice device) { - return Future.delayed(const Duration(seconds: 1), () { - _logger.finest('sane_openDevice()'); - return SaneHandle(deviceName: device.name); - }); + Future> controlButtonOption(int index) { + _logger.finest('sane_controlButtonOption()'); + return Future.delayed( + const Duration(seconds: 1), + () => SaneOptionResult(result: null, infos: []), + ); } @override - Future read(SaneHandle handle, int bufferSize) { - return Future.delayed(const Duration(seconds: 1), () { - _logger.finest('sane_read()'); - return Uint8List.fromList([]); - }); + Future> controlFixedOption( + int index, + SaneAction action, [ + double? value, + ]) { + _logger.finest('sane_controlFixedOption()'); + return Future.delayed( + const Duration(seconds: 1), + () => SaneOptionResult(result: value ?? .1, infos: []), + ); } @override - Future setIOMode(SaneHandle handle, SaneIOMode mode) { - return Future.delayed(const Duration(seconds: 1), () { - _logger.finest('sane_setIOMode()'); - }); + Future> controlIntOption( + int index, + SaneAction action, [ + int? value, + ]) { + _logger.finest('sane_controlIntOption()'); + return Future.delayed( + const Duration(seconds: 1), + () => SaneOptionResult(result: value ?? 1, infos: []), + ); } @override - Future start(SaneHandle handle) { - return Future.delayed(const Duration(seconds: 1), () { - _logger.finest('sane_start()'); - }); + Future> controlStringOption( + int index, + SaneAction action, [ + String? value, + ]) { + _logger.finest('sane_controlStringOption()'); + return Future.delayed( + const Duration(seconds: 1), + () => SaneOptionResult(result: value ?? 'value', infos: []), + ); } } diff --git a/packages/sane/lib/src/impl/sane_native.dart b/packages/sane/lib/src/impl/sane_native.dart index 65a674a..39955f9 100644 --- a/packages/sane/lib/src/impl/sane_native.dart +++ b/packages/sane/lib/src/impl/sane_native.dart @@ -1,322 +1,292 @@ -import 'dart:isolate'; +import 'dart:async'; import 'dart:typed_data'; -import 'package:sane/sane.dart'; +import 'package:sane/src/exceptions.dart'; +import 'package:sane/src/impl/sane_sync.dart'; +import 'package:sane/src/isolate.dart'; import 'package:sane/src/isolate_messages/cancel.dart'; import 'package:sane/src/isolate_messages/close.dart'; import 'package:sane/src/isolate_messages/control_button_option.dart'; import 'package:sane/src/isolate_messages/control_option.dart'; -import 'package:sane/src/isolate_messages/exception.dart'; import 'package:sane/src/isolate_messages/exit.dart'; import 'package:sane/src/isolate_messages/get_all_option_descriptors.dart'; import 'package:sane/src/isolate_messages/get_devices.dart'; import 'package:sane/src/isolate_messages/get_option_descriptor.dart'; import 'package:sane/src/isolate_messages/get_parameters.dart'; import 'package:sane/src/isolate_messages/init.dart'; -import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/isolate_messages/open.dart'; import 'package:sane/src/isolate_messages/read.dart'; -import 'package:sane/src/isolate_messages/set_io_mode.dart'; import 'package:sane/src/isolate_messages/start.dart'; +import 'package:sane/src/sane.dart'; +import 'package:sane/src/structures.dart'; + +class NativeSane implements Sane { + /// Instantiates or returns a [NativeSane] instance. + /// + /// If no [backingSane] is provided, a [SyncSane] instance will be used. + /// + /// No multiple [NativeSane] instances with different backing SANEs can exist + /// at the same time. Call [dispose] to dispose the current instance. + factory NativeSane([Sane? backingSane]) { + // This would cause `StackOverflowError`s + if (backingSane is NativeSane) { + throw ArgumentError( + 'NativeSane cannot be created from a NativeSane instance', + 'backingSane', + ); + } -class SaneNative implements Sane { - SaneNative({ - required Sane sane, - }) : _sane = sane; - - final Sane _sane; - - late final Isolate _isolate; - late final SendPort _sendPort; - - Future spawn() async { - final receivePort = ReceivePort(); - _isolate = await Isolate.spawn( - _isolateEntryPoint, - _IsolateEntryPointArgs( - mainSendPort: receivePort.sendPort, - sane: _sane, - ), - ); - _sendPort = await receivePort.first as SendPort; - } + backingSane ??= SyncSane(); + + // Avoid undefined behavior + final instance = _instance; + if (instance != null) { + if (instance.backingSane.runtimeType != backingSane.runtimeType) { + throw StateError( + 'The existing NativeSane instance must be disposed before ' + 'instantiating a new one with a different backing SANE ' + 'implementation.', + ); + } + + // Reuse instance + return instance; + } - void kill() { - _isolate.kill(priority: Isolate.immediate); + // Create instance + return _instance = NativeSane._(backingSane); } - Future _sendMessage( - IsolateMessage message, - ) async { - final replyPort = ReceivePort(); + NativeSane._(this.backingSane); - _sendPort.send( - _IsolateMessageEnvelope( - replyPort: replyPort.sendPort, - message: message, - ), - ); + final Sane backingSane; - final response = await replyPort.first; - replyPort.close(); + static NativeSane? _instance; - if (response is ExceptionResponse) { - Error.throwWithStackTrace( - response.exception, - response.stackTrace, - ); - } + bool _disposed = false; + SaneIsolate? _isolate; - return response as T; + Future _getIsolate() async { + if (_isolate?.exited == true || _disposed) throw SaneDisposedError(); + return _isolate ??= await SaneIsolate.spawn(backingSane); } @override - Future init({ - AuthCallback? authCallback, - }) async { - final response = await _sendMessage(InitMessage()); - return response.versionCode; + Future initialize([AuthCallback? authCallback]) async { + final isolate = await _getIsolate(); + final response = await isolate.sendMessage(InitMessage()); + return response.version; } @override - Future exit() async { - await _sendMessage(ExitMessage()); + Future dispose({bool force = false}) async { + final isolate = _isolate; + + if (force) { + isolate?.kill(); + return; + } + + if (_disposed) return; + + _disposed = true; + _instance = null; + + await isolate?.sendMessage(ExitMessage()); + + _isolate = null; } @override Future> getDevices({ required bool localOnly, }) async { - final response = await _sendMessage( + final isolate = await _getIsolate(); + final response = await isolate.sendMessage( GetDevicesMessage(localOnly: localOnly), ); return response.devices; } +} + +class NativeSaneDevice implements SaneDevice { + NativeSaneDevice({ + required NativeSane sane, + required this.name, + required this.type, + required this.vendor, + required this.model, + }) : _sane = sane; + + final NativeSane _sane; + + bool _closed = false; @override - Future open(String deviceName) async { - final response = await _sendMessage( - OpenMessage(deviceName: deviceName), - ); + final String name; - return response.handle; - } + @override + final String type; @override - Future openDevice(SaneDevice device) { - return open(device.name); - } + final String? vendor; @override - Future close(SaneHandle handle) async { - await _sendMessage( - CloseMessage(saneHandle: handle), - ); - } + final String model; @override - Future getOptionDescriptor( - SaneHandle handle, - int index, - ) async { - final response = await _sendMessage( - GetOptionDescriptorMessage( - saneHandle: handle, - index: index, - ), - ); + Future cancel() async { + if (_closed) return; - return response.optionDescriptor; + final isolate = _sane._isolate; + + if (isolate == null) return; + + final message = CancelMessage(name); + await isolate.sendMessage(message); } @override - Future> getAllOptionDescriptors( - SaneHandle handle, - ) async { - final response = await _sendMessage( - GetAllOptionDescriptorsMessage(saneHandle: handle), - ); + Future close() async { + if (_closed) return; + + _closed = true; + + final isolate = _sane._isolate; - return response.optionDescriptors; + if (isolate == null) return; + + final message = CloseMessage(name); + await isolate.sendMessage(message); } @override - Future> controlBoolOption({ - required SaneHandle handle, - required int index, - required SaneAction action, - bool? value, - }) async { - final response = await _sendMessage( - ControlValueOptionMessage( - saneHandle: handle, - index: index, - action: action, - value: value, - ), - ); + Future read({required int bufferSize}) async { + final isolate = await _sane._getIsolate(); + final message = ReadMessage(bufferSize: bufferSize, deviceName: name); + final response = await isolate.sendMessage(message); - return response.result; + return response.bytes; } @override - Future> controlIntOption({ - required SaneHandle handle, - required int index, - required SaneAction action, - int? value, - }) async { - final response = await _sendMessage( - ControlValueOptionMessage( - saneHandle: handle, - index: index, - action: action, - value: value, - ), - ); - - return response.result; + Future start() async { + final isolate = await _sane._getIsolate(); + final message = StartMessage(name); + await isolate.sendMessage(message); } @override - Future> controlFixedOption({ - required SaneHandle handle, - required int index, - required SaneAction action, - double? value, - }) async { - final response = await _sendMessage( - ControlValueOptionMessage( - saneHandle: handle, - index: index, - action: action, - value: value, - ), - ); + Future getOptionDescriptor(int index) async { + final isolate = await _sane._getIsolate(); + final message = GetOptionDescriptorMessage(deviceName: name, index: index); - return response.result; + final GetOptionDescriptorResponse(:optionDescriptor) = + await isolate.sendMessage(message); + + return optionDescriptor; } @override - Future> controlStringOption({ - required SaneHandle handle, - required int index, - required SaneAction action, - String? value, - }) async { - final response = await _sendMessage( - ControlValueOptionMessage( - saneHandle: handle, - index: index, - action: action, - value: value, - ), - ); + Future> getAllOptionDescriptors() async { + final isolate = await _sane._getIsolate(); + final message = GetAllOptionDescriptorsMessage(name); - return response.result; + final GetAllOptionDescriptorsResponse(:optionDescriptors) = + await isolate.sendMessage(message); + + return optionDescriptors; } @override - Future> controlButtonOption({ - required SaneHandle handle, - required int index, - }) async { - final response = await _sendMessage( - ControlButtonOptionMessage( - saneHandle: handle, - index: index, - ), + Future> controlBoolOption( + int index, + SaneAction action, + bool? value, + ) async { + final isolate = await _sane._getIsolate(); + final message = ControlValueOptionMessage( + deviceName: name, + index: index, + action: action, + value: value, ); + final response = await isolate.sendMessage(message); return response.result; } @override - Future getParameters(SaneHandle handle) async { - final response = await _sendMessage( - GetParametersMessage( - saneHandle: handle, - ), + Future> controlIntOption( + int index, + SaneAction action, + int? value, + ) async { + final isolate = await _sane._getIsolate(); + final message = ControlValueOptionMessage( + deviceName: name, + index: index, + action: action, + value: value, ); + final response = await isolate.sendMessage(message); - return response.parameters; + return response.result; } @override - Future start(SaneHandle handle) async { - await _sendMessage( - StartMessage(saneHandle: handle), + Future> controlFixedOption( + int index, + SaneAction action, + double? value, + ) async { + final isolate = await _sane._getIsolate(); + final message = ControlValueOptionMessage( + deviceName: name, + index: index, + action: action, + value: value, ); + final response = await isolate.sendMessage(message); + + return response.result; } @override - Future read(SaneHandle handle, int bufferSize) async { - final response = await _sendMessage( - ReadMessage( - saneHandle: handle, - bufferSize: bufferSize, - ), + Future> controlStringOption( + int index, + SaneAction action, + String? value, + ) async { + final isolate = await _sane._getIsolate(); + final message = ControlValueOptionMessage( + deviceName: name, + index: index, + action: action, + value: value, ); + final response = await isolate.sendMessage(message); - return response.bytes; + return response.result; } @override - Future cancel(SaneHandle handle) async { - await _sendMessage( - CancelMessage(saneHandle: handle), + Future> controlButtonOption(int index) async { + final isolate = await _sane._getIsolate(); + final message = ControlButtonOptionMessage( + deviceName: name, + index: index, ); + final response = await isolate.sendMessage(message); + + return response.result; } @override - Future setIOMode( - SaneHandle handle, - SaneIOMode ioMode, - ) async { - await _sendMessage( - SetIOModeMessage(saneHandle: handle, ioMode: ioMode), - ); + Future getParameters() async { + final isolate = await _sane._getIsolate(); + final message = GetParametersMessage(name); + final response = await isolate.sendMessage(message); + return response.parameters; } } - -class _IsolateEntryPointArgs { - _IsolateEntryPointArgs({ - required this.mainSendPort, - required this.sane, - }); - - final SendPort mainSendPort; - final Sane sane; -} - -void _isolateEntryPoint(_IsolateEntryPointArgs args) { - final isolateReceivePort = ReceivePort(); - args.mainSendPort.send(isolateReceivePort.sendPort); - - final sane = args.sane; - isolateReceivePort.cast<_IsolateMessageEnvelope>().listen((envelope) async { - late IsolateResponse response; - - try { - response = await envelope.message.handle(sane); - } on SaneException catch (exception, stackTrace) { - response = ExceptionResponse( - exception: exception, - stackTrace: stackTrace, - ); - } - - envelope.replyPort.send(response); - }); -} - -class _IsolateMessageEnvelope { - _IsolateMessageEnvelope({ - required this.replyPort, - required this.message, - }); - - final SendPort replyPort; - final IsolateMessage message; -} diff --git a/packages/sane/lib/src/impl/sane_sync.dart b/packages/sane/lib/src/impl/sane_sync.dart index d72532c..1902fe3 100644 --- a/packages/sane/lib/src/impl/sane_sync.dart +++ b/packages/sane/lib/src/impl/sane_sync.dart @@ -1,36 +1,27 @@ import 'dart:async'; import 'dart:ffi' as ffi; +import 'dart:ffi'; import 'dart:typed_data'; import 'package:ffi/ffi.dart' as ffi; +import 'package:sane/sane.dart'; import 'package:sane/src/bindings.g.dart'; import 'package:sane/src/dylib.dart'; -import 'package:sane/src/exceptions.dart'; import 'package:sane/src/extensions.dart'; import 'package:sane/src/logger.dart'; -import 'package:sane/src/structures.dart'; import 'package:sane/src/type_conversion.dart'; -import 'package:sane/src/utils.dart'; -typedef AuthCallback = SaneCredentials Function(String resourceName); +class SyncSane implements Sane { + factory SyncSane() => _instance ??= SyncSane._(); -class Sane { - factory Sane() => _instance ??= Sane._(); + SyncSane._(); - Sane._(); + static SyncSane? _instance; + bool _disposed = false; - static Sane? _instance; - bool _exited = false; - final Map _nativeHandles = {}; - - SANE_Handle _getNativeHandle(SaneHandle handle) => _nativeHandles[handle]!; - - Future init({ - AuthCallback? authCallback, - }) { - _checkIfExited(); - - final completer = Completer(); + @override + SaneVersion initialize([AuthCallback? authCallback]) { + _checkIfDisposed(); void authCallbackAdapter( SANE_String_Const resource, @@ -50,39 +41,40 @@ class Sane { } } - Future(() { - final versionCodePointer = ffi.calloc(); - final nativeAuthCallback = authCallback != null - ? ffi.NativeCallable.isolateLocal( - authCallbackAdapter, - ).nativeFunction - : ffi.nullptr; + final versionCodePointer = ffi.calloc(); + final nativeAuthCallback = authCallback != null + ? ffi.NativeCallable.isolateLocal( + authCallbackAdapter, + ).nativeFunction + : ffi.nullptr; + try { final status = dylib.sane_init(versionCodePointer, nativeAuthCallback); + logger.finest('sane_init() -> ${status.name}'); status.check(); final versionCode = versionCodePointer.value; - logger.finest( - 'SANE version: ${SaneUtils.version(versionCodePointer.value)}', - ); - ffi.calloc.free(versionCodePointer); - ffi.calloc.free(nativeAuthCallback); + final version = SaneVersion.fromCode(versionCode); - completer.complete(versionCode); - }); + logger.finest('SANE version: $version'); - return completer.future; + return version; + } finally { + ffi.calloc.free(versionCodePointer); + ffi.calloc.free(nativeAuthCallback); + } } - Future exit() { - if (_exited) return Future.value(); + @override + Future dispose() { + if (_disposed) return Future.value(); final completer = Completer(); Future(() { - _exited = true; + _disposed = true; dylib.sane_exit(); logger.finest('sane_exit()'); @@ -95,452 +87,384 @@ class Sane { return completer.future; } - Future> getDevices({ - required bool localOnly, - }) { - _checkIfExited(); + @override + List getDevices({required bool localOnly}) { + _checkIfDisposed(); - final completer = Completer>(); + final deviceListPointer = + ffi.calloc>>(); - Future(() { - final deviceListPointer = - ffi.calloc>>(); + try { final status = dylib.sane_get_devices( deviceListPointer, - saneBoolFromDartBool(localOnly), + localOnly.asSaneBool, ); logger.finest('sane_get_devices() -> ${status.name}'); status.check(); - final devices = []; + final devices = []; + for (var i = 0; deviceListPointer.value[i] != ffi.nullptr; i++) { - final nativeDevice = deviceListPointer.value[i].ref; - devices.add(saneDeviceFromNative(nativeDevice)); + final device = deviceListPointer.value[i].ref; + devices.add(SyncSaneDevice(device)); } + return List.unmodifiable(devices); + } finally { ffi.calloc.free(deviceListPointer); + } + } - completer.complete(devices); - }); + @pragma('vm:prefer-inline') + void _checkIfDisposed() { + if (_disposed) throw SaneDisposedError(); + } +} - return completer.future; +class SyncSaneDevice implements SaneDevice, ffi.Finalizable { + factory SyncSaneDevice(SANE_Device device) { + final vendor = device.vendor.toDartString(); + return SyncSaneDevice._( + name: device.name.toDartString(), + vendor: vendor == 'Noname' ? null : vendor, + type: device.type.toDartString(), + model: device.model.toDartString(), + ); } - Future open(String deviceName) { - _checkIfExited(); + SyncSaneDevice._({ + required this.name, + required this.vendor, + required this.model, + required this.type, + }); - final completer = Completer(); + static final _finalizer = ffi.NativeFinalizer(dylib.addresses.sane_close); - Future(() { - final nativeHandlePointer = ffi.calloc(); - final deviceNamePointer = saneStringFromDartString(deviceName); - final status = dylib.sane_open(deviceNamePointer, nativeHandlePointer); - logger.finest('sane_open() -> ${status.name}'); + SANE_Handle? _handle; - status.check(); + bool _closed = false; - final handle = SaneHandle(deviceName: deviceName); - _nativeHandles.addAll({ - handle: nativeHandlePointer.value, - }); + @override + final String name; - ffi.calloc.free(nativeHandlePointer); - ffi.calloc.free(deviceNamePointer); + @override + final String type; - completer.complete(handle); - }); + @override + final String? vendor; - return completer.future; + @override + final String model; + + @override + void cancel() { + _checkIfDisposed(); + + final handle = _handle; + + if (handle == null) return; + + dylib.sane_cancel(handle); + } + + SANE_Handle _open() { + final namePointer = name.toSaneString(); + final handlePointer = ffi.calloc.allocate( + ffi.sizeOf(), + ); + + try { + dylib.sane_open(namePointer, handlePointer).check(); + final handle = handlePointer.value; + _finalizer.attach(this, handle); + return handle; + } finally { + ffi.calloc.free(namePointer); + ffi.calloc.free(handlePointer); + } } - Future openDevice(SaneDevice device) { - _checkIfExited(); + @override + void close() { + if (_closed) return; + + _closed = true; + + if (_handle == null) return; - return open(device.name); + _finalizer.detach(this); + dylib.sane_close(_handle!); } - Future close(SaneHandle handle) { - _checkIfExited(); + @override + Uint8List read({required int bufferSize}) { + _checkIfDisposed(); - final completer = Completer(); + final handle = _handle ??= _open(); - Future(() { - dylib.sane_close(_getNativeHandle(handle)); - _nativeHandles.remove(handle); - logger.finest('sane_close()'); + final lengthPointer = ffi.calloc(); + final bufferPointer = ffi.calloc(bufferSize); - completer.complete(); - }); + try { + dylib.sane_read(handle, bufferPointer, bufferSize, lengthPointer).check(); - return completer.future; + logger.finest('sane_read()'); + + final length = lengthPointer.value; + final buffer = bufferPointer.cast().asTypedList(length); + + return buffer; + } finally { + ffi.calloc.free(lengthPointer); + ffi.calloc.free(bufferPointer); + } } - Future getOptionDescriptor( - SaneHandle handle, - int index, - ) { - _checkIfExited(); + @override + void start() { + _checkIfDisposed(); - final completer = Completer(); + final handle = _handle ??= _open(); - Future(() { - final optionDescriptorPointer = - dylib.sane_get_option_descriptor(_getNativeHandle(handle), index); - final optionDescriptor = saneOptionDescriptorFromNative( + dylib.sane_start(handle).check(); + } + + @pragma('vm:prefer-inline') + void _checkIfDisposed() { + if (_closed) throw SaneDisposedError(); + } + + @override + SaneOptionDescriptor getOptionDescriptor(int index) { + _checkIfDisposed(); + + final handle = _handle ??= _open(); + + final optionDescriptorPointer = + dylib.sane_get_option_descriptor(handle, index); + + try { + return saneOptionDescriptorFromNative( optionDescriptorPointer.ref, index, ); - + } finally { ffi.calloc.free(optionDescriptorPointer); - - completer.complete(optionDescriptor); - }); - - return completer.future; + } } - Future> getAllOptionDescriptors( - SaneHandle handle, - ) { - _checkIfExited(); + @override + List getAllOptionDescriptors() { + _checkIfDisposed(); - final completer = Completer>(); + final handle = _handle ??= _open(); - Future(() { - final optionDescriptors = []; + final optionDescriptors = []; - for (var i = 0; true; i++) { - final descriptorPointer = - dylib.sane_get_option_descriptor(_getNativeHandle(handle), i); + for (var i = 0; true; i++) { + final descriptorPointer = dylib.sane_get_option_descriptor(handle, i); + try { if (descriptorPointer == ffi.nullptr) break; optionDescriptors.add( saneOptionDescriptorFromNative(descriptorPointer.ref, i), ); + } finally { + ffi.calloc.free(descriptorPointer); } + } - completer.complete(optionDescriptors); - }); - - return completer.future; + return optionDescriptors; } - Future> _controlOption({ - required SaneHandle handle, + SaneOptionResult _controlOption({ required int index, required SaneAction action, T? value, }) { - _checkIfExited(); + _checkIfDisposed(); - final completer = Completer>(); + final handle = _handle ??= _open(); - Future(() { - final optionDescriptor = saneOptionDescriptorFromNative( - dylib.sane_get_option_descriptor(_getNativeHandle(handle), index).ref, - index, - ); - final optionType = optionDescriptor.type; - final optionSize = optionDescriptor.size; + final optionDescriptor = saneOptionDescriptorFromNative( + dylib.sane_get_option_descriptor(handle, index).ref, + index, + ); + final optionType = optionDescriptor.type; + final optionSize = optionDescriptor.size; + + final infoPointer = ffi.calloc(); + + ffi.Pointer allocateOptionValue() { + return switch (optionType) { + SaneOptionValueType.bool => ffi.calloc(optionSize), + SaneOptionValueType.int => ffi.calloc(optionSize), + SaneOptionValueType.fixed => ffi.calloc(optionSize), + SaneOptionValueType.string => ffi.calloc(optionSize), + SaneOptionValueType.button => ffi.nullptr, + SaneOptionValueType.group => throw const SaneInvalidDataException(), + }; + } - final infoPointer = ffi.calloc(); + final valuePointer = allocateOptionValue(); - late final ffi.Pointer valuePointer; + if (action == SaneAction.setValue) { switch (optionType) { - case SaneOptionValueType.bool: - valuePointer = ffi.calloc(optionSize); + case SaneOptionValueType.bool when value is bool: + (valuePointer as ffi.Pointer).value = value.asSaneBool; + break; - case SaneOptionValueType.int: - valuePointer = ffi.calloc(optionSize); + case SaneOptionValueType.int when value is int: + (valuePointer as ffi.Pointer).value = value; + break; - case SaneOptionValueType.fixed: - valuePointer = ffi.calloc(optionSize); + case SaneOptionValueType.fixed when value is double: + (valuePointer as ffi.Pointer).value = + doubleToSaneFixed(value); + break; - case SaneOptionValueType.string: - valuePointer = ffi.calloc(optionSize); + case SaneOptionValueType.string when value is String: + (valuePointer as ffi.Pointer).value = + value.toSaneString(); + break; case SaneOptionValueType.button: - valuePointer = ffi.nullptr; + break; case SaneOptionValueType.group: + default: throw const SaneInvalidDataException(); } + } - if (action == SaneAction.setValue) { - switch (optionType) { - case SaneOptionValueType.bool: - if (value is! bool) continue invalid; - (valuePointer as ffi.Pointer).value = - saneBoolFromDartBool(value); - break; - - case SaneOptionValueType.int: - if (value is! int) continue invalid; - (valuePointer as ffi.Pointer).value = value; - break; - - case SaneOptionValueType.fixed: - if (value is! double) continue invalid; - (valuePointer as ffi.Pointer).value = - doubleToSaneFixed(value); - break; - - case SaneOptionValueType.string: - if (value is! String) continue invalid; - (valuePointer as ffi.Pointer).value = - saneStringFromDartString(value).value; - break; - - case SaneOptionValueType.button: - break; - - case SaneOptionValueType.group: - continue invalid; - - invalid: - default: - throw const SaneInvalidDataException(); - } - } - - final status = dylib.sane_control_option( - _getNativeHandle(handle), - index, - nativeSaneActionFromDart(action), - valuePointer.cast(), - infoPointer, - ); - logger.finest( - 'sane_control_option($index, $action, $value) -> ${status.name}', - ); + final status = dylib.sane_control_option( + handle, + index, + nativeSaneActionFromDart(action), + valuePointer.cast(), + infoPointer, + ); + logger.finest( + 'sane_control_option($index, $action, $value) -> ${status.name}', + ); - status.check(); + status.check(); - final infos = saneOptionInfoFromNative(infoPointer.value); - late final dynamic result; - switch (optionType) { - case SaneOptionValueType.bool: - result = dartBoolFromSaneBool( - (valuePointer as ffi.Pointer).value, - ); + final infos = saneOptionInfoFromNative(infoPointer.value); + late final dynamic result; + switch (optionType) { + case SaneOptionValueType.bool: + result = dartBoolFromSaneBool( + (valuePointer as ffi.Pointer).value, + ); - case SaneOptionValueType.int: - result = (valuePointer as ffi.Pointer).value; + case SaneOptionValueType.int: + result = (valuePointer as ffi.Pointer).value; - case SaneOptionValueType.fixed: - result = - saneFixedToDouble((valuePointer as ffi.Pointer).value); + case SaneOptionValueType.fixed: + result = + saneFixedToDouble((valuePointer as ffi.Pointer).value); - case SaneOptionValueType.string: - result = dartStringFromSaneString( - valuePointer as ffi.Pointer, - ) ?? - ''; + case SaneOptionValueType.string: + result = dartStringFromSaneString( + valuePointer as ffi.Pointer, + ) ?? + ''; - case SaneOptionValueType.button: - result = null; + case SaneOptionValueType.button: + result = null; - default: - throw const SaneInvalidDataException(); - } - - ffi.calloc.free(valuePointer); - ffi.calloc.free(infoPointer); + default: + throw const SaneInvalidDataException(); + } - completer.complete( - SaneOptionResult( - result: result, - infos: infos, - ), - ); - }); + ffi.calloc.free(valuePointer); + ffi.calloc.free(infoPointer); - return completer.future; + return SaneOptionResult( + result: result, + infos: infos, + ); } - Future> controlBoolOption({ - required SaneHandle handle, - required int index, - required SaneAction action, - bool? value, - }) { + @override + SaneOptionResult controlBoolOption( + int index, + SaneAction action, + bool? value,) { return _controlOption( - handle: handle, index: index, action: action, value: value, ); } - Future> controlIntOption({ - required SaneHandle handle, - required int index, - required SaneAction action, - int? value, - }) { + @override + SaneOptionResult controlIntOption( + int index, + SaneAction action, + int? value,) { return _controlOption( - handle: handle, index: index, action: action, value: value, ); } - Future> controlFixedOption({ - required SaneHandle handle, - required int index, - required SaneAction action, - double? value, - }) { + @override + SaneOptionResult controlFixedOption( + int index, + SaneAction action, + double? value,) { return _controlOption( - handle: handle, index: index, action: action, value: value, ); } - Future> controlStringOption({ - required SaneHandle handle, - required int index, - required SaneAction action, - String? value, - }) { + @override + SaneOptionResult controlStringOption( + int index, + SaneAction action, + String? value,) { return _controlOption( - handle: handle, index: index, action: action, value: value, ); } - Future> controlButtonOption({ - required SaneHandle handle, - required int index, - }) { + @override + SaneOptionResult controlButtonOption(int index) { return _controlOption( - handle: handle, index: index, action: SaneAction.setValue, value: null, ); } - Future getParameters(SaneHandle handle) { - _checkIfExited(); + @override + SaneParameters getParameters() { + _checkIfDisposed(); - final completer = Completer(); + final handle = _handle ??= _open(); + final nativeParametersPointer = ffi.calloc(); - Future(() { - final nativeParametersPointer = ffi.calloc(); + try { final status = dylib.sane_get_parameters( - _getNativeHandle(handle), + handle, nativeParametersPointer, ); logger.finest('sane_get_parameters() -> ${status.name}'); status.check(); - final parameters = saneParametersFromNative(nativeParametersPointer.ref); - + return saneParametersFromNative(nativeParametersPointer.ref); + } finally { ffi.calloc.free(nativeParametersPointer); - - completer.complete(parameters); - }); - - return completer.future; - } - - Future start(SaneHandle handle) { - _checkIfExited(); - - final completer = Completer(); - - Future(() { - final status = dylib.sane_start(_getNativeHandle(handle)); - logger.finest('sane_start() -> ${status.name}'); - - status.check(); - - completer.complete(); - }); - - return completer.future; - } - - Future read(SaneHandle handle, int bufferSize) { - _checkIfExited(); - - final completer = Completer(); - - Future(() { - final bytesReadPointer = ffi.calloc(); - final bufferPointer = ffi.calloc(bufferSize); - - final status = dylib.sane_read( - _getNativeHandle(handle), - bufferPointer, - bufferSize, - bytesReadPointer, - ); - logger.finest('sane_read() -> ${status.name}'); - - status.check(); - - final bytes = Uint8List.fromList( - List.generate( - bytesReadPointer.value, - (i) => (bufferPointer + i).value, - ), - ); - - ffi.calloc.free(bytesReadPointer); - ffi.calloc.free(bufferPointer); - - completer.complete(bytes); - }); - - return completer.future; - } - - Future cancel(SaneHandle handle) { - _checkIfExited(); - - final completer = Completer(); - - Future(() { - dylib.sane_cancel(_getNativeHandle(handle)); - logger.finest('sane_cancel()'); - - completer.complete(); - }); - - return completer.future; - } - - Future setIOMode(SaneHandle handle, SaneIOMode mode) { - _checkIfExited(); - - final completer = Completer(); - - Future(() { - final status = dylib.sane_set_io_mode( - _getNativeHandle(handle), - saneBoolFromIOMode(mode), - ); - logger.finest('sane_set_io_mode() -> ${status.name}'); - - status.check(); - - completer.complete(); - }); - - return completer.future; - } - - @pragma('vm:prefer-inline') - void _checkIfExited() { - if (_exited) throw SaneDisposedError(); + } } } diff --git a/packages/sane/lib/src/isolate.dart b/packages/sane/lib/src/isolate.dart new file mode 100644 index 0000000..65117a2 --- /dev/null +++ b/packages/sane/lib/src/isolate.dart @@ -0,0 +1,117 @@ +import 'dart:async'; +import 'dart:isolate'; + +import 'package:sane/src/exceptions.dart'; +import 'package:sane/src/isolate_messages/exception.dart'; +import 'package:sane/src/isolate_messages/interface.dart'; +import 'package:sane/src/sane.dart'; + +class SaneIsolate { + SaneIsolate._( + this._isolate, + this._sendPort, + this._exitReceivePort, + ) : _exited = false { + _exitReceivePort.listen((message) { + assert(message == null); + _exited = true; + }); + } + + final Isolate _isolate; + final SendPort _sendPort; + final ReceivePort _exitReceivePort; + + bool _exited; + + bool get exited => _exited; + + static Future spawn(Sane sane) async { + final receivePort = ReceivePort(); + final exitReceivePort = ReceivePort(); + + final isolate = await Isolate.spawn( + _entryPoint, + (receivePort.sendPort, sane), + onExit: exitReceivePort.sendPort, + ); + + final sendPort = await receivePort.first as SendPort; + return SaneIsolate._(isolate, sendPort, exitReceivePort); + } + + void kill() { + _isolate.kill(priority: Isolate.immediate); + } + + Future sendMessage( + IsolateMessage message, + ) async { + final replyPort = ReceivePort(); + + _sendPort.send( + _IsolateMessageEnvelope( + replyPort: replyPort.sendPort, + message: message, + ), + ); + + final response = await replyPort.first; + replyPort.close(); + + if (response is ExceptionResponse) { + Error.throwWithStackTrace( + response.exception, + response.stackTrace, + ); + } + + return response as T; + } +} + +typedef _EntryPointArgs = (SendPort sendPort, Sane sane); + +void _entryPoint(_EntryPointArgs args) { + final (sendPort, sane) = args; + + final receivePort = ReceivePort(); + sendPort.send(receivePort.sendPort); + + receivePort.cast<_IsolateMessageEnvelope>().listen((envelope) async { + final _IsolateMessageEnvelope(:message, :replyPort) = envelope; + + IsolateResponse response; + + try { + response = await message.handle(sane); + } on SaneException catch (exception, stackTrace) { + response = ExceptionResponse( + exception: exception, + stackTrace: stackTrace, + ); + } + + replyPort.send(response); + }); +} + +class _IsolateMessageEnvelope { + _IsolateMessageEnvelope({ + required this.replyPort, + required this.message, + }); + + final SendPort replyPort; + final IsolateMessage message; +} + +late Map _devices; + +SaneDevice getDevice(String name) => _devices[name]!; + +void setDevices(Iterable devices) { + _devices = { + for (final device in devices) device.name: device, + }; +} diff --git a/packages/sane/lib/src/isolate_messages/cancel.dart b/packages/sane/lib/src/isolate_messages/cancel.dart index a4bb836..5068008 100644 --- a/packages/sane/lib/src/isolate_messages/cancel.dart +++ b/packages/sane/lib/src/isolate_messages/cancel.dart @@ -1,15 +1,15 @@ -import 'package:sane/src/impl/sane_sync.dart'; +import 'package:sane/src/isolate.dart'; import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/structures.dart'; +import 'package:sane/src/sane.dart'; class CancelMessage implements IsolateMessage { - CancelMessage({required this.saneHandle}); + CancelMessage(this.deviceName); - final SaneHandle saneHandle; + final String deviceName; @override Future handle(Sane sane) async { - await sane.cancel(saneHandle); + await getDevice(deviceName).cancel(); return CancelResponse(); } } diff --git a/packages/sane/lib/src/isolate_messages/close.dart b/packages/sane/lib/src/isolate_messages/close.dart index 028f854..5109f5c 100644 --- a/packages/sane/lib/src/isolate_messages/close.dart +++ b/packages/sane/lib/src/isolate_messages/close.dart @@ -1,14 +1,15 @@ import 'package:sane/sane.dart'; +import 'package:sane/src/isolate.dart'; import 'package:sane/src/isolate_messages/interface.dart'; class CloseMessage implements IsolateMessage { - CloseMessage({required this.saneHandle}); + const CloseMessage(this.deviceName); - final SaneHandle saneHandle; + final String deviceName; @override Future handle(Sane sane) async { - await sane.close(saneHandle); + await getDevice(deviceName).close(); return CloseResponse(); } } diff --git a/packages/sane/lib/src/isolate_messages/control_button_option.dart b/packages/sane/lib/src/isolate_messages/control_button_option.dart index 2f6c8ad..7cee0cd 100644 --- a/packages/sane/lib/src/isolate_messages/control_button_option.dart +++ b/packages/sane/lib/src/isolate_messages/control_button_option.dart @@ -1,30 +1,28 @@ -import 'package:sane/src/impl/sane_sync.dart'; +import 'package:sane/src/isolate.dart'; import 'package:sane/src/isolate_messages/interface.dart'; +import 'package:sane/src/sane.dart'; import 'package:sane/src/structures.dart'; class ControlButtonOptionMessage implements IsolateMessage { - ControlButtonOptionMessage({ - required this.saneHandle, + const ControlButtonOptionMessage({ + required this.deviceName, required this.index, }); - final SaneHandle saneHandle; + final String deviceName; final int index; @override Future handle(Sane sane) async { - return ControlButtonOptionResponse( - result: await sane.controlButtonOption( - handle: saneHandle, - index: index, - ), - ); + final device = getDevice(deviceName); + final result = await device.controlButtonOption(index); + return ControlButtonOptionResponse(result); } } class ControlButtonOptionResponse implements IsolateResponse { - ControlButtonOptionResponse({required this.result}); + ControlButtonOptionResponse(this.result); final SaneOptionResult result; } diff --git a/packages/sane/lib/src/isolate_messages/control_option.dart b/packages/sane/lib/src/isolate_messages/control_option.dart index d886e36..1804ff7 100644 --- a/packages/sane/lib/src/isolate_messages/control_option.dart +++ b/packages/sane/lib/src/isolate_messages/control_option.dart @@ -1,60 +1,47 @@ -import 'package:sane/src/impl/sane_sync.dart'; +import 'package:sane/src/isolate.dart'; import 'package:sane/src/isolate_messages/interface.dart'; +import 'package:sane/src/sane.dart'; import 'package:sane/src/structures.dart'; class ControlValueOptionMessage implements IsolateMessage> { - ControlValueOptionMessage({ - required this.saneHandle, + const ControlValueOptionMessage({ + required this.deviceName, required this.index, required this.action, this.value, }); - final SaneHandle saneHandle; + final String deviceName; final int index; final SaneAction action; final T? value; @override Future> handle(Sane sane) async { + final device = getDevice(deviceName); + switch (value) { case final bool value: - return ControlValueOptionResponse( - result: await sane.controlBoolOption( - handle: saneHandle, - index: index, - action: action, - value: value, - ), - ) as ControlValueOptionResponse; + final result = await device.controlBoolOption(index, action, value); + return ControlValueOptionResponse(result) + as ControlValueOptionResponse; + case final int value: - return ControlValueOptionResponse( - result: await sane.controlIntOption( - handle: saneHandle, - index: index, - action: action, - value: value, - ), - ) as ControlValueOptionResponse; + final result = await device.controlIntOption(index, action, value); + return ControlValueOptionResponse(result) + as ControlValueOptionResponse; + case final double value: - return ControlValueOptionResponse( - result: await sane.controlFixedOption( - handle: saneHandle, - index: index, - action: action, - value: value, - ), - ) as ControlValueOptionResponse; + final result = await device.controlFixedOption(index, action, value); + return ControlValueOptionResponse(result) + as ControlValueOptionResponse; + case final String value: - return ControlValueOptionResponse( - result: await sane.controlStringOption( - handle: saneHandle, - index: index, - action: action, - value: value, - ), - ) as ControlValueOptionResponse; + final result = await device.controlStringOption(index, action, value); + return ControlValueOptionResponse(result) + as ControlValueOptionResponse; + default: throw Exception('Invalid value type.'); } @@ -62,7 +49,7 @@ class ControlValueOptionMessage } class ControlValueOptionResponse implements IsolateResponse { - ControlValueOptionResponse({required this.result}); + ControlValueOptionResponse(this.result); final SaneOptionResult result; } diff --git a/packages/sane/lib/src/isolate_messages/exit.dart b/packages/sane/lib/src/isolate_messages/exit.dart index cf2d7f7..ea793c2 100644 --- a/packages/sane/lib/src/isolate_messages/exit.dart +++ b/packages/sane/lib/src/isolate_messages/exit.dart @@ -1,10 +1,10 @@ -import 'package:sane/src/impl/sane_sync.dart'; import 'package:sane/src/isolate_messages/interface.dart'; +import 'package:sane/src/sane.dart'; class ExitMessage implements IsolateMessage { @override Future handle(Sane sane) async { - await sane.exit(); + sane.dispose(); return ExitResponse(); } } diff --git a/packages/sane/lib/src/isolate_messages/get_all_option_descriptors.dart b/packages/sane/lib/src/isolate_messages/get_all_option_descriptors.dart index e1c2c8e..c50d8e8 100644 --- a/packages/sane/lib/src/isolate_messages/get_all_option_descriptors.dart +++ b/packages/sane/lib/src/isolate_messages/get_all_option_descriptors.dart @@ -1,23 +1,24 @@ -import 'package:sane/src/impl/sane_sync.dart'; +import 'package:sane/src/isolate.dart'; import 'package:sane/src/isolate_messages/interface.dart'; +import 'package:sane/src/sane.dart'; import 'package:sane/src/structures.dart'; class GetAllOptionDescriptorsMessage implements IsolateMessage { - GetAllOptionDescriptorsMessage({required this.saneHandle}); + GetAllOptionDescriptorsMessage(this.deviceName); - final SaneHandle saneHandle; + final String deviceName; @override Future handle(Sane sane) async { - return GetAllOptionDescriptorsResponse( - optionDescriptors: await sane.getAllOptionDescriptors(saneHandle), - ); + final device = getDevice(deviceName); + final optionDescriptors = await device.getAllOptionDescriptors(); + return GetAllOptionDescriptorsResponse(optionDescriptors); } } class GetAllOptionDescriptorsResponse implements IsolateResponse { - GetAllOptionDescriptorsResponse({required this.optionDescriptors}); + GetAllOptionDescriptorsResponse(this.optionDescriptors); final List optionDescriptors; } diff --git a/packages/sane/lib/src/isolate_messages/get_devices.dart b/packages/sane/lib/src/isolate_messages/get_devices.dart index c48d7d7..cb12eda 100644 --- a/packages/sane/lib/src/isolate_messages/get_devices.dart +++ b/packages/sane/lib/src/isolate_messages/get_devices.dart @@ -1,6 +1,5 @@ -import 'package:sane/src/impl/sane_sync.dart'; import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/structures.dart'; +import 'package:sane/src/sane.dart'; class GetDevicesMessage implements IsolateMessage { GetDevicesMessage({required this.localOnly}); diff --git a/packages/sane/lib/src/isolate_messages/get_option_descriptor.dart b/packages/sane/lib/src/isolate_messages/get_option_descriptor.dart index 8d6347d..ba84d53 100644 --- a/packages/sane/lib/src/isolate_messages/get_option_descriptor.dart +++ b/packages/sane/lib/src/isolate_messages/get_option_descriptor.dart @@ -1,22 +1,23 @@ -import 'package:sane/src/impl/sane_sync.dart'; +import 'package:sane/src/isolate.dart'; import 'package:sane/src/isolate_messages/interface.dart'; +import 'package:sane/src/sane.dart'; import 'package:sane/src/structures.dart'; class GetOptionDescriptorMessage implements IsolateMessage { GetOptionDescriptorMessage({ - required this.saneHandle, + required this.deviceName, required this.index, }); - final SaneHandle saneHandle; + final String deviceName; final int index; @override Future handle(Sane sane) async { - return GetOptionDescriptorResponse( - optionDescriptor: await sane.getOptionDescriptor(saneHandle, index), - ); + final device = getDevice(deviceName); + final optionDescriptor = await device.getOptionDescriptor(index); + return GetOptionDescriptorResponse(optionDescriptor: optionDescriptor); } } diff --git a/packages/sane/lib/src/isolate_messages/get_parameters.dart b/packages/sane/lib/src/isolate_messages/get_parameters.dart index 009a81a..eedc92e 100644 --- a/packages/sane/lib/src/isolate_messages/get_parameters.dart +++ b/packages/sane/lib/src/isolate_messages/get_parameters.dart @@ -1,22 +1,23 @@ -import 'package:sane/src/impl/sane_sync.dart'; +import 'package:sane/src/isolate.dart'; import 'package:sane/src/isolate_messages/interface.dart'; +import 'package:sane/src/sane.dart'; import 'package:sane/src/structures.dart'; class GetParametersMessage implements IsolateMessage { - GetParametersMessage({required this.saneHandle}); + const GetParametersMessage(this.deviceName); - final SaneHandle saneHandle; + final String deviceName; @override Future handle(Sane sane) async { - return GetParametersResponse( - parameters: await sane.getParameters(saneHandle), - ); + final device = getDevice(deviceName); + final parameters = await device.getParameters(); + return GetParametersResponse(parameters); } } class GetParametersResponse implements IsolateResponse { - GetParametersResponse({required this.parameters}); + GetParametersResponse(this.parameters); final SaneParameters parameters; } diff --git a/packages/sane/lib/src/isolate_messages/init.dart b/packages/sane/lib/src/isolate_messages/init.dart index a480352..531d3c0 100644 --- a/packages/sane/lib/src/isolate_messages/init.dart +++ b/packages/sane/lib/src/isolate_messages/init.dart @@ -1,17 +1,16 @@ -import 'package:sane/src/impl/sane_sync.dart'; import 'package:sane/src/isolate_messages/interface.dart'; +import 'package:sane/src/sane.dart'; class InitMessage implements IsolateMessage { @override Future handle(Sane sane) async { - return InitResponse( - versionCode: await sane.init(), - ); + final version = await sane.initialize(); + return InitResponse(version); } } class InitResponse implements IsolateResponse { - InitResponse({required this.versionCode}); + InitResponse(this.version); - final int versionCode; + final SaneVersion version; } diff --git a/packages/sane/lib/src/isolate_messages/interface.dart b/packages/sane/lib/src/isolate_messages/interface.dart index 1e09181..41fef01 100644 --- a/packages/sane/lib/src/isolate_messages/interface.dart +++ b/packages/sane/lib/src/isolate_messages/interface.dart @@ -1,6 +1,8 @@ -import 'package:sane/src/impl/sane_sync.dart'; +import 'package:sane/sane.dart'; abstract interface class IsolateMessage { + const IsolateMessage(); + Future handle(Sane sane); } diff --git a/packages/sane/lib/src/isolate_messages/open.dart b/packages/sane/lib/src/isolate_messages/open.dart deleted file mode 100644 index 81f25e9..0000000 --- a/packages/sane/lib/src/isolate_messages/open.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:sane/src/impl/sane_sync.dart'; -import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/structures.dart'; - -class OpenMessage implements IsolateMessage { - OpenMessage({required this.deviceName}); - - final String deviceName; - - @override - Future handle(Sane sane) async { - return OpenResponse( - handle: await sane.open(deviceName), - ); - } -} - -class OpenResponse implements IsolateResponse { - OpenResponse({required this.handle}); - - final SaneHandle handle; -} diff --git a/packages/sane/lib/src/isolate_messages/read.dart b/packages/sane/lib/src/isolate_messages/read.dart index cef282d..c5a0786 100644 --- a/packages/sane/lib/src/isolate_messages/read.dart +++ b/packages/sane/lib/src/isolate_messages/read.dart @@ -1,30 +1,28 @@ import 'dart:typed_data'; -import 'package:sane/src/impl/sane_sync.dart'; +import 'package:sane/src/isolate.dart'; import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/structures.dart'; +import 'package:sane/src/sane.dart'; class ReadMessage implements IsolateMessage { - ReadMessage({ - required this.saneHandle, + const ReadMessage({ + required this.deviceName, required this.bufferSize, }); - final SaneHandle saneHandle; + final String deviceName; final int bufferSize; @override Future handle(Sane sane) async { - return ReadResponse( - bytes: await sane.read(saneHandle, bufferSize), - ); + final device = getDevice(deviceName); + final bytes = await device.read(bufferSize: bufferSize); + return ReadResponse(bytes); } } class ReadResponse implements IsolateResponse { - ReadResponse({ - required this.bytes, - }); + ReadResponse(this.bytes); final Uint8List bytes; } diff --git a/packages/sane/lib/src/isolate_messages/set_io_mode.dart b/packages/sane/lib/src/isolate_messages/set_io_mode.dart deleted file mode 100644 index 802caaa..0000000 --- a/packages/sane/lib/src/isolate_messages/set_io_mode.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:sane/src/impl/sane_sync.dart'; -import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/structures.dart'; - -class SetIOModeMessage implements IsolateMessage { - SetIOModeMessage({ - required this.saneHandle, - required this.ioMode, - }); - - final SaneHandle saneHandle; - final SaneIOMode ioMode; - - @override - Future handle(Sane sane) async { - await sane.setIOMode(saneHandle, ioMode); - return SetIOModeResponse(); - } -} - -class SetIOModeResponse implements IsolateResponse {} diff --git a/packages/sane/lib/src/isolate_messages/start.dart b/packages/sane/lib/src/isolate_messages/start.dart index 1212705..8efbfae 100644 --- a/packages/sane/lib/src/isolate_messages/start.dart +++ b/packages/sane/lib/src/isolate_messages/start.dart @@ -1,15 +1,15 @@ -import 'package:sane/src/impl/sane_sync.dart'; +import 'package:sane/src/isolate.dart'; import 'package:sane/src/isolate_messages/interface.dart'; -import 'package:sane/src/structures.dart'; +import 'package:sane/src/sane.dart'; class StartMessage implements IsolateMessage { - StartMessage({required this.saneHandle}); + const StartMessage(this.deviceName); - final SaneHandle saneHandle; + final String deviceName; @override Future handle(Sane sane) async { - await sane.start(saneHandle); + getDevice(deviceName).start(); return StartResponse(); } } diff --git a/packages/sane/lib/src/logger.dart b/packages/sane/lib/src/logger.dart index 7117d01..1a2ca27 100644 --- a/packages/sane/lib/src/logger.dart +++ b/packages/sane/lib/src/logger.dart @@ -1,3 +1,5 @@ import 'package:logging/logging.dart'; +import 'package:meta/meta.dart'; +@internal final logger = Logger('sane'); diff --git a/packages/sane/lib/src/sane.dart b/packages/sane/lib/src/sane.dart new file mode 100644 index 0000000..821f883 --- /dev/null +++ b/packages/sane/lib/src/sane.dart @@ -0,0 +1,210 @@ +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:meta/meta.dart'; +import 'package:sane/sane.dart'; +import 'package:sane/src/impl/sane_dev.dart'; +import 'package:sane/src/impl/sane_native.dart'; +import 'package:sane/src/impl/sane_sync.dart'; + +typedef AuthCallback = SaneCredentials Function(String resourceName); + +abstract interface class Sane { + /// Instantiates a new asynchronous SANE instance. + /// + /// See also: + /// + /// - [Sane.sync] + factory Sane() => NativeSane(); + + /// Instantiates a new synchronous SANE instance. + factory Sane.sync() => SyncSane(); + + /// Instantiates a mock SANE instance for testing. + factory Sane.mock() => MockSane(); + + /// Disposes the SANE instance. + /// + /// Closes all device handles and all future calls are invalid. + /// + /// See also: + /// + /// - [`sane_exit`](https://sane-project.gitlab.io/standard/api.html#sane-exit) + void dispose(); + + /// Initializes the SANE library. + FutureOr initialize([AuthCallback? authCallback]); + + /// Queries the list of devices that are available. + /// + /// This method can be called repeatedly to detect when new devices become + /// available. If argument [localOnly] is true, only local devices are + /// returned (devices directly attached to the machine that SANE is running + /// on). If it is `false`, the device list includes all remote devices that + /// are accessible to the SANE library. + /// + /// See also: + /// + /// - [`sane_get_devices`](https://sane-project.gitlab.io/standard/api.html#sane-get-devices) + FutureOr> getDevices({required bool localOnly}); +} + +// TODO(Craftplacer): Turn SaneVersion into an extension type, once available. +@immutable +class SaneVersion { + const SaneVersion.fromCode(this.code); + + final int code; + + int get major => (code >> 24) & 0xff; + + int get minor => (code >> 16) & 0xff; + + int get build => (code >> 0) & 0xffff; + + @override + String toString() => '$major.$minor.$build'; + + @override + bool operator ==(covariant SaneVersion other) => code == other.code; + + @override + int get hashCode => code; +} + +/// Represents a SANE device. +/// +/// Devices can be retrieved using [Sane.getDevices]. +/// +/// See also: +/// +/// - [Device Descriptor Type](https://sane-project.gitlab.io/standard/api.html#device-descriptor-type) +abstract interface class SaneDevice { + /// The name of the device. + String get name; + + /// The type of the device. + /// + /// For a list of predefined types, see [SaneDeviceTypes]. + String get type; + + /// The vendor (manufacturer) of the device. + /// + /// Can be `null` for virtual devices that have no physical vendor associated. + String? get vendor; + + /// The model of the device. + String get model; + + /// Disposes the SANE device. Infers [cancel]. + /// + /// See also: + /// + /// - [`sane_close`](https://sane-project.gitlab.io/standard/api.html#sane-close) + FutureOr close(); + + /// Tries to cancel the currently pending operation of the device immediately + /// or as quickly as possible. + /// + /// See also: + /// + /// - [`sane_cancel`](https://sane-project.gitlab.io/standard/api.html#sane-cancel) + FutureOr cancel(); + + /// Reads image data from the device. + /// + /// The returned [Uint8List] is [bufferSize] bytes long or less. If it is + /// zero, the end of the frame has been reached. + /// + /// Exceptions: + /// + /// - Throws [SaneCancelledException] if the operation was cancelled through + /// a call to [cancel]. + /// - Throws [SaneJammedException] if the document feeder is jammed. + /// - Throws [SaneNoDocumentsException] if the document feeder is out of + /// documents. + /// - Throws [SaneCoverOpenException] if the scanner cover is open. + /// - Throws [SaneIoException] if an error occurred while communicating with + /// the device. + /// - Throws [SaneNoMemoryException] if no memory is available. + /// - Throws [SaneAccessDeniedException] if access to the device has been + /// denied due to insufficient or invalid authentication. + /// + /// See also: + /// + /// - [`sane_read`](https://sane-project.gitlab.io/standard/api.html#sane-read) + FutureOr read({required int bufferSize}); + + /// Initiates acquisition of an image from the device. + /// + /// Exceptions: + /// + /// - Throws [SaneCancelledException] if the operation was cancelled through + /// a call to [cancel]. + /// - Throws [SaneDeviceBusyException] if the device is busy. The operation + /// should be later again. + /// - Throws [SaneJammedException] if the document feeder is jammed. + /// - Throws [SaneNoDocumentsException] if the document feeder is out of + /// documents. + /// - Throws [SaneCoverOpenException] if the scanner cover is open. + /// - Throws [SaneIoException] if an error occurred while communicating with + /// the device. + /// - Throws [SaneNoMemoryException] if no memory is available. + /// - Throws [SaneInvalidDataException] if the sane cannot be started with the + /// current set of options. The frontend should reload the option + /// descriptors. + /// + /// See also: + /// + /// - [`sane_start`](https://sane-project.gitlab.io/standard/api.html#sane-start) + FutureOr start(); + + FutureOr getOptionDescriptor(int index); + + FutureOr> getAllOptionDescriptors(); + + FutureOr> controlBoolOption( + int index, + SaneAction action, + bool? value, + ); + + FutureOr> controlIntOption( + int index, + SaneAction action, + int? value, + ); + + FutureOr> controlFixedOption( + int index, + SaneAction action, + double? value, + ); + + FutureOr> controlStringOption( + int index, + SaneAction action, + String? value, + ); + + FutureOr> controlButtonOption(int index); + + FutureOr getParameters(); +} + +/// Predefined device types for [SaneDevice.type]. +/// +/// See also: +/// +/// - [Predefined Device Information Strings](https://sane-project.gitlab.io/standard/api.html#vendor-names) +abstract final class SaneDeviceTypes { + static const filmScanner = 'film scanner'; + static const flatbedScanner = 'flatbed scanner'; + static const frameGrabber = 'frame grabber'; + static const handheldScanner = 'handheld scanner'; + static const multiFunctionPeripheral = 'multi-function peripheral'; + static const sheetfedScanner = 'sheetfed scanner'; + static const stillCamera = 'still camera'; + static const videoCamera = 'video camera'; + static const virtualDevice = 'virtual device'; +} diff --git a/packages/sane/lib/src/structures.dart b/packages/sane/lib/src/structures.dart index e199a96..580f4f2 100644 --- a/packages/sane/lib/src/structures.dart +++ b/packages/sane/lib/src/structures.dart @@ -1,5 +1,3 @@ -import 'package:meta/meta.dart'; - class SaneCredentials { SaneCredentials({ required this.username, @@ -10,33 +8,6 @@ class SaneCredentials { final String password; } -class SaneDevice { - SaneDevice({ - required this.name, - required this.vendor, - required this.model, - required this.type, - }); - - final String name; - final String vendor; - final String model; - final String type; -} - -@immutable -class SaneHandle { - const SaneHandle({required this.deviceName}); - final String deviceName; - - @override - bool operator ==(Object other) => - other is SaneHandle && other.deviceName == deviceName; - - @override - int get hashCode => deviceName.hashCode; -} - enum SaneFrameFormat { gray, rgb, @@ -170,8 +141,3 @@ class SaneOptionResult { final T result; final List infos; } - -enum SaneIOMode { - nonBlocking, - blocking; -} diff --git a/packages/sane/lib/src/type_conversion.dart b/packages/sane/lib/src/type_conversion.dart index 0371910..d5de49b 100644 --- a/packages/sane/lib/src/type_conversion.dart +++ b/packages/sane/lib/src/type_conversion.dart @@ -1,18 +1,11 @@ import 'dart:ffi' as ffi; import 'package:ffi/ffi.dart' as ffi; +import 'package:meta/meta.dart'; import 'package:sane/src/bindings.g.dart'; import 'package:sane/src/structures.dart'; -SaneDevice saneDeviceFromNative(SANE_Device device) { - return SaneDevice( - name: dartStringFromSaneString(device.name) ?? '', - vendor: dartStringFromSaneString(device.vendor) ?? '', - model: dartStringFromSaneString(device.model) ?? '', - type: dartStringFromSaneString(device.type) ?? '', - ); -} - +@internal SaneFrameFormat saneFrameFormatFromNative(SANE_Frame frame) { return switch (frame) { SANE_Frame.FRAME_GRAY => SaneFrameFormat.gray, @@ -23,6 +16,7 @@ SaneFrameFormat saneFrameFormatFromNative(SANE_Frame frame) { }; } +@internal SaneParameters saneParametersFromNative(SANE_Parameters parameters) { return SaneParameters( format: saneFrameFormatFromNative(parameters.format), @@ -34,6 +28,7 @@ SaneParameters saneParametersFromNative(SANE_Parameters parameters) { ); } +@internal SaneOptionValueType saneOptionValueTypeFromNative(SANE_Value_Type valueType) { return switch (valueType) { SANE_Value_Type.TYPE_BOOL => SaneOptionValueType.bool, @@ -45,6 +40,7 @@ SaneOptionValueType saneOptionValueTypeFromNative(SANE_Value_Type valueType) { }; } +@internal SaneOptionUnit saneOptionUnitFromNative(SANE_Unit unit) { return switch (unit) { SANE_Unit.UNIT_NONE => SaneOptionUnit.none, @@ -57,6 +53,7 @@ SaneOptionUnit saneOptionUnitFromNative(SANE_Unit unit) { }; } +@internal SANE_Action nativeSaneActionFromDart(SaneAction action) { return switch (action) { SaneAction.getValue => SANE_Action.ACTION_GET_VALUE, @@ -65,6 +62,7 @@ SANE_Action nativeSaneActionFromDart(SaneAction action) { }; } +@internal List saneOptionCapabilityFromBitmap(int bitset) { final capabilities = []; @@ -93,6 +91,7 @@ List saneOptionCapabilityFromBitmap(int bitset) { return capabilities; } +@internal SaneOptionConstraint? saneConstraintFromNative( UnnamedUnion1 constraint, SANE_Constraint_Type constraintType, @@ -150,6 +149,7 @@ SaneOptionConstraint? saneConstraintFromNative( } } +@internal SaneOptionDescriptor saneOptionDescriptorFromNative( SANE_Option_Descriptor optionDescriptor, int index, @@ -171,6 +171,7 @@ SaneOptionDescriptor saneOptionDescriptorFromNative( ); } +@internal List saneOptionInfoFromNative(int bitset) { final infos = []; if (bitset & SANE_INFO_INEXACT != 0) { @@ -185,13 +186,7 @@ List saneOptionInfoFromNative(int bitset) { return infos; } -DartSANE_Word saneBoolFromIOMode(SaneIOMode mode) { - return switch (mode) { - SaneIOMode.blocking => SANE_FALSE, - SaneIOMode.nonBlocking => SANE_TRUE, - }; -} - +@internal bool dartBoolFromSaneBool(int bool) { switch (bool) { case 0: @@ -203,25 +198,40 @@ bool dartBoolFromSaneBool(int bool) { } } -DartSANE_Word saneBoolFromDartBool(bool bool) { - return bool ? SANE_TRUE : SANE_FALSE; -} - +@internal String? dartStringFromSaneString(SANE_String_Const stringPointer) { if (stringPointer == ffi.nullptr) return null; return stringPointer.cast().toDartString(); } +@Deprecated('Use extension') SANE_String_Const saneStringFromDartString(String string) { - return string.toNativeUtf8().cast(); + return string.toSaneString(); +} + +@internal +extension SaneStringExtensions on SANE_String_Const { + String toDartString() => cast().toDartString(); +} + +@internal +extension StringExtensions on String { + SANE_String_Const toSaneString() => toNativeUtf8().cast(); +} + +@internal +extension BoolExtensions on bool { + DartSANE_Word get asSaneBool => this ? SANE_TRUE : SANE_FALSE; } const int _saneFixedScaleFactor = 1 << SANE_FIXED_SCALE_SHIFT; +@internal double saneFixedToDouble(int saneFixed) { return saneFixed / _saneFixedScaleFactor; } +@internal int doubleToSaneFixed(double double) { return (double * _saneFixedScaleFactor).toInt(); } diff --git a/packages/sane/lib/src/utils.dart b/packages/sane/lib/src/utils.dart deleted file mode 100644 index 0dc7804..0000000 --- a/packages/sane/lib/src/utils.dart +++ /dev/null @@ -1,17 +0,0 @@ -abstract class SaneUtils { - static int versionMajor(int versionCode) { - return (versionCode >> 24) & 0xff; - } - - static int versionMinor(int versionCode) { - return (versionCode >> 16) & 0xff; - } - - static int versionBuild(int versionCode) { - return (versionCode >> 0) & 0xffff; - } - - static String version(int versionCode) { - return '${versionMajor(versionCode)}.${versionMinor(versionCode)}.${versionBuild(versionCode)}'; - } -} diff --git a/packages/sane/pubspec.yaml b/packages/sane/pubspec.yaml index fccbe9b..a836bec 100644 --- a/packages/sane/pubspec.yaml +++ b/packages/sane/pubspec.yaml @@ -25,6 +25,10 @@ ffigen: headers: entry-points: - "/usr/include/sane/sane.h" + functions: + symbol-address: + include: + - sane_close enums: member-rename: ".*": diff --git a/packages/sane/test/sane_singleton_test.dart b/packages/sane/test/sane_singleton_test.dart index 25e81ca..b033bbf 100644 --- a/packages/sane/test/sane_singleton_test.dart +++ b/packages/sane/test/sane_singleton_test.dart @@ -14,7 +14,7 @@ void main() { }); test('can exit', () { - expect(sane.exit, returnsNormally); + expect(sane.dispose, returnsNormally); }); test('throws upon use', () { diff --git a/packages/sane/test/sane_test.dart b/packages/sane/test/sane_test.dart index e05952a..d87b485 100644 --- a/packages/sane/test/sane_test.dart +++ b/packages/sane/test/sane_test.dart @@ -1,4 +1,4 @@ -import 'package:sane/src/impl/sane_sync.dart'; +import 'package:sane/src/sane.dart'; import 'package:test/test.dart'; void main() { From 89d9a0517c0c2605789b175e4e2a6429f6973e95 Mon Sep 17 00:00:00 2001 From: Craftplacer <22963120+Craftplacer@users.noreply.github.com> Date: Fri, 22 Nov 2024 11:49:22 +0100 Subject: [PATCH 4/4] Add more extensions --- packages/sane/lib/src/impl/sane_sync.dart | 7 ++----- packages/sane/lib/src/type_conversion.dart | 19 ++++--------------- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/packages/sane/lib/src/impl/sane_sync.dart b/packages/sane/lib/src/impl/sane_sync.dart index 1ec82d0..83998d9 100644 --- a/packages/sane/lib/src/impl/sane_sync.dart +++ b/packages/sane/lib/src/impl/sane_sync.dart @@ -28,7 +28,7 @@ class SyncSane implements Sane { ffi.Pointer username, ffi.Pointer password, ) { - final credentials = authCallback!(dartStringFromSaneString(resource)!); + final credentials = authCallback!(resource.toDartString()); for (var i = 0; i < credentials.username.length && i < SANE_MAX_USERNAME_LEN; i++) { @@ -368,10 +368,7 @@ class SyncSaneDevice implements SaneDevice, ffi.Finalizable { saneFixedToDouble((valuePointer as ffi.Pointer).value); case SaneOptionValueType.string: - result = dartStringFromSaneString( - valuePointer as ffi.Pointer, - ) ?? - ''; + result = (valuePointer as ffi.Pointer).toDartString(); case SaneOptionValueType.button: result = null; diff --git a/packages/sane/lib/src/type_conversion.dart b/packages/sane/lib/src/type_conversion.dart index d5de49b..25b912e 100644 --- a/packages/sane/lib/src/type_conversion.dart +++ b/packages/sane/lib/src/type_conversion.dart @@ -142,7 +142,7 @@ SaneOptionConstraint? saneConstraintFromNative( case SANE_Constraint_Type.CONSTRAINT_STRING_LIST: final stringList = []; for (var i = 0; constraint.string_list[i] != ffi.nullptr; i++) { - final string = dartStringFromSaneString(constraint.string_list[i])!; + final string = constraint.string_list[i].toDartString(); stringList.add(string); } return SaneOptionConstraintStringList(stringList: stringList); @@ -156,9 +156,9 @@ SaneOptionDescriptor saneOptionDescriptorFromNative( ) { return SaneOptionDescriptor( index: index, - name: dartStringFromSaneString(optionDescriptor.name) ?? '', - title: dartStringFromSaneString(optionDescriptor.title) ?? '', - desc: dartStringFromSaneString(optionDescriptor.desc) ?? '', + name: optionDescriptor.name.toDartString(), + title: optionDescriptor.title.toDartString(), + desc: optionDescriptor.desc.toDartString(), type: saneOptionValueTypeFromNative(optionDescriptor.type), unit: saneOptionUnitFromNative(optionDescriptor.unit), size: optionDescriptor.size, @@ -198,17 +198,6 @@ bool dartBoolFromSaneBool(int bool) { } } -@internal -String? dartStringFromSaneString(SANE_String_Const stringPointer) { - if (stringPointer == ffi.nullptr) return null; - return stringPointer.cast().toDartString(); -} - -@Deprecated('Use extension') -SANE_String_Const saneStringFromDartString(String string) { - return string.toSaneString(); -} - @internal extension SaneStringExtensions on SANE_String_Const { String toDartString() => cast().toDartString();