Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,6 @@ migrate_working_dir/
/build/
/coverage/
pubspec.lock

# Runtime output
output.ppm
37 changes: 19 additions & 18 deletions packages/sane/example/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,20 @@ import 'dart:typed_data';

import 'package:logging/logging.dart';
import 'package:sane/sane.dart';
import 'package:sane/src/impl/sane_mock.dart';
import 'package:sane/src/impl/sane_native.dart';

void main(List<String> args) async {
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) {
print('${record.level.name}: ${record.time}: ${record.message}');
});

final sane = SaneIsolate(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) {
Expand All @@ -26,40 +29,36 @@ void main(List<String> 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>[];
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<Uint8List> lists) {
final totalLength = lists.fold(0, (length, list) => length + list.length);
Expand All @@ -80,4 +79,6 @@ void main(List<String> args) async {
);
final rawPixelData = mergeUint8Lists(rawPixelDataList);
file.writeAsBytesSync(rawPixelData, mode: FileMode.append);

await sane.dispose();
}
3 changes: 0 additions & 3 deletions packages/sane/lib/sane.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,4 @@ library;

export 'src/exceptions.dart';
export 'src/sane.dart';
export 'src/sane_dev.dart';
export 'src/sane_isolate.dart';
export 'src/structures.dart';
export 'src/utils.dart';
11 changes: 11 additions & 0 deletions packages/sane/lib/src/bindings.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,17 @@ class LibSane {
'sane_strstatus');
late final _sane_strstatus =
_sane_strstatusPtr.asFunction<SANE_String_Const Function(int)>();

late final addresses = _SymbolAddresses(this);
}

class _SymbolAddresses {
final LibSane _library;

_SymbolAddresses(this._library);

ffi.Pointer<ffi.NativeFunction<ffi.Void Function(SANE_Handle)>>
get sane_close => _library._sane_closePtr;
}

enum SANE_Status {
Expand Down
3 changes: 3 additions & 0 deletions packages/sane/lib/src/extensions.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import 'package:logging/logging.dart';
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')
Expand All @@ -12,6 +14,7 @@ extension SaneStatusExtension on SANE_Status {
}
}

@internal
extension LoggerExtension on Logger {
void redirect(LogRecord record) {
log(
Expand Down
191 changes: 191 additions & 0 deletions packages/sane/lib/src/impl/sane_mock.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import 'dart:async';
import 'dart:typed_data';

import 'package:logging/logging.dart';
import 'package:sane/sane.dart';

final _logger = Logger('sane.mock');

class MockSane implements Sane {
@override
void dispose() => _logger.finest('disposed');

@override
Future<List<SaneDevDevice>> getDevices({required bool localOnly}) {
_logger.finest('sane_get_devices()');
return Future.delayed(
const Duration(seconds: 1),
() => List.generate(3, SaneDevDevice.new),
);
}

@override
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<void> cancel() {
_logger.finest('sane_cancel()');
return Future.delayed(const Duration(seconds: 1));
}

@override
Future<void> close() {
_logger.finest('sane_close()');
return Future.delayed(const Duration(seconds: 1));
}

@override
String get model => 'Model $index';

@override
String get name => 'Name $index';

@override
Future<Uint8List> read({required int bufferSize}) {
_logger.finest('sane_read()');
return Future.delayed(
const Duration(seconds: 1),
() => Uint8List.fromList([]),
);
}

@override
Future<void> start() {
_logger.finest('sane_start()');
return Future.delayed(const Duration(seconds: 1));
}

@override
String get type => 'Type $index';

@override
String? get vendor => 'Vendor $index';

@override
Future<SaneOptionDescriptor> getOptionDescriptor(
int index,
) {
_logger.finest('sane_getOptionDescriptor()');
return Future.delayed(
const Duration(seconds: 1),
() => SaneOptionDescriptor(
index: index,
name: 'name',
title: 'title',
desc: 'desc',
type: SaneOptionValueType.int,
unit: SaneOptionUnit.none,
size: 1,
capabilities: [],
constraint: null,
),
);
}

@override
Future<SaneParameters> 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<List<SaneOptionDescriptor>> 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<SaneOptionResult<bool>> 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<SaneOptionResult<Null>> controlButtonOption(int index) {
_logger.finest('sane_controlButtonOption()');
return Future.delayed(
const Duration(seconds: 1),
() => SaneOptionResult(result: null, infos: []),
);
}

@override
Future<SaneOptionResult<double>> 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<SaneOptionResult<int>> 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<SaneOptionResult<String>> controlStringOption(
int index,
SaneAction action, [
String? value,
]) {
_logger.finest('sane_controlStringOption()');
return Future.delayed(
const Duration(seconds: 1),
() => SaneOptionResult(result: value ?? 'value', infos: []),
);
}
}
Loading