From 798ff200daeb22794f7ec06c28e56e6cf4505d9c Mon Sep 17 00:00:00 2001 From: Henning Geinitz Date: Sat, 13 Apr 2002 19:07:49 +0000 Subject: [PATCH] Added new test backend. Henning Meier-Geinitz --- backend/Makefile.in | 9 +- backend/test-picture.c | 834 +++++++++++++ backend/test.c | 2590 ++++++++++++++++++++++++++++++++++++++++ backend/test.conf | 81 ++ backend/test.h | 103 ++ 5 files changed, 3614 insertions(+), 3 deletions(-) create mode 100644 backend/test-picture.c create mode 100644 backend/test.c create mode 100644 backend/test.conf create mode 100644 backend/test.h diff --git a/backend/Makefile.in b/backend/Makefile.in index 58902e632..aff16824e 100644 --- a/backend/Makefile.in +++ b/backend/Makefile.in @@ -63,8 +63,9 @@ PRELOADABLE_BACKENDS = abaton agfafocus apple artec as6e avision bh canon \ canon630u @CANON_PP@ coolscan dc25 @DC210@ @DC240@ dmc epson \ @GPHOTO2@ hp m3096g matsushita microtek microtek2 mustek mustek_pp \ mustek_usb nec @NET@ pie @PINT@ plustek @PNM@ @QCAM@ ricoh s9036 \ - sceptre sharp @SM3600@ @SNAPSCAN@ sp15c st400 tamarack teco1 umax \ - umax_pp umax1220u @V4L@ + sceptre sharp @SM3600@ @SNAPSCAN@ sp15c st400 tamarack test \ + teco1 umax umax_pp umax1220u @V4L@ + ALL_BACKENDS = $(PRELOADABLE_BACKENDS) dll LIBS = $(addprefix libsane-,$(addsuffix .la,$(ALL_BACKENDS))) @@ -108,7 +109,8 @@ DISTFILES = abaton.c abaton.conf abaton.h agfafocus.c agfafocus.conf \ snapscan-sources.h snapscan-usb.c snapscan-usb.h snapscan-options.c sp15c.c \ sp15c.conf sp15c.h sp15c-scsi.h st400.c st400.conf st400.h stubs.c \ tamarack.c tamarack.conf tamarack.h teco1.c teco1.h \ - teco1.conf umax1220u.c umax1220u-common.c \ + teco1.conf test.c test.conf test.h test-picture.c umax1220u.c \ + umax1220u-common.c \ umax1220u.conf umax.c umax.conf umax.h umax_pp.c umax_pp.conf umax_pp.h \ umax_pp_low.c umax_pp_low.h umax_pp_mid.c umax_pp_mid.h umax-scanner.c \ umax-scanner.h umax-scsidef.h umax-uc1200s.c umax-uc1200se.c umax-uc1260.c \ @@ -333,6 +335,7 @@ libsane-st400.la: ../sanei/sanei_scsi.lo libsane-tamarack.la: ../sanei/sanei_config2.lo libsane-tamarack.la: ../sanei/sanei_constrain_value.lo libsane-tamarack.la: ../sanei/sanei_scsi.lo +libsane-test.la: ../sanei/sanei_constrain_value.lo libsane-teco1.la: ../sanei/sanei_config2.lo libsane-teco1.la: ../sanei/sanei_constrain_value.lo libsane-teco1.la: ../sanei/sanei_scsi.lo diff --git a/backend/test-picture.c b/backend/test-picture.c new file mode 100644 index 000000000..4a4fa9612 --- /dev/null +++ b/backend/test-picture.c @@ -0,0 +1,834 @@ +/* sane - Scanner Access Now Easy. + Copyright (C) 2002 Henning Meier-Geinitz + This file is part of the SANE package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + As a special exception, the authors of SANE give permission for + additional uses of the libraries contained in this release of SANE. + + The exception is that, if you link a SANE library with other files + to produce an executable, this does not by itself cause the + resulting executable to be covered by the GNU General Public + License. Your use of that executable is in no way restricted on + account of linking the SANE library code into it. + + This exception does not, however, invalidate any other reasons why + the executable file might be covered by the GNU General Public + License. + + If you submit changes to SANE to the maintainers to be included in + a subsequent release, you agree by submitting the changes that + those changes may be distributed with this exception intact. + + If you write modifications of your own for SANE, it is your choice + whether to permit this exception to apply to your modifications. + If you do not wish that, delete this exception notice. + + This file implements test picture functions for the test backend. +*/ + +#define BUFFER_SIZE (64 * 1024) + +static SANE_Bool little_endian (void); + +static SANE_Status +init_picture_buffer (Test_Device * test_device, SANE_Byte ** buffer, + size_t * buffer_size) +{ + SANE_Word pattern_size = 0, pattern_distance = 0; + SANE_Word line_count, b_size; + SANE_Word lines = 0; + SANE_Word bpl = test_device->bytes_per_line; + SANE_Byte *b; + SANE_Bool is_little_endian = little_endian (); + + DBG (2, "(child) init_picture_buffer test_device=%p, buffer=%p, " + "buffer_size=%p\n", test_device, buffer, buffer_size); + + if (strcmp (test_device->val[opt_test_picture].s, "Solid black") == 0 + || strcmp (test_device->val[opt_test_picture].s, "Solid white") == 0) + { + SANE_Byte pattern = 0; + + b_size = BUFFER_SIZE; + if (buffer_size) + *buffer_size = b_size; + + b = malloc (b_size); + if (!b) + { + DBG (1, "(child) init_picture_buffer: couldn't malloc buffer\n"); + return SANE_STATUS_NO_MEM; + } + + if (buffer) + *buffer = b; + + if (strcmp (test_device->val[opt_test_picture].s, "Solid black") == 0) + { + DBG (3, "(child) init_picture_buffer: drawing solid black test " + "picture %d bytes\n", b_size); + if (test_device->params.format == SANE_FRAME_GRAY + && test_device->params.depth == 1) + pattern = 0xff; + else + pattern = 0x00; + } + else + { + DBG (3, "(child) init_picture_buffer: drawing solid white test " + "picture %d bytes\n", b_size); + if (test_device->params.format == SANE_FRAME_GRAY + && test_device->params.depth == 1) + pattern = 0x00; + else + pattern = 0xff; + } + memset (b, pattern, b_size); + return SANE_STATUS_GOOD; + } + + /* Grid */ + if (strcmp (test_device->val[opt_test_picture].s, "Grid") == 0) + { + double p_size = (10.0 * SANE_UNFIX (test_device->val[opt_resolution].w) + / MM_PER_INCH); + SANE_Word increment = 1; + if (test_device->params.format == SANE_FRAME_RGB) + increment *= 3; + if (test_device->params.depth == 16) + increment *= 2; + + lines = 2 * p_size + 0.5; + b_size = lines * bpl; + if (buffer_size) + *buffer_size = b_size; + b = malloc (b_size); + if (!b) + { + DBG (1, "(child) init_picture_buffer: couldn't malloc buffer\n"); + return SANE_STATUS_NO_MEM; + } + if (buffer) + *buffer = b; + DBG (3, "(child) init_picture_buffer: drawing grid test picture " + "%d bytes, %d bpl, %d lines\n", b_size, bpl, lines); + + for (line_count = 0; line_count < lines; line_count++) + { + SANE_Word x = 0; + + for (x = 0; x < bpl; x += increment) + { + SANE_Word x1; + SANE_Byte color = 0; + + if (test_device->params.depth == 1) + { + if (test_device->params.format == SANE_FRAME_GRAY) + { + SANE_Byte value = 0; + for (x1 = 0; x1 < 8; x1++) + { + if (((SANE_Word) + ((x * 8 / increment + + (7 - x1)) / p_size) % 2) ^ (line_count > + (SANE_Word) + (p_size + + 0.5)) ^ + (test_device->params.format == SANE_FRAME_GRAY)) + color = 0x0; + else + color = 0x1; + value |= (color << x1); + } + for (x1 = 0; x1 < increment; x1++) + b[line_count * bpl + x + x1] = value; + } + else + { + SANE_Byte value = 0; + for (x1 = 0; x1 < 8; x1++) + { + if (((SANE_Word) ((x * 8 / increment + x1) / p_size) + % 2) ^ (line_count > + (SANE_Word) (p_size + + 0.5)) ^ (test_device-> + params.format == + SANE_FRAME_GRAY)) + color = 0x0; + else + color = 0x1; + value |= (color << x1); + } + for (x1 = 0; x1 < increment; x1++) + b[line_count * bpl + x + x1] = value; + } + } + else /* depth = 8, 16 */ + { + if ((((SANE_Int) (x / increment / p_size)) % 2) + ^ (line_count > (SANE_Int) (p_size + 0.5))) + color = 0x00; + else + color = 0xff; + for (x1 = 0; x1 < increment; x1++) + b[line_count * bpl + x + x1] = color; + } + } + } + return SANE_STATUS_GOOD; + } + + /* Color patterns */ + if (test_device->params.format == SANE_FRAME_GRAY + && test_device->params.depth == 1) + { + /* 1 bit black/white */ + pattern_size = 16; + pattern_distance = 0; + lines = 2 * (pattern_size + pattern_distance); + b_size = lines * bpl; + + if (buffer_size) + *buffer_size = b_size; + b = malloc (b_size); + if (!b) + { + DBG (1, "(child) init_picture_buffer: couldn't malloc buffer\n"); + return SANE_STATUS_NO_MEM; + } + if (buffer) + *buffer = b; + DBG (3, "(child) init_picture_buffer: drawing b/w test picture " + "%d bytes, %d bpl, %d lines\n", b_size, bpl, lines); + memset (b, 255, b_size); + for (line_count = 0; line_count < lines; line_count++) + { + SANE_Word x = 0; + + if (line_count >= lines / 2) + x += (pattern_size + pattern_distance) / 8; + + while (x < bpl) + { + SANE_Word width; + + width = pattern_size / 8; + if (x + width >= bpl) + width = bpl - x; + memset (b + line_count * bpl + x, 0x00, width); + x += (pattern_size + pattern_distance) * 2 / 8; + } + } + } + else if (test_device->params.format == SANE_FRAME_GRAY + && test_device->params.depth == 8) + { + /* 8 bit gray */ + pattern_size = 4; + pattern_distance = 1; + lines = 2 * (pattern_size + pattern_distance); + b_size = lines * bpl; + + if (buffer_size) + *buffer_size = b_size; + b = malloc (b_size); + if (!b) + { + DBG (1, "(child) init_picture_buffer: couldn't malloc buffer\n"); + return SANE_STATUS_NO_MEM; + } + if (buffer) + *buffer = b; + DBG (3, "(child) init_picture_buffer: drawing 8 bit gray test picture " + "%d bytes, %d bpl, %d lines\n", b_size, bpl, lines); + memset (b, 0x55, b_size); + for (line_count = 0; line_count < lines; line_count++) + { + SANE_Word x = pattern_distance; + + if (line_count % (pattern_size + pattern_distance) + < pattern_distance) + continue; + + while (x < bpl) + { + SANE_Word width; + SANE_Byte color; + + width = pattern_size; + if (x + width >= bpl) + width = bpl - x; + if (line_count > (pattern_size + pattern_distance)) + color = + 0xff - ((x / (pattern_size + pattern_distance)) & 0xff); + else + color = (x / (pattern_size + pattern_distance)) & 0xff; + memset (b + line_count * bpl + x, color, width); + x += (pattern_size + pattern_distance); + } + } + + } + else if (test_device->params.format == SANE_FRAME_GRAY + && test_device->params.depth == 16) + { + /* 16 bit gray */ + pattern_size = 256; + pattern_distance = 4; + lines = 1 * (pattern_size + pattern_distance); + b_size = lines * bpl; + + if (buffer_size) + *buffer_size = b_size; + b = malloc (b_size); + if (!b) + { + DBG (1, "(child) init_picture_buffer: couldn't malloc buffer\n"); + return SANE_STATUS_NO_MEM; + } + if (buffer) + *buffer = b; + DBG (3, "(child) init_picture_buffer: drawing 16 bit gray test picture " + "%d bytes, %d bpl, %d lines\n", b_size, bpl, lines); + memset (b, 0x55, b_size); + for (line_count = 0; line_count < lines; line_count++) + { + SANE_Word x = pattern_distance * 2; + + if (line_count % (pattern_size + pattern_distance) + < pattern_distance) + continue; + + while (x < bpl) + { + SANE_Word width; + SANE_Word x1; + SANE_Byte pattern_lo, pattern_hi; + + width = pattern_size * 2; + if (x + width >= bpl) + width = bpl - x; + pattern_lo = + ((line_count - pattern_distance) + % (pattern_size + pattern_distance)) & 0xff; + for (x1 = 0; x1 < width; x1 += 2) + { + pattern_hi = (x1 / 2) & 0xff; + if (is_little_endian) + { + b[line_count * bpl + x + x1 + 0] = pattern_lo; + b[line_count * bpl + x + x1 + 1] = pattern_hi; + } + else + { + b[line_count * bpl + x + x1 + 0] = pattern_hi; + b[line_count * bpl + x + x1 + 1] = pattern_lo; + } + } + x += ((pattern_size + pattern_distance) * 2); + } + } + + } + else if (test_device->params.format == SANE_FRAME_RGB + && test_device->params.depth == 1) + { + /* 1 bit color */ + pattern_size = 16; + pattern_distance = 0; + lines = 2 * (pattern_size + pattern_distance); + b_size = lines * bpl; + + if (buffer_size) + *buffer_size = b_size; + b = malloc (b_size); + if (!b) + { + DBG (1, "(child) init_picture_buffer: couldn't malloc buffer\n"); + return SANE_STATUS_NO_MEM; + } + if (buffer) + *buffer = b; + DBG (3, "(child) init_picture_buffer: drawing color lineart test " + "picture %d bytes, %d bpl, %d lines\n", b_size, bpl, lines); + memset (b, 0x55, b_size); + + for (line_count = 0; line_count < lines; line_count++) + { + SANE_Word x = 0; + SANE_Byte color = 0, color_r = 0, color_g = 0, color_b = 0; + + if (line_count >= lines / 2) + color = 7; + while (x < bpl) + { + SANE_Word width; + SANE_Word x2 = 0; + + width = pattern_size / 8 * 3; + if (x + width >= bpl) + width = bpl - x; + + color_b = (color & 1) * 0xff; + color_g = ((color >> 1) & 1) * 0xff; + color_r = ((color >> 2) & 1) * 0xff; + + for (x2 = 0; x2 < width; x2 += 3) + { + b[line_count * bpl + x + x2 + 0] = color_r; + b[line_count * bpl + x + x2 + 1] = color_g; + b[line_count * bpl + x + x2 + 2] = color_b; + } + if (line_count < lines / 2) + { + ++color; + if (color >= 8) + color = 0; + } + else + { + if (color == 0) + color = 8; + --color; + } + x += ((pattern_size + pattern_distance) / 8 * 3); + } + } + } + else if ((test_device->params.format == SANE_FRAME_RED + || test_device->params.format == SANE_FRAME_GREEN + || test_device->params.format == SANE_FRAME_BLUE) + && test_device->params.depth == 1) + { + /* 1 bit color three-pass */ + pattern_size = 16; + pattern_distance = 0; + lines = 2 * (pattern_size + pattern_distance); + b_size = lines * bpl; + + if (buffer_size) + *buffer_size = b_size; + b = malloc (b_size); + if (!b) + { + DBG (1, "(child) init_picture_buffer: couldn't malloc buffer\n"); + return SANE_STATUS_NO_MEM; + } + if (buffer) + *buffer = b; + DBG (3, "(child) init_picture_buffer: drawing color lineart three-pass " + "test picture %d bytes, %d bpl, %d lines\n", b_size, bpl, lines); + memset (b, 0x55, b_size); + + for (line_count = 0; line_count < lines; line_count++) + { + SANE_Word x = 0; + SANE_Byte color = 0, color_r = 0, color_g = 0, color_b = 0; + + if (line_count >= lines / 2) + color = 7; + while (x < bpl) + { + SANE_Word width; + SANE_Word x2 = 0; + + width = pattern_size / 8; + if (x + width >= bpl) + width = bpl - x; + + color_b = (color & 1) * 0xff; + color_g = ((color >> 1) & 1) * 0xff; + color_r = ((color >> 2) & 1) * 0xff; + + for (x2 = 0; x2 < width; x2++) + { + if (test_device->params.format == SANE_FRAME_RED) + b[line_count * bpl + x + x2] = color_r; + else if (test_device->params.format == SANE_FRAME_GREEN) + b[line_count * bpl + x + x2] = color_g; + else + b[line_count * bpl + x + x2] = color_b; + } + if (line_count < lines / 2) + { + ++color; + if (color >= 8) + color = 0; + } + else + { + if (color == 0) + color = 8; + --color; + } + x += (pattern_size + pattern_distance) / 8; + } + } + } + else if (test_device->params.format == SANE_FRAME_RGB + && test_device->params.depth == 8) + { + /* 8 bit color */ + pattern_size = 4; + pattern_distance = 1; + lines = 6 * (pattern_size + pattern_distance); + b_size = lines * bpl; + + if (buffer_size) + *buffer_size = b_size; + b = malloc (b_size); + if (!b) + { + DBG (1, "(child) init_picture_buffer: couldn't malloc buffer\n"); + return SANE_STATUS_NO_MEM; + } + if (buffer) + *buffer = b; + DBG (3, "(child) init_picture_buffer: drawing 8 bit color test picture " + "%d bytes, %d bpl, %d lines\n", b_size, bpl, lines); + memset (b, 0x55, b_size); + for (line_count = 0; line_count < lines; line_count++) + { + SANE_Word x = pattern_distance * 3; + + if (line_count % (pattern_size + pattern_distance) + < pattern_distance) + continue; + + while (x < bpl) + { + SANE_Word width; + SANE_Byte color = 0, color_r = 0, color_g = 0, color_b = 0; + SANE_Word x1; + + width = pattern_size * 3; + if (x + width >= bpl) + width = bpl - x; + + if ((line_count / (pattern_size + pattern_distance)) & 1) + color = + 0xff - ((x / ((pattern_size + pattern_distance) * 3)) + & 0xff); + else + color = (x / ((pattern_size + pattern_distance) * 3)) & 0xff; + + if (line_count / (pattern_size + pattern_distance) < 2) + { + color_r = color; + color_g = 0; + color_b = 0; + } + else if (line_count / (pattern_size + pattern_distance) < 4) + { + color_r = 0; + color_g = color; + color_b = 0; + } + else + { + color_r = 0; + color_g = 0; + color_b = color; + } + + for (x1 = 0; x1 < width; x1 += 3) + { + b[line_count * bpl + x + x1 + 0] = color_r; + b[line_count * bpl + x + x1 + 1] = color_g; + b[line_count * bpl + x + x1 + 2] = color_b; + } + + x += ((pattern_size + pattern_distance) * 3); + } + } + + } + else if ((test_device->params.format == SANE_FRAME_RED + || test_device->params.format == SANE_FRAME_GREEN + || test_device->params.format == SANE_FRAME_BLUE) + && test_device->params.depth == 8) + { + /* 8 bit color three-pass */ + pattern_size = 4; + pattern_distance = 1; + lines = 6 * (pattern_size + pattern_distance); + b_size = lines * bpl; + + if (buffer_size) + *buffer_size = b_size; + b = malloc (b_size); + if (!b) + { + DBG (1, "(child) init_picture_buffer: couldn't malloc buffer\n"); + return SANE_STATUS_NO_MEM; + } + if (buffer) + *buffer = b; + DBG (3, "(child) init_picture_buffer: drawing 8 bit color three-pass " + "test picture %d bytes, %d bpl, %d lines\n", b_size, bpl, lines); + memset (b, 0x55, b_size); + for (line_count = 0; line_count < lines; line_count++) + { + SANE_Word x = pattern_distance; + + if (line_count % (pattern_size + pattern_distance) + < pattern_distance) + continue; + + while (x < bpl) + { + SANE_Word width; + SANE_Byte color = 0; + + width = pattern_size; + if (x + width >= bpl) + width = bpl - x; + + if ((line_count / (pattern_size + pattern_distance)) & 1) + color = + 0xff - (x / ((pattern_size + pattern_distance)) & 0xff); + else + color = (x / (pattern_size + pattern_distance)) & 0xff; + + if (line_count / (pattern_size + pattern_distance) < 2) + { + if (test_device->params.format != SANE_FRAME_RED) + color = 0x00; + } + else if (line_count / (pattern_size + pattern_distance) < 4) + { + if (test_device->params.format != SANE_FRAME_GREEN) + color = 0x00; + } + else + { + if (test_device->params.format != SANE_FRAME_BLUE) + color = 0x00; + } + memset (b + line_count * bpl + x, color, width); + + x += (pattern_size + pattern_distance); + } + } + } + else if (test_device->params.format == SANE_FRAME_RGB + && test_device->params.depth == 16) + { + /* 16 bit color */ + pattern_size = 256; + pattern_distance = 4; + lines = pattern_size + pattern_distance; + b_size = lines * bpl; + + if (buffer_size) + *buffer_size = b_size; + b = malloc (b_size); + if (!b) + { + DBG (1, "(child) init_picture_buffer: couldn't malloc buffer\n"); + return SANE_STATUS_NO_MEM; + } + if (buffer) + *buffer = b; + DBG (3, + "(child) init_picture_buffer: drawing 16 bit color test picture " + "%d bytes, %d bpl, %d lines\n", b_size, bpl, lines); + memset (b, 0x55, b_size); + for (line_count = 0; line_count < lines; line_count++) + { + SANE_Word x = pattern_distance * 2 * 3; + + if (line_count % (pattern_size + pattern_distance) + < pattern_distance) + continue; + + while (x < bpl) + { + SANE_Word width; + SANE_Word x1; + SANE_Byte color_hi = 0, color_lo = 0; + SANE_Byte color_hi_r = 0, color_lo_r = 0; + SANE_Byte color_hi_g = 0, color_lo_g = 0; + SANE_Byte color_hi_b = 0, color_lo_b = 0; + + width = pattern_size * 2 * 3; + if (x + width >= bpl) + width = bpl - x; + + + for (x1 = 0; x1 < width; x1 += 6) + { + color_lo = + ((line_count + pattern_size) + % (pattern_size + pattern_distance)) & 0xff; + color_hi = (x1 / 6) & 0xff; + if (((x / ((pattern_size + pattern_distance) * 6)) % 3) == + 0) + { + color_lo_r = color_lo; + color_hi_r = color_hi; + color_lo_g = 0; + color_hi_g = 0; + color_lo_b = 0; + color_hi_b = 0; + } + else if (((x / ((pattern_size + pattern_distance) * 6)) % 3) + == 1) + { + color_lo_r = 0; + color_hi_r = 0; + color_lo_g = color_lo; + color_hi_g = color_hi; + color_lo_b = 0; + color_hi_b = 0; + } + else + { + color_lo_r = 0; + color_hi_r = 0; + color_lo_g = 0; + color_hi_g = 0; + color_lo_b = color_lo; + color_hi_b = color_hi; + } + if (is_little_endian) + { + b[line_count * bpl + x + x1 + 0] = color_lo_r; + b[line_count * bpl + x + x1 + 1] = color_hi_r; + b[line_count * bpl + x + x1 + 2] = color_lo_g; + b[line_count * bpl + x + x1 + 3] = color_hi_g; + b[line_count * bpl + x + x1 + 4] = color_lo_b; + b[line_count * bpl + x + x1 + 5] = color_hi_b; + } + else + { + b[line_count * bpl + x + x1 + 0] = color_hi_r; + b[line_count * bpl + x + x1 + 1] = color_lo_r; + b[line_count * bpl + x + x1 + 2] = color_hi_g; + b[line_count * bpl + x + x1 + 3] = color_lo_g; + b[line_count * bpl + x + x1 + 4] = color_hi_b; + b[line_count * bpl + x + x1 + 5] = color_lo_b; + } + } + x += ((pattern_size + pattern_distance) * 2 * 3); + } + } + + } + else if ((test_device->params.format == SANE_FRAME_RED + || test_device->params.format == SANE_FRAME_GREEN + || test_device->params.format == SANE_FRAME_BLUE) + && test_device->params.depth == 16) + { + /* 16 bit color three-pass */ + pattern_size = 256; + pattern_distance = 4; + lines = pattern_size + pattern_distance; + b_size = lines * bpl; + + if (buffer_size) + *buffer_size = b_size; + b = malloc (b_size); + if (!b) + { + DBG (1, "(child) init_picture_buffer: couldn't malloc buffer\n"); + return SANE_STATUS_NO_MEM; + } + if (buffer) + *buffer = b; + DBG (3, "(child) init_picture_buffer: drawing 16 bit color three-pass " + "test picture %d bytes, %d bpl, %d lines\n", b_size, bpl, lines); + memset (b, 0x55, b_size); + for (line_count = 0; line_count < lines; line_count++) + { + SANE_Word x = pattern_distance * 2; + + if (line_count % (pattern_size + pattern_distance) + < pattern_distance) + continue; + + while (x < bpl) + { + SANE_Word width; + SANE_Word x1; + SANE_Byte color_hi = 0, color_lo = 0; + + width = pattern_size * 2; + if (x + width >= bpl) + width = bpl - x; + + + for (x1 = 0; x1 < width; x1 += 2) + { + color_lo = + ((line_count + pattern_size) + % (pattern_size + pattern_distance)) & 0xff; + color_hi = (x1 / 2) & 0xff; + if (((x / ((pattern_size + pattern_distance) * 2)) % 3) == + 0) + { + if (test_device->params.format != SANE_FRAME_RED) + { + color_lo = 0x00; + color_hi = 0x00; + } + } + else if (((x / ((pattern_size + pattern_distance) * 2)) % 3) + == 1) + { + if (test_device->params.format != SANE_FRAME_GREEN) + { + color_lo = 0x00; + color_hi = 0x00; + } + } + else + { + if (test_device->params.format != SANE_FRAME_BLUE) + { + color_lo = 0x00; + color_hi = 0x00; + } + } + + if (is_little_endian) + { + b[line_count * bpl + x + x1 + 0] = color_lo; + b[line_count * bpl + x + x1 + 1] = color_hi; + } + else + { + b[line_count * bpl + x + x1 + 0] = color_hi; + b[line_count * bpl + x + x1 + 1] = color_lo; + } + + } + x += ((pattern_size + pattern_distance) * 2); + } + } + + } + else /* Huh? */ + { + DBG (1, "(child) init_picture_buffer: unknown mode\n"); + return SANE_STATUS_INVAL; + } + + return SANE_STATUS_GOOD; +} diff --git a/backend/test.c b/backend/test.c new file mode 100644 index 000000000..3aaa5af49 --- /dev/null +++ b/backend/test.c @@ -0,0 +1,2590 @@ +/* sane - Scanner Access Now Easy. + Copyright (C) 2002 Henning Meier-Geinitz + This file is part of the SANE package. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + As a special exception, the authors of SANE give permission for + additional uses of the libraries contained in this release of SANE. + + The exception is that, if you link a SANE library with other files + to produce an executable, this does not by itself cause the + resulting executable to be covered by the GNU General Public + License. Your use of that executable is in no way restricted on + account of linking the SANE library code into it. + + This exception does not, however, invalidate any other reasons why + the executable file might be covered by the GNU General Public + License. + + If you submit changes to SANE to the maintainers to be included in + a subsequent release, you agree by submitting the changes that + those changes may be distributed with this exception intact. + + If you write modifications of your own for SANE, it is your choice + whether to permit this exception to apply to your modifications. + If you do not wish that, delete this exception notice. + + This backend is for testing frontends. +*/ + +#define BUILD 12 + +#include "../include/sane/config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../include/sane/sane.h" +#include "../include/sane/sanei.h" +#include "../include/sane/saneopts.h" +#include "../include/sane/sanei_config.h" + +#define BACKEND_NAME test +#include "../include/sane/sanei_backend.h" + +#include "test.h" + +#include "test-picture.c" + +#define TEST_CONFIG_FILE "test.conf" + +static SANE_Device **sane_device_list = 0; +static Test_Device *first_test_device = 0; + +static SANE_Range geometry_range = { + SANE_FIX (0.0), + SANE_FIX (200.0), + SANE_FIX (1.0) +}; + +static SANE_Range resolution_range = { + SANE_FIX (1.0), + SANE_FIX (1200.0), + SANE_FIX (1.0) +}; + +static SANE_Range read_limit_size_range = { + 1, + 64 * 1024, /* 64 KB ought to be enough for everyone :-) */ + 1 +}; + +static SANE_Range read_delay_duration_range = { + 1000, + 200 * 1000, /* 200 msec */ + 1000 +}; + +static SANE_Range int_constraint_range = { + 4, + 192, + 2 +}; + +static SANE_Range fixed_constraint_range = { + SANE_FIX (17001.0), + SANE_FIX (32767.0), + SANE_FIX (2.0) +}; + +static SANE_String_Const mode_list[] = { + SANE_I18N ("Gray"), SANE_I18N ("Color"), + 0 +}; + +static SANE_String_Const order_list[] = { + "RGB", "RBG", "GBR", "GRB", "BRG", "BGR", + 0 +}; + +static SANE_String_Const test_picture_list[] = { + SANE_I18N ("Solid black"), SANE_I18N ("Solid white"), + SANE_I18N ("Color patterns"), SANE_I18N ("Grid"), + 0 +}; + +static SANE_String_Const read_status_code_list[]= { + SANE_I18N("Default"), "SANE_STATUS_UNSUPPORTED", "SANE_STATUS_CANCELLED", + "SANE_STATUS_DEVICE_BUSY", "SANE_STATUS_INVAL", "SANE_STATUS_EOF", + "SANE_STATUS_JAMMED", "SANE_STATUS_NO_DOCS", "SANE_STATUS_COVER_OPEN", + "SANE_STATUS_IO_ERROR", "SANE_STATUS_NO_MEM", "SANE_STATUS_ACCESS_DENIED", + 0 +}; + +static SANE_Int depth_list[] = { + 3, 1, 8, 16 +}; + +static SANE_Int int_constraint_word_list[] = { + 9, -42, -8, 0, 17, 42, 256, 65536, 256 * 256 * 256, 256 * 256 * 256 * 64 +}; + +static SANE_Int fixed_constraint_word_list[] = { + 4, SANE_FIX (-32.7), SANE_FIX (12.1), SANE_FIX (42.0), SANE_FIX (129.5) +}; + +static SANE_String_Const string_constraint_string_list[] = { + "First entry", "Second entry", + "This is the very long third entry. Maybe the frontend has an idea how to " + "display it", + 0 +}; + +static SANE_String_Const string_constraint_long_string_list[] = { + "First entry", "Second entry", "3", "4", "5", "6", "7", "8", "9", "10", + "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", + "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", + "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", + 0 +}; + +static SANE_Int int_array[] = { + -17, 0, -5, 42, 91, 256 * 256 * 256 * 64 +}; + +static SANE_Int int_array_constraint_range[] = { + 48, 6, 4, 92, 190, 16 +}; + +static SANE_Int int_array_constraint_word_list[] = { + -42, 0, 9, 17, 42, 42 +}; + +static double random_factor; /* use for fuzzyness of parameters */ + +/* initial values */ +static SANE_Word init_number_of_devices = 2; +static SANE_Fixed init_tl_x = SANE_FIX (0.0); +static SANE_Fixed init_tl_y = SANE_FIX (0.0); +static SANE_Fixed init_br_x = SANE_FIX (80.0); +static SANE_Fixed init_br_y = SANE_FIX (100.0); +static SANE_Word init_resolution = 50; +static SANE_String init_mode = "Gray"; +static SANE_Word init_depth = 8; +static SANE_Bool init_hand_scanner = SANE_FALSE; +static SANE_Bool init_three_pass = SANE_FALSE; +static SANE_String init_three_pass_order = "RGB"; +static SANE_String init_test_picture = "Solid black"; +static SANE_Bool init_read_limit = SANE_FALSE; +static SANE_Word init_read_limit_size = 1; +static SANE_Bool init_read_delay = SANE_FALSE; +static SANE_Word init_read_delay_duration = 1000; +static SANE_String init_read_status_code = "Default"; +static SANE_Bool init_fuzzy_parameters = SANE_FALSE; +static SANE_Bool init_non_blocking = SANE_FALSE; +static SANE_Bool init_select_fd = SANE_FALSE; +static SANE_Bool init_enable_test_options = SANE_FALSE; +static SANE_String init_string = "This is the contents of the string option. " + "Fill some more words to see how the frontend behaves."; +static SANE_String init_string_constraint_string_list = "First entry"; +static SANE_String init_string_constraint_long_string_list = "First entry"; + +/* Test if this machine is little endian (from coolscan.c) */ +static SANE_Bool +little_endian (void) +{ + SANE_Int testvalue = 255; + u_int8_t *firstbyte = (u_int8_t *) & testvalue; + + if (*firstbyte == 255) + return SANE_TRUE; + return SANE_FALSE; +} + +static void +swap_double (double *a, double *b) +{ + double c; + + c = *a; + *a = *b; + *b = c; + + return; +} + +static size_t +max_string_size (const SANE_String_Const strings[]) +{ + size_t size, max_size = 0; + SANE_Int i; + + for (i = 0; strings[i]; ++i) + { + size = strlen (strings[i]) + 1; + if (size > max_size) + max_size = size; + } + return max_size; +} + +static SANE_Bool +check_handle (SANE_Handle handle) +{ + Test_Device *test_device = first_test_device; + + while (test_device) + { + if (test_device == (Test_Device *) handle) + return SANE_TRUE; + test_device = test_device->next; + } + return SANE_FALSE; +} + +static SANE_Status +init_options (Test_Device * test_device) +{ + SANE_Option_Descriptor *od; + + DBG (2, "init_options: test_device=%p\n", test_device); + + /* opt_num_opts */ + od = &test_device->opt[opt_num_opts]; + od->name = ""; + od->title = SANE_TITLE_NUM_OPTIONS; + od->desc = SANE_DESC_NUM_OPTIONS; + od->type = SANE_TYPE_INT; + od->unit = SANE_UNIT_NONE; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_num_opts].w = num_options; + + /* opt_mode_group */ + od = &test_device->opt[opt_mode_group]; + od->name = ""; + od->title = SANE_I18N ("Scan Mode"); + od->desc = ""; + od->type = SANE_TYPE_GROUP; + od->unit = SANE_UNIT_NONE; + od->size = 0; + od->cap = 0; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_mode_group].w = 0; + + /* opt_mode */ + od = &test_device->opt[opt_mode]; + od->name = SANE_NAME_SCAN_MODE; + od->title = SANE_TITLE_SCAN_MODE; + od->desc = SANE_DESC_SCAN_MODE; + od->type = SANE_TYPE_STRING; + od->unit = SANE_UNIT_NONE; + od->size = max_string_size (mode_list); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + od->constraint_type = SANE_CONSTRAINT_STRING_LIST; + od->constraint.string_list = mode_list; + test_device->val[opt_mode].s = malloc (od->size); + if (!test_device->val[opt_mode].s) + return SANE_STATUS_NO_MEM; + strcpy (test_device->val[opt_mode].s, init_mode); + + /* opt_depth */ + od = &test_device->opt[opt_depth]; + od->name = SANE_NAME_BIT_DEPTH; + od->title = SANE_TITLE_BIT_DEPTH; + od->desc = SANE_DESC_BIT_DEPTH; + od->type = SANE_TYPE_INT; + od->unit = SANE_UNIT_NONE; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + od->constraint_type = SANE_CONSTRAINT_WORD_LIST; + od->constraint.word_list = depth_list; + test_device->val[opt_depth].w = init_depth; + + /* opt_hand_scanner */ + od = &test_device->opt[opt_hand_scanner]; + od->name = "hand-scanner"; + od->title = SANE_I18N ("Hand-scanner simulation"); + od->desc = SANE_I18N ("Simulate a hand-scanner. Hand-scanners do not " + "know the image height a priori. Instead, they " + "return a height of -1. Setting this option allows " + "to test whether a frontend can handle this " + "correctly. This option also enables a fixed width " + "of 11 cm."); + od->type = SANE_TYPE_BOOL; + od->unit = SANE_UNIT_NONE; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_hand_scanner].w = init_hand_scanner; + + /* opt_three_pass */ + od = &test_device->opt[opt_three_pass]; + od->name = "three-pass"; + od->title = SANE_I18N ("Three-pass simulation"); + od->desc = SANE_I18N ("Simulate a three-pass scanner. In color mode, three " + "frames are transmitted."); + od->type = SANE_TYPE_BOOL; + od->unit = SANE_UNIT_NONE; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + if (strcmp (init_mode, "Color") != 0) + od->cap |= SANE_CAP_INACTIVE; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_three_pass].w = init_three_pass; + + /* opt_three_pass_order */ + od = &test_device->opt[opt_three_pass_order]; + od->name = "three-pass-order"; + od->title = SANE_I18N ("Set the order of frames."); + od->desc = SANE_I18N ("Set the order of frames in three-pass color mode."); + od->type = SANE_TYPE_STRING; + od->unit = SANE_UNIT_NONE; + od->size = max_string_size (order_list); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + if (strcmp (init_mode, "Color") != 0) + od->cap |= SANE_CAP_INACTIVE; + if (!init_three_pass) + od->cap |= SANE_CAP_INACTIVE; + od->constraint_type = SANE_CONSTRAINT_STRING_LIST; + od->constraint.string_list = order_list; + test_device->val[opt_three_pass_order].s = malloc (od->size); + if (!test_device->val[opt_three_pass_order].s) + return SANE_STATUS_NO_MEM; + strcpy (test_device->val[opt_three_pass_order].s, init_three_pass_order); + + /* opt_resolution */ + od = &test_device->opt[opt_resolution]; + od->name = SANE_NAME_SCAN_RESOLUTION; + od->title = SANE_TITLE_SCAN_RESOLUTION; + od->desc = SANE_DESC_SCAN_RESOLUTION; + od->type = SANE_TYPE_FIXED; + od->unit = SANE_UNIT_DPI; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + od->constraint_type = SANE_CONSTRAINT_RANGE; + od->constraint.range = &resolution_range; + test_device->val[opt_resolution].w = init_resolution; + + /* opt_special_group */ + od = &test_device->opt[opt_special_group]; + od->name = ""; + od->title = SANE_I18N ("Special Options"); + od->desc = ""; + od->type = SANE_TYPE_GROUP; + od->unit = SANE_UNIT_NONE; + od->size = 0; + od->cap = 0; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_special_group].w = 0; + + /* opt_test_picture */ + od = &test_device->opt[opt_test_picture]; + od->name = "test-picture"; + od->title = SANE_I18N ("Select the test picture"); + od->desc = + SANE_I18N ("Select the kind of test picture. Available options:\n" + "Solid black: fills the whole scan with black.\n" + "Solid white: fills the whole scan with white.\n" + "Color pattern: draws various color test patterns " + "depending on the mode.\n" + "Grid: draws a black/white grid with a width and " + "height of 10 mm per square."); + od->type = SANE_TYPE_STRING; + od->unit = SANE_UNIT_NONE; + od->size = max_string_size (test_picture_list); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + od->constraint_type = SANE_CONSTRAINT_STRING_LIST; + od->constraint.string_list = test_picture_list; + test_device->val[opt_test_picture].s = malloc (od->size); + if (!test_device->val[opt_test_picture].s) + return SANE_STATUS_NO_MEM; + strcpy (test_device->val[opt_test_picture].s, init_test_picture); + + /* opt_read_limit */ + od = &test_device->opt[opt_read_limit]; + od->name = "read-limit"; + od->title = SANE_I18N ("Read limit"); + od->desc = SANE_I18N ("Limit the amount of data tranferred with each " + "call to sane_read()."); + od->type = SANE_TYPE_BOOL; + od->unit = SANE_UNIT_NONE; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_read_limit].w = init_read_limit; + + /* opt_read_limit_size */ + od = &test_device->opt[opt_read_limit_size]; + od->name = "read-limit-size"; + od->title = SANE_I18N ("Size of read-limit"); + od->desc = SANE_I18N ("The (maximum) amount of data transferred with each " + "call to sane_read()."); + od->type = SANE_TYPE_INT; + od->unit = SANE_UNIT_NONE; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + if (!init_read_limit) + od->cap |= SANE_CAP_INACTIVE; + od->constraint_type = SANE_CONSTRAINT_RANGE; + od->constraint.range = &read_limit_size_range; + test_device->val[opt_read_limit_size].w = init_read_limit_size; + + /* opt_read_delay */ + od = &test_device->opt[opt_read_delay]; + od->name = "read-delay"; + od->title = SANE_I18N ("Read delay"); + od->desc = SANE_I18N ("Delay the transfer of data to the pipe."); + od->type = SANE_TYPE_BOOL; + od->unit = SANE_UNIT_NONE; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_read_delay].w = init_read_delay; + + /* opt_read_delay_duration */ + od = &test_device->opt[opt_read_delay_duration]; + od->name = "read-delay-duration"; + od->title = SANE_I18N ("Duration of read-delay"); + od->desc = SANE_I18N ("How long to wait after transferring each buffer of " + "data through the pipe."); + od->type = SANE_TYPE_INT; + od->unit = SANE_UNIT_MICROSECOND; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + if (!init_read_delay) + od->cap |= SANE_CAP_INACTIVE; + od->constraint_type = SANE_CONSTRAINT_RANGE; + od->constraint.range = &read_delay_duration_range; + test_device->val[opt_read_delay_duration].w = init_read_delay_duration; + + /* opt_read_status_code */ + od = &test_device->opt[opt_read_status_code]; + od->name = "read-return-value"; + od->title = SANE_I18N ("Return-value of sane_read"); + od->desc = + SANE_I18N ("Select the return-value of sane_read(). \"Default\" " + "is the normal handling for scanning. All other status \n" + "codes are for testing how the frontend handles them.\n"); + od->type = SANE_TYPE_STRING; + od->unit = SANE_UNIT_NONE; + od->size = max_string_size (read_status_code_list); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + od->constraint_type = SANE_CONSTRAINT_STRING_LIST; + od->constraint.string_list = read_status_code_list; + test_device->val[opt_read_status_code].s = malloc (od->size); + if (!test_device->val[opt_read_status_code].s) + return SANE_STATUS_NO_MEM; + strcpy (test_device->val[opt_read_status_code].s, init_read_status_code); + + /* opt_fuzzy_parameters */ + od = &test_device->opt[opt_fuzzy_parameters]; + od->name = "fuzzy-parameters"; + od->title = SANE_I18N ("Fuzzy parameters"); + od->desc = SANE_I18N ("Return fuzzy lines and bytes per line when " + "sane_parameters() is called before sane_start()."); + od->type = SANE_TYPE_BOOL; + od->unit = SANE_UNIT_NONE; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_fuzzy_parameters].w = init_fuzzy_parameters; + + /* opt_non_blocking */ + od = &test_device->opt[opt_non_blocking]; + od->name = "non-blocking"; + od->title = SANE_I18N ("Use non-blocking IO"); + od->desc = SANE_I18N ("Use non-blocking IO for sane_read() if supported " + "by the frontend."); + od->type = SANE_TYPE_BOOL; + od->unit = SANE_UNIT_NONE; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_non_blocking].w = init_non_blocking; + + /* opt_select_fd */ + od = &test_device->opt[opt_select_fd]; + od->name = "select-fd"; + od->title = SANE_I18N ("Offer select file descriptor"); + od->desc = SANE_I18N ("Offer a select filedescriptor for detecting if " + "sane_read() will return data."); + od->type = SANE_TYPE_BOOL; + od->unit = SANE_UNIT_NONE; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_select_fd].w = init_select_fd; + + /* opt_enable_test_options */ + od = &test_device->opt[opt_enable_test_options]; + od->name = "enable-test-options"; + od->title = SANE_I18N ("Enable test options"); + od->desc = SANE_I18N ("Enable various test options. This is for testing " + "the ability of frontends to view and modify all the " + "different SANE option types."); + od->type = SANE_TYPE_BOOL; + od->unit = SANE_UNIT_NONE; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_enable_test_options].w = init_enable_test_options; + + /* opt_print_options */ + od = &test_device->opt[opt_print_options]; + od->name = "print-options"; + od->title = SANE_I18N ("Print options"); + od->desc = SANE_I18N ("Print a list of all options."); + od->type = SANE_TYPE_BUTTON; + od->unit = SANE_UNIT_NONE; + od->size = 0; + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.string_list = 0; + test_device->val[opt_print_options].w = 0; + + /* opt_geometry_group */ + od = &test_device->opt[opt_geometry_group]; + od->name = ""; + od->title = SANE_I18N ("Geometry"); + od->desc = ""; + od->type = SANE_TYPE_GROUP; + od->unit = SANE_UNIT_NONE; + od->size = 0; + od->cap = 0; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_geometry_group].w = 0; + + /* opt_tl_x */ + od = &test_device->opt[opt_tl_x]; + od->name = SANE_NAME_SCAN_TL_X; + od->title = SANE_TITLE_SCAN_TL_X; + od->desc = SANE_DESC_SCAN_TL_X; + od->type = SANE_TYPE_FIXED; + od->unit = SANE_UNIT_MM; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + od->constraint_type = SANE_CONSTRAINT_RANGE; + od->constraint.range = &geometry_range; + test_device->val[opt_tl_x].w = init_tl_x; + + /* opt_tl_y */ + od = &test_device->opt[opt_tl_y]; + od->name = SANE_NAME_SCAN_TL_Y; + od->title = SANE_TITLE_SCAN_TL_Y; + od->desc = SANE_DESC_SCAN_TL_Y; + od->type = SANE_TYPE_FIXED; + od->unit = SANE_UNIT_MM; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + od->constraint_type = SANE_CONSTRAINT_RANGE; + od->constraint.range = &geometry_range; + test_device->val[opt_tl_y].w = init_tl_y; + + /* opt_br_x */ + od = &test_device->opt[opt_br_x]; + od->name = SANE_NAME_SCAN_BR_X; + od->title = SANE_TITLE_SCAN_BR_X; + od->desc = SANE_DESC_SCAN_BR_X; + od->type = SANE_TYPE_FIXED; + od->unit = SANE_UNIT_MM; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + od->constraint_type = SANE_CONSTRAINT_RANGE; + od->constraint.range = &geometry_range; + test_device->val[opt_br_x].w = init_br_x; + + /* opt_br_y */ + od = &test_device->opt[opt_br_y]; + od->name = SANE_NAME_SCAN_BR_Y; + od->title = SANE_TITLE_SCAN_BR_Y; + od->desc = SANE_DESC_SCAN_BR_Y; + od->type = SANE_TYPE_FIXED; + od->unit = SANE_UNIT_MM; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + od->constraint_type = SANE_CONSTRAINT_RANGE; + od->constraint.range = &geometry_range; + test_device->val[opt_br_y].w = init_br_y; + + /* opt_bool_group */ + od = &test_device->opt[opt_bool_group]; + od->name = ""; + od->title = SANE_I18N ("Bool test options"); + od->desc = ""; + od->type = SANE_TYPE_GROUP; + od->unit = SANE_UNIT_NONE; + od->size = 0; + od->cap = 0; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_bool_group].w = 0; + + /* opt_bool_soft_select_soft_detect */ + od = &test_device->opt[opt_bool_soft_select_soft_detect]; + od->name = "bool-soft-select-soft-detect"; + od->title = SANE_I18N ("(1/6) Bool soft select soft detect"); + od->desc = + SANE_I18N ("(1/6) Bool test option that has soft select and soft " + "detect (and advanced) capabilities. That's just a " + "normal bool option."); + od->type = SANE_TYPE_BOOL; + od->unit = SANE_UNIT_NONE; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED; + if (init_enable_test_options == SANE_FALSE) + od->cap |= SANE_CAP_INACTIVE; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_bool_soft_select_soft_detect].w = SANE_FALSE; + + /* opt_bool_hard_select_soft_detect */ + od = &test_device->opt[opt_bool_hard_select_soft_detect]; + od->name = "bool-hard-select-soft-detect"; + od->title = SANE_I18N ("(2/6) Bool hard select soft detect"); + od->desc = + SANE_I18N ("(2/6) Bool test option that has hard select and soft " + "detect (and advanced) capabilities. That means the " + "option can't be set by the frontend but by the user " + "(e.g. by pressing a button at the device)."); + od->type = SANE_TYPE_BOOL; + od->unit = SANE_UNIT_NONE; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; + if (init_enable_test_options == SANE_FALSE) + od->cap |= SANE_CAP_INACTIVE; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_bool_hard_select_soft_detect].w = SANE_FALSE; + + /* opt_bool_hard_select */ + od = &test_device->opt[opt_bool_hard_select]; + od->name = "bool-hard-select"; + od->title = SANE_I18N ("(3/6) Bool hard select"); + od->desc = SANE_I18N ("(3/6) Bool test option that has hard select " + "(and advanced) capabilities. That means the option " + "can't be set by the frontend but by the user " + "(e.g. by pressing a button at the device) and can't " + "be read by the frontend."); + od->type = SANE_TYPE_BOOL; + od->unit = SANE_UNIT_NONE; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; + if (init_enable_test_options == SANE_FALSE) + od->cap |= SANE_CAP_INACTIVE; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_bool_hard_select].w = SANE_FALSE; + + /* opt_bool_soft_detect */ + od = &test_device->opt[opt_bool_soft_detect]; + od->name = "bool-soft-detect"; + od->title = SANE_I18N ("(4/6) Bool soft detect"); + od->desc = SANE_I18N ("(4/6) Bool test option that has soft detect " + "(and advanced) capabilities. That means the option " + "is read-only."); + od->type = SANE_TYPE_BOOL; + od->unit = SANE_UNIT_NONE; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; + if (init_enable_test_options == SANE_FALSE) + od->cap |= SANE_CAP_INACTIVE; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_bool_soft_detect].w = SANE_FALSE; + + /* opt_bool_soft_select_soft_detect_emulated */ + od = &test_device->opt[opt_bool_soft_select_soft_detect_emulated]; + od->name = "bool-soft-select-soft-detect-emulated"; + od->title = SANE_I18N ("(5/6) Bool soft select soft detect emulated"); + od->desc = SANE_I18N ("(5/6) Bool test option that has soft select, soft " + "detect, and emulated (and advanced) capabilities. "); + od->type = SANE_TYPE_BOOL; + od->unit = SANE_UNIT_NONE; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED + | SANE_CAP_EMULATED; + if (init_enable_test_options == SANE_FALSE) + od->cap |= SANE_CAP_INACTIVE; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_bool_soft_select_soft_detect_emulated].w = SANE_FALSE; + + /* opt_bool_soft_select_soft_detect_auto */ + od = &test_device->opt[opt_bool_soft_select_soft_detect_auto]; + od->name = "bool-soft-select-soft-detect-auto"; + od->title = SANE_I18N ("(6/6) Bool soft select soft detect auto"); + od->desc = SANE_I18N ("(6/6) Bool test option that has soft select, soft " + "detect, and automatic (and advanced) capabilities. " + "This option can be automatically set by the backend."); + od->type = SANE_TYPE_BOOL; + od->unit = SANE_UNIT_NONE; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED + | SANE_CAP_AUTOMATIC; + if (init_enable_test_options == SANE_FALSE) + od->cap |= SANE_CAP_INACTIVE; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_bool_soft_select_soft_detect_auto].w = SANE_FALSE; + + /* opt_int_group */ + od = &test_device->opt[opt_int_group]; + od->name = ""; + od->title = SANE_I18N ("Int test options"); + od->desc = ""; + od->type = SANE_TYPE_GROUP; + od->unit = SANE_UNIT_NONE; + od->size = 0; + od->cap = 0; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_int_group].w = 0; + + /* opt_int */ + od = &test_device->opt[opt_int]; + od->name = "int"; + od->title = SANE_I18N ("(1/6) Int"); + od->desc = SANE_I18N ("(1/6) Int test option with no unit and no " + "constraint set."); + od->type = SANE_TYPE_INT; + od->unit = SANE_UNIT_NONE; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED; + if (init_enable_test_options == SANE_FALSE) + od->cap |= SANE_CAP_INACTIVE; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_int].w = 42; + + /* opt_int_constraint_range */ + od = &test_device->opt[opt_int_constraint_range]; + od->name = "int-constraint-range"; + od->title = SANE_I18N ("(2/6) Int constraint range"); + od->desc = SANE_I18N ("(2/6) Int test option with unit pixel and " + "constraint range set. Minimum is 4, maximum 192, and " + "quant is 2."); + od->type = SANE_TYPE_INT; + od->unit = SANE_UNIT_PIXEL; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED; + if (init_enable_test_options == SANE_FALSE) + od->cap |= SANE_CAP_INACTIVE; + od->constraint_type = SANE_CONSTRAINT_RANGE; + od->constraint.range = &int_constraint_range; + test_device->val[opt_int_constraint_range].w = 26; + + /* opt_int_constraint_word_list */ + od = &test_device->opt[opt_int_constraint_word_list]; + od->name = "int-constraint-word-list"; + od->title = SANE_I18N ("(3/6) Int constraint word list"); + od->desc = SANE_I18N ("(3/6) Int test option with unit bits and " + "constraint word list set. "); + od->type = SANE_TYPE_INT; + od->unit = SANE_UNIT_BIT; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED; + if (init_enable_test_options == SANE_FALSE) + od->cap |= SANE_CAP_INACTIVE; + od->constraint_type = SANE_CONSTRAINT_WORD_LIST; + od->constraint.word_list = int_constraint_word_list; + test_device->val[opt_int_constraint_word_list].w = 42; + + /* opt_int_array */ + od = &test_device->opt[opt_int_array]; + od->name = "int-constraint-array"; + od->title = SANE_I18N ("(4/6) Int array"); + od->desc = SANE_I18N ("(4/6) Int test option with unit mm and using " + "an array without constraints."); + od->type = SANE_TYPE_INT; + od->unit = SANE_UNIT_MM; + od->size = 6 * sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED; + if (init_enable_test_options == SANE_FALSE) + od->cap |= SANE_CAP_INACTIVE; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_int_array].wa = &int_array[0]; + + /* opt_int_array_constraint_range */ + od = &test_device->opt[opt_int_array_constraint_range]; + od->name = "int-constraint-array-constraint-range"; + od->title = SANE_I18N ("(5/6) Int array constraint range"); + od->desc = SANE_I18N ("(5/6) Int test option with unit mm and using " + "an array with a range constraint. Minimum is 4, " + "maximum 192, and quant is 2."); + od->type = SANE_TYPE_INT; + od->unit = SANE_UNIT_DPI; + od->size = 6 * sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED; + if (init_enable_test_options == SANE_FALSE) + od->cap |= SANE_CAP_INACTIVE; + od->constraint_type = SANE_CONSTRAINT_RANGE; + od->constraint.range = &int_constraint_range; + test_device->val[opt_int_array_constraint_range].wa = + &int_array_constraint_range[0]; + + /* opt_int_array_constraint_word_list */ + od = &test_device->opt[opt_int_array_constraint_word_list]; + od->name = "int-constraint-array-constraint-word-list"; + od->title = SANE_I18N ("(6/6) Int array constraint word list"); + od->desc = SANE_I18N ("(6/6) Int test option with unit percent and using " + "an array a word list constraint."); + od->type = SANE_TYPE_INT; + od->unit = SANE_UNIT_PERCENT; + od->size = 6 * sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED; + if (init_enable_test_options == SANE_FALSE) + od->cap |= SANE_CAP_INACTIVE; + od->constraint_type = SANE_CONSTRAINT_WORD_LIST; + od->constraint.word_list = int_constraint_word_list; + test_device->val[opt_int_array_constraint_word_list].wa = + &int_array_constraint_word_list[0]; + + /* opt_fixed_group */ + od = &test_device->opt[opt_fixed_group]; + od->name = ""; + od->title = SANE_I18N ("Fixed test options"); + od->desc = ""; + od->type = SANE_TYPE_GROUP; + od->unit = SANE_UNIT_NONE; + od->size = 0; + od->cap = 0; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_fixed_group].w = 0; + + /* opt_fixed */ + od = &test_device->opt[opt_fixed]; + od->name = "fixed"; + od->title = SANE_I18N ("(1/3) Fixed"); + od->desc = SANE_I18N ("(1/3) Fixed test option with no unit and no " + "constraint set."); + od->type = SANE_TYPE_FIXED; + od->unit = SANE_UNIT_NONE; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED; + if (init_enable_test_options == SANE_FALSE) + od->cap |= SANE_CAP_INACTIVE; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_fixed].w = SANE_FIX (42.0); + + /* opt_fixed_constraint_range */ + od = &test_device->opt[opt_fixed_constraint_range]; + od->name = "fixed-constraint-range"; + od->title = SANE_I18N ("(2/3) Fixed constraint range"); + od->desc = SANE_I18N ("(2/3) Fixed test option with unit microsecond and " + "constraint range set. Minimum is -42.17, maximum " + "32767.9999, and quant is 2.0."); + od->type = SANE_TYPE_FIXED; + od->unit = SANE_UNIT_MICROSECOND; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED; + if (init_enable_test_options == SANE_FALSE) + od->cap |= SANE_CAP_INACTIVE; + od->constraint_type = SANE_CONSTRAINT_RANGE; + od->constraint.range = &fixed_constraint_range; + test_device->val[opt_fixed_constraint_range].w = SANE_FIX (42.0); + + /* opt_fixed_constraint_word_list */ + od = &test_device->opt[opt_fixed_constraint_word_list]; + od->name = "fixed-constraint-word-list"; + od->title = SANE_I18N ("(3/3) Fixed constraint word list"); + od->desc = SANE_I18N ("(3/3) Fixed test option with no unit and " + "constraint word list set. "); + od->type = SANE_TYPE_FIXED; + od->unit = SANE_UNIT_NONE; + od->size = sizeof (SANE_Word); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED; + if (init_enable_test_options == SANE_FALSE) + od->cap |= SANE_CAP_INACTIVE; + od->constraint_type = SANE_CONSTRAINT_WORD_LIST; + od->constraint.word_list = fixed_constraint_word_list; + test_device->val[opt_fixed_constraint_word_list].w = SANE_FIX (42.0); + + /* opt_string_group */ + od = &test_device->opt[opt_string_group]; + od->name = ""; + od->title = SANE_I18N ("String test options"); + od->desc = ""; + od->type = SANE_TYPE_GROUP; + od->unit = SANE_UNIT_NONE; + od->size = 0; + od->cap = 0; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_string_group].w = 0; + + /* opt_string */ + od = &test_device->opt[opt_string]; + od->name = "string"; + od->title = SANE_I18N ("(1/3) String"); + od->desc = SANE_I18N ("(1/3) String test option without constraint."); + od->type = SANE_TYPE_STRING; + od->unit = SANE_UNIT_NONE; + od->size = strlen (init_string) + 1; + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + if (init_enable_test_options == SANE_FALSE) + od->cap |= SANE_CAP_INACTIVE; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.string_list = 0; + test_device->val[opt_string].s = malloc (od->size); + if (!test_device->val[opt_string].s) + return SANE_STATUS_NO_MEM; + strcpy (test_device->val[opt_string].s, init_string); + + /* opt_string_constraint_string_list */ + od = &test_device->opt[opt_string_constraint_string_list]; + od->name = "string-constraint-string-list"; + od->title = SANE_I18N ("(2/3) String constraint string list"); + od->desc = SANE_I18N ("(2/3) String test option with string list " + "constraint."); + od->type = SANE_TYPE_STRING; + od->unit = SANE_UNIT_NONE; + od->size = max_string_size (string_constraint_string_list); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + if (init_enable_test_options == SANE_FALSE) + od->cap |= SANE_CAP_INACTIVE; + od->constraint_type = SANE_CONSTRAINT_STRING_LIST; + od->constraint.string_list = string_constraint_string_list; + test_device->val[opt_string_constraint_string_list].s = malloc (od->size); + if (!test_device->val[opt_string_constraint_string_list].s) + return SANE_STATUS_NO_MEM; + strcpy (test_device->val[opt_string_constraint_string_list].s, + init_string_constraint_string_list); + + /* opt_string_constraint_long_string_list */ + od = &test_device->opt[opt_string_constraint_long_string_list]; + od->name = "string-constraint-long-string-list"; + od->title = SANE_I18N ("(3/3) String constraint long string list"); + od->desc = SANE_I18N ("(3/3) String test option with string list " + "constraint. Contains some more entries..."); + od->type = SANE_TYPE_STRING; + od->unit = SANE_UNIT_NONE; + od->size = max_string_size (string_constraint_long_string_list); + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + if (init_enable_test_options == SANE_FALSE) + od->cap |= SANE_CAP_INACTIVE; + od->constraint_type = SANE_CONSTRAINT_STRING_LIST; + od->constraint.string_list = string_constraint_long_string_list; + test_device->val[opt_string_constraint_long_string_list].s = + malloc (od->size); + if (!test_device->val[opt_string_constraint_long_string_list].s) + return SANE_STATUS_NO_MEM; + strcpy (test_device->val[opt_string_constraint_long_string_list].s, + init_string_constraint_long_string_list); + + /* opt_button_group */ + od = &test_device->opt[opt_button_group]; + od->name = ""; + od->title = SANE_I18N ("Button test options"); + od->desc = ""; + od->type = SANE_TYPE_GROUP; + od->unit = SANE_UNIT_NONE; + od->size = 0; + od->cap = 0; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.range = 0; + test_device->val[opt_button_group].w = 0; + + /* opt_button */ + od = &test_device->opt[opt_button]; + od->name = "button"; + od->title = SANE_I18N ("(1/1) Button"); + od->desc = SANE_I18N ("(1/1) Button test option. Prints some text..."); + od->type = SANE_TYPE_BUTTON; + od->unit = SANE_UNIT_NONE; + od->size = 0; + od->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + if (init_enable_test_options == SANE_FALSE) + od->cap |= SANE_CAP_INACTIVE; + od->constraint_type = SANE_CONSTRAINT_NONE; + od->constraint.string_list = 0; + test_device->val[opt_button].w = 0; + + return SANE_STATUS_GOOD; +} + +static SANE_Status +read_option (SANE_String line, SANE_String option_string, + parameter_type p_type, void *value) +{ + SANE_String_Const cp; + SANE_Char *word, *end; + + word = 0; + + cp = sanei_config_get_string (line, &word); + + if (strcmp (word, option_string) != 0) + return SANE_STATUS_INVAL; + + free (word); + word = 0; + + switch (p_type) + { + case param_none: + return SANE_STATUS_GOOD; + case param_bool: + { + cp = sanei_config_get_string (cp, &word); + if (strlen (word) == 0) + { + DBG (3, "read_option: option `%s' requires parameter\n", + option_string); + return SANE_STATUS_INVAL; + } + if (strcmp (word, "true") != 0 && strcmp (word, "false") != 0) + { + DBG (3, "read_option: option `%s' requires parameter " + "`true' or `false'\n", option_string); + return SANE_STATUS_INVAL; + } + else if (strcmp (word, "true") == 0) + *(SANE_Bool *) value = SANE_TRUE; + else + *(SANE_Bool *) value = SANE_FALSE; + DBG (3, "read_option: set option `%s' to %s\n", option_string, + *(SANE_Bool *) value == SANE_TRUE ? "true" : "false"); + break; + } + case param_int: + { + SANE_Int int_value; + + cp = sanei_config_get_string (cp, &word); + errno = 0; + int_value = (SANE_Int) strtol (word, &end, 0); + if (end == word) + { + DBG (3, "read_option: option `%s' requires parameter\n", + option_string); + return SANE_STATUS_INVAL; + } + else if (errno) + { + DBG (3, "read_option: option `%s': can't parse parameter `%s' " + "(%s)\n", option_string, word, strerror (errno)); + return SANE_STATUS_INVAL; + } + else + { + DBG (3, "read_option: set option `%s' to %d\n", option_string, + int_value); + *(SANE_Int *) value = int_value; + } + break; + } + case param_fixed: + { + double double_value; + SANE_Fixed fixed_value; + + cp = sanei_config_get_string (cp, &word); + errno = 0; + double_value = strtod (word, &end); + if (end == word) + { + DBG (3, "read_option: option `%s' requires parameter\n", + option_string); + return SANE_STATUS_INVAL; + } + else if (errno) + { + DBG (3, "read_option: option `%s': can't parse parameter `%s' " + "(%s)\n", option_string, word, strerror (errno)); + return SANE_STATUS_INVAL; + } + else + { + DBG (3, "read_option: set option `%s' to %.0f\n", option_string, + double_value); + fixed_value = SANE_FIX (double_value); + *(SANE_Fixed *) value = fixed_value; + } + break; + } + case param_string: + { + cp = sanei_config_get_string (cp, &word); + if (strlen (word) == 0) + { + DBG (3, "read_option: option `%s' requires parameter\n", + option_string); + return SANE_STATUS_INVAL; + } + else + { + DBG (3, "read_option: set option `%s' to `%s'\n", option_string, + word); + *(SANE_String *) value = strdup (word); + } + break; + } + default: + DBG (1, "read_option: unknown param_type %d\n", p_type); + return SANE_STATUS_INVAL; + } /* switch */ + + if (word) + free (word); + word = 0; + return SANE_STATUS_GOOD; +} + +static SANE_Status +reader_process (Test_Device * test_device, SANE_Int fd) +{ + SANE_Status status; + SANE_Word byte_count = 0, bytes_total; + SANE_Byte *buffer = 0; + ssize_t bytes_written = 0; + size_t buffer_size = 0, write_count = 0; + + DBG (2, "(child) reader_process: test_device=%p, fd=%d\n", test_device, fd); + + bytes_total = test_device->lines * test_device->bytes_per_line; + status = init_picture_buffer (test_device, &buffer, &buffer_size); + if (status != SANE_STATUS_GOOD) + return status; + + DBG (2, "(child) reader_process: buffer=%p, buffersize=%d\n", + buffer, buffer_size); + + while (byte_count < bytes_total) + { + if (write_count == 0) + { + write_count = buffer_size; + if (byte_count + (SANE_Word) write_count > bytes_total) + write_count = bytes_total - byte_count; + + if (test_device->val[opt_read_delay].w == SANE_TRUE) + usleep (test_device->val[opt_read_delay_duration].w); + } + bytes_written = write (fd, buffer, write_count); + if (bytes_written < 0) + { + DBG (1, "(child) reader_process: write returned %s\n", + strerror (errno)); + return SANE_STATUS_IO_ERROR; + } + byte_count += bytes_written; + DBG (4, "(child) reader_process: wrote %d bytes of %d (%d total)\n", + bytes_written, write_count, byte_count); + write_count -= bytes_written; + } + + free (buffer); + DBG (4, "(child) reader_process: finished, wrote %d bytes, expected %d " + "bytes\n", byte_count, bytes_total); + return SANE_STATUS_GOOD; +} + +static SANE_Status +finish_pass (Test_Device * test_device) +{ + SANE_Status return_status = SANE_STATUS_GOOD; + + DBG (2, "finish_pass: test_device=%p\n", test_device); + if (test_device->reader_pid > 0) + { + int status; + + DBG (2, "finish_pass: terminating reader process %d\n", + test_device->reader_pid); + kill (test_device->reader_pid, SIGTERM); + waitpid (test_device->reader_pid, &status, 0); + + if (WIFEXITED (status)) + { + DBG (2, "finish_pass: reader process terminated with status %s\n", + sane_strstatus (WEXITSTATUS (status))); + if (WEXITSTATUS (status) != SANE_STATUS_GOOD) + return_status = WEXITSTATUS (status); + } + else if (WIFSIGNALED (status)) + { + DBG (2, "finish_pass: reader process was terminated by signal %d\n", + WTERMSIG (status)); + if (WTERMSIG (status) != 15) + return_status = SANE_STATUS_IO_ERROR; + } + else + DBG (1, "finish_pass: reader process terminated by unknown reason\n"); + + test_device->reader_pid = 0; + } + if (test_device->pipe > 0) + { + DBG (2, "finish_pass: closing pipe\n"); + fclose (test_device->pipe_handle); + close (test_device->pipe); + test_device->pipe = 0; + } + return return_status; +} + +static void +print_options (Test_Device * test_device) +{ + SANE_Option_Descriptor *od; + SANE_Word option_number; + SANE_Char caps[1024]; + + for (option_number = 0; option_number < num_options; option_number++) + { + od = &test_device->opt[option_number]; + DBG (0, "-----> number: %d\n", option_number); + DBG (0, " name: `%s'\n", od->name); + DBG (0, " title: `%s'\n", od->title); + DBG (0, " description: `%s'\n", od->desc); + DBG (0, " type: %s\n", + od->type == SANE_TYPE_BOOL ? "SANE_TYPE_BOOL" : + od->type == SANE_TYPE_INT ? "SANE_TYPE_INT" : + od->type == SANE_TYPE_FIXED ? "SANE_TYPE_FIXED" : + od->type == SANE_TYPE_STRING ? "SANE_TYPE_STRING" : + od->type == SANE_TYPE_BUTTON ? "SANE_TYPE_BUTTON" : + od->type == SANE_TYPE_GROUP ? "SANE_TYPE_GROUP" : "unknown"); + DBG (0, " unit: %s\n", + od->unit == SANE_UNIT_NONE ? "SANE_UNIT_NONE" : + od->unit == SANE_UNIT_PIXEL ? "SANE_UNIT_PIXEL" : + od->unit == SANE_UNIT_BIT ? "SANE_UNIT_BIT" : + od->unit == SANE_UNIT_MM ? "SANE_UNIT_MM" : + od->unit == SANE_UNIT_DPI ? "SANE_UNIT_DPI" : + od->unit == SANE_UNIT_PERCENT ? "SANE_UNIT_PERCENT" : + od->unit == SANE_UNIT_MICROSECOND ? "SANE_UNIT_MICROSECOND" : + "unknown"); + DBG (0, " size: %d\n", od->size); + caps[0] = '\0'; + if (od->cap & SANE_CAP_SOFT_SELECT) + strcat (caps, "SANE_CAP_SOFT_SELECT "); + if (od->cap & SANE_CAP_HARD_SELECT) + strcat (caps, "SANE_CAP_HARD_SELECT "); + if (od->cap & SANE_CAP_SOFT_DETECT) + strcat (caps, "SANE_CAP_SOFT_DETECT "); + if (od->cap & SANE_CAP_EMULATED) + strcat (caps, "SANE_CAP_EMULATED "); + if (od->cap & SANE_CAP_AUTOMATIC) + strcat (caps, "SANE_CAP_AUTOMATIC "); + if (od->cap & SANE_CAP_INACTIVE) + strcat (caps, "SANE_CAP_INACTIVE "); + if (od->cap & SANE_CAP_ADVANCED) + strcat (caps, "SANE_CAP_ADVANCED "); + DBG (0, " capabilities: %s\n", caps); + DBG (0, "constraint type: %s\n", + od->constraint_type == SANE_CONSTRAINT_NONE ? + "SANE_CONSTRAINT_NONE" : + od->constraint_type == SANE_CONSTRAINT_RANGE ? + "SANE_CONSTRAINT_RANGE" : + od->constraint_type == SANE_CONSTRAINT_WORD_LIST ? + "SANE_CONSTRAINT_WORD_LIST" : + od->constraint_type == SANE_CONSTRAINT_STRING_LIST ? + "SANE_CONSTRAINT_STRING_LIST" : "unknown"); + } +} + +/***************************** SANE API ****************************/ + + +SANE_Status +sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) +{ + FILE *fp; + SANE_Int linenumber; + SANE_Char line[PATH_MAX], *word; + SANE_String_Const cp; + SANE_Device *sane_device; + Test_Device *test_device, *previous_device; + SANE_Int num; + + DBG_INIT (); + + test_device = 0; + previous_device = 0; + + DBG (2, "sane_init: version_code= %p, authorize=%p\n", version_code, + authorize); + DBG (1, "sane_init: SANE test backend version %d.%d.%d from %s\n", + V_MAJOR, V_MINOR, BUILD, PACKAGE_VERSION); + + if (version_code) + *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, BUILD); + + fp = sanei_config_open (TEST_CONFIG_FILE); + if (fp) + { + linenumber = 0; + DBG (4, "sane_init: reading config file `%s'\n", TEST_CONFIG_FILE); + while (sanei_config_read (line, sizeof (line), fp)) + { + word = 0; + linenumber++; + + cp = sanei_config_get_string (line, &word); + if (!word || cp == line) + { + DBG (5, + "sane_init: config file line %3d: ignoring empty line\n", + linenumber); + if (word) + free (word); + continue; + } + if (word[0] == '#') + { + DBG (5, + "sane_init: config file line %3d: ignoring comment line\n", + linenumber); + free (word); + continue; + } + + DBG (5, "sane_init: config file line %3d: `%s'\n", + linenumber, line); + if (read_option (line, "number_of_devices", param_int, + &init_number_of_devices) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "mode", param_string, + &init_mode) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "hand-scanner", param_bool, + &init_hand_scanner) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "three-pass", param_bool, + &init_three_pass) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "three-pass-order", param_string, + &init_three_pass_order) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "resolution_min", param_fixed, + &resolution_range.min) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "resolution_max", param_fixed, + &resolution_range.max) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "resolution_quant", param_fixed, + &resolution_range.quant) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "resolution", param_fixed, + &init_resolution) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "depth", param_int, + &init_depth) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "test-picture", param_string, + &init_test_picture) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "read-limit", param_bool, + &init_read_limit) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "read-limit-size", param_int, + &init_read_limit_size) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "read-delay", param_bool, + &init_read_delay) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "read-delay-duration", param_int, + &init_read_delay_duration) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "read-status-code", param_string, + &init_read_status_code) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "fuzzy-parameters", param_bool, + &init_fuzzy_parameters) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "non-blocking", param_bool, + &init_non_blocking) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "select-fd", param_bool, + &init_select_fd) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "enable-test-options", param_bool, + &init_enable_test_options) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "geometry_min", param_fixed, + &geometry_range.min) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "geometry_max", param_fixed, + &geometry_range.max) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "geometry_quant", param_fixed, + &geometry_range.quant) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "tl_x", param_fixed, + &init_tl_x) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "tl_y", param_fixed, + &init_tl_y) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "br_x", param_fixed, + &init_br_x) == SANE_STATUS_GOOD) + continue; + if (read_option (line, "br_y", param_fixed, + &init_br_y) == SANE_STATUS_GOOD) + continue; + + DBG (3, "sane-init: I don't know how to handle option `%s'\n", + word); + } /* while */ + fclose (fp); + } /* if */ + else + { + DBG (3, "sane_init: couldn't find config file (%s), using default " + "settings\n", TEST_CONFIG_FILE); + } + + /* create devices */ + sane_device_list = + malloc ((init_number_of_devices + 1) * sizeof (sane_device)); + if (!sane_device_list) + return SANE_STATUS_NO_MEM; + for (num = 0; num < init_number_of_devices; num++) + { + SANE_Char number_string[PATH_MAX]; + + test_device = malloc (sizeof (*test_device)); + if (!test_device) + return SANE_STATUS_NO_MEM; + test_device->sane.vendor = "Noname"; + test_device->sane.type = "virtual device"; + test_device->sane.model = "frontend-tester"; + snprintf (number_string, sizeof (number_string), "%d", num); + number_string[sizeof (number_string) - 1] = '\0'; + test_device->name = strdup (number_string); + if (!test_device->name) + return SANE_STATUS_NO_MEM; + test_device->sane.name = test_device->name; + if (previous_device) + previous_device->next = test_device; + previous_device = test_device; + if (num == 0) + first_test_device = test_device; + sane_device_list[num] = &test_device->sane; + test_device->open = SANE_FALSE; + test_device->eof = SANE_FALSE; + test_device->scanning = SANE_FALSE; + test_device->cancelled = SANE_FALSE; + test_device->reader_pid = 0; + test_device->pipe = 0; + DBG (4, "sane_init: new device: `%s' is a %s %s %s\n", + test_device->sane.name, test_device->sane.vendor, + test_device->sane.model, test_device->sane.type); + } + test_device->next = 0; + sane_device_list[num] = 0; + srand (time (NULL)); + random_factor = ((double) rand ()) / RAND_MAX + 0.5; + return SANE_STATUS_GOOD; +} + +void +sane_exit (void) +{ + Test_Device *test_device, *previous_device; + + DBG (2, "sane_exit\n"); + test_device = first_test_device; + while (test_device) + { + DBG (4, "sane_exit: freeing device %s\n", test_device->name); + previous_device = test_device; + test_device = test_device->next; + if (previous_device->name) + free (previous_device->name); + free (previous_device); + } + DBG (4, "sane_exit: freeing device list\n"); + if (sane_device_list) + free (sane_device_list); + sane_device_list = NULL; + first_test_device = NULL; + return; +} + + +SANE_Status +sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) +{ + + DBG (2, "sane_get_devices: device_list=%p, local_only=%d\n", + device_list, local_only); + if (!device_list) + { + DBG (1, "sane_get_devices: device_list == 0\n"); + return SANE_STATUS_INVAL; + } + *device_list = (const SANE_Device **) sane_device_list; + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_open (SANE_String_Const devicename, SANE_Handle * handle) +{ + Test_Device *test_device = first_test_device; + SANE_Status status; + + DBG (2, "sane_open: devicename = \"%s\", handle=%p\n", devicename, handle); + + if (!handle) + { + DBG (1, "sane_open: handle == 0\n"); + return SANE_STATUS_INVAL; + } + + if (!devicename || strlen (devicename) == 0) + { + DBG (2, "sane_open: device name NULL or empty\n"); + } + else + { + for (test_device = first_test_device; test_device; + test_device = test_device->next) + { + if (strcmp (devicename, test_device->name) == 0) + break; + } + } + if (!test_device) + { + DBG (1, "sane_open: device `%s' not found\n", devicename); + return SANE_STATUS_INVAL; + } + if (test_device->open) + { + DBG (1, "sane_open: device `%s' already open\n", devicename); + return SANE_STATUS_DEVICE_BUSY; + } + DBG (2, "sane_open: opening device `%s', handle = %p\n", test_device->name, + test_device); + test_device->open = SANE_TRUE; + *handle = test_device; + + status = init_options (test_device); + if (status != SANE_STATUS_GOOD) + return status; + + test_device->open = SANE_TRUE; + test_device->scanning = SANE_FALSE; + test_device->cancelled = SANE_FALSE; + test_device->eof = SANE_FALSE; + test_device->bytes_total = 0; + test_device->pass = 0; + + return SANE_STATUS_GOOD; +} + +void +sane_close (SANE_Handle handle) +{ + Test_Device *test_device = handle; + + DBG (2, "sane_close: handle=%p\n", handle); + if (!check_handle (handle)) + { + DBG (1, "sane_close: handle %p unknown\n", handle); + return; + } + if (!test_device->open) + { + DBG (1, "sane_close: handle %p not open\n", handle); + return; + } + test_device->open = SANE_FALSE; + return; +} + +const SANE_Option_Descriptor * +sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) +{ + Test_Device *test_device = handle; + + DBG (4, "sane_get_option_descriptor: handle=%p, option = %d\n", handle, + option); + if (!check_handle (handle)) + { + DBG (1, "sane_get_option_descriptor: handle %p unknown\n", handle); + return 0; + } + if (!test_device->open) + { + DBG (1, "sane_get_option_descriptor: not open\n"); + return 0; + } + if (option < 0 || option >= num_options) + { + DBG (3, "sane_get_option_descriptor: option < 0 || " + "option > num_options\n"); + return 0; + } + + return &test_device->opt[option]; +} + +SANE_Status +sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, + void *value, SANE_Int * info) +{ + Test_Device *test_device = handle; + SANE_Int myinfo = 0; + SANE_Status status; + + DBG (4, "sane_control_option: handle=%p, opt=%d, act=%d, val=%p, info=%p\n", + handle, option, action, value, info); + + if (!check_handle (handle)) + { + DBG (1, "sane_control_option: handle %p unknown\n", handle); + return SANE_STATUS_INVAL; + } + if (!test_device->open) + { + DBG (1, "sane_control_option: not open\n"); + return SANE_STATUS_INVAL; + } + if (test_device->scanning) + { + DBG (1, "sane_control_option: is scanning\n"); + return SANE_STATUS_INVAL; + } + if (option < 0 || option >= num_options) + { + DBG (1, "sane_control_option: option < 0 || option > num_options\n"); + return SANE_STATUS_INVAL; + } + + if (!SANE_OPTION_IS_ACTIVE (test_device->opt[option].cap)) + { + DBG (1, "sane_control_option: option is inactive\n"); + return SANE_STATUS_INVAL; + } + + if (test_device->opt[option].type == SANE_TYPE_GROUP) + { + DBG (1, "sane_control_option: option is a group\n"); + return SANE_STATUS_INVAL; + } + + switch (action) + { + case SANE_ACTION_SET_AUTO: + if (!SANE_OPTION_IS_SETTABLE (test_device->opt[option].cap)) + { + DBG (1, "sane_control_option: option is not setable\n"); + return SANE_STATUS_INVAL; + } + if (!(test_device->opt[option].cap & SANE_CAP_AUTOMATIC)) + { + DBG (1, "sane_control_option: option is not automatically " + "setable\n"); + return SANE_STATUS_INVAL; + } + switch (option) + { + case opt_bool_soft_select_soft_detect_auto: + test_device->val[option].w = SANE_TRUE; + DBG (4, "sane_control_option: set option %d (%s) automatically " + "to %s\n", option, test_device->opt[option].name, + test_device->val[option].w == SANE_TRUE ? "true" : "false"); + break; + + default: + DBG (1, "sane_control_option: trying to automatically set " + "unexpected option\n"); + return SANE_STATUS_INVAL; + } + break; + + case SANE_ACTION_SET_VALUE: + if (!SANE_OPTION_IS_SETTABLE (test_device->opt[option].cap)) + { + DBG (1, "sane_control_option: option is not setable\n"); + return SANE_STATUS_INVAL; + } + status = sanei_constrain_value (&test_device->opt[option], + value, &myinfo); + if (status != SANE_STATUS_GOOD) + { + DBG (3, "sane_control_option: sanei_constrain_value returned %s\n", + sane_strstatus (status)); + return status; + } + switch (option) + { + case opt_tl_x: /* Fixed with parameter reloading */ + case opt_tl_y: + case opt_br_x: + case opt_br_y: + case opt_resolution: + if (test_device->val[option].w == *(SANE_Fixed *) value) + { + DBG (4, "sane_control_option: option %d (%s) not changed\n", + option, test_device->opt[option].name); + break; + } + test_device->val[option].w = *(SANE_Fixed *) value; + myinfo |= SANE_INFO_RELOAD_PARAMS; + DBG (4, "sane_control_option: set option %d (%s) to %.0f %s\n", + option, test_device->opt[option].name, + SANE_UNFIX (*(SANE_Fixed *) value), + test_device->opt[option].unit == SANE_UNIT_MM ? "mm" : "dpi"); + break; + case opt_fixed: /* Fixed */ + case opt_fixed_constraint_range: + if (test_device->val[option].w == *(SANE_Fixed *) value) + { + DBG (4, "sane_control_option: option %d (%s) not changed\n", + option, test_device->opt[option].name); + break; + } + test_device->val[option].w = *(SANE_Fixed *) value; + DBG (4, "sane_control_option: set option %d (%s) to %.0f\n", + option, test_device->opt[option].name, + SANE_UNFIX (*(SANE_Fixed *) value)); + break; + case opt_read_limit_size: /* Int */ + case opt_read_delay_duration: + case opt_int: + case opt_int_constraint_range: + if (test_device->val[option].w == *(SANE_Int *) value) + { + DBG (4, "sane_control_option: option %d (%s) not changed\n", + option, test_device->opt[option].name); + break; + } + test_device->val[option].w = *(SANE_Int *) value; + DBG (4, "sane_control_option: set option %d (%s) to %d\n", + option, test_device->opt[option].name, *(SANE_Int *) value); + break; + case opt_fuzzy_parameters: /* Bool with parameter reloading */ + if (test_device->val[option].w == *(SANE_Bool *) value) + { + DBG (4, "sane_control_option: option %d (%s) not changed\n", + option, test_device->opt[option].name); + break; + } + test_device->val[option].w = *(SANE_Bool *) value; + myinfo |= SANE_INFO_RELOAD_PARAMS; + DBG (4, "sane_control_option: set option %d (%s) to %s\n", + option, test_device->opt[option].name, + *(SANE_Bool *) value == SANE_TRUE ? "true" : "false"); + break; + case opt_non_blocking: /* Bool */ + case opt_select_fd: + case opt_bool_soft_select_soft_detect: + case opt_bool_soft_select_soft_detect_auto: + case opt_bool_soft_select_soft_detect_emulated: + if (test_device->val[option].w == *(SANE_Bool *) value) + { + DBG (4, "sane_control_option: option %d (%s) not changed\n", + option, test_device->opt[option].name); + break; + } + test_device->val[option].w = *(SANE_Bool *) value; + DBG (4, "sane_control_option: set option %d (%s) to %s\n", + option, test_device->opt[option].name, + *(SANE_Bool *) value == SANE_TRUE ? "true" : "false"); + break; + case opt_depth: /* Word list with parameter reloading */ + if (test_device->val[option].w == *(SANE_Int *) value) + { + DBG (4, "sane_control_option: option %d (%s) not changed\n", + option, test_device->opt[option].name); + break; + } + test_device->val[option].w = *(SANE_Int *) value; + myinfo |= SANE_INFO_RELOAD_PARAMS; + DBG (4, "sane_control_option: set option %d (%s) to %d\n", + option, test_device->opt[option].name, *(SANE_Int *) value); + break; + case opt_three_pass_order: /* String list with parameter reloading */ + if (strcmp (test_device->val[option].s, value) == 0) + { + DBG (4, "sane_control_option: option %d (%s) not changed\n", + option, test_device->opt[option].name); + break; + } + strcpy (test_device->val[option].s, (SANE_String) value); + myinfo |= SANE_INFO_RELOAD_PARAMS; + DBG (4, "sane_control_option: set option %d (%s) to %s\n", + option, test_device->opt[option].name, (SANE_String) value); + break; + case opt_int_constraint_word_list: /* Word list */ + case opt_fixed_constraint_word_list: + if (test_device->val[option].w == *(SANE_Int *) value) + { + DBG (4, "sane_control_option: option %d (%s) not changed\n", + option, test_device->opt[option].name); + break; + } + test_device->val[option].w = *(SANE_Int *) value; + DBG (4, "sane_control_option: set option %d (%s) to %d\n", + option, test_device->opt[option].name, *(SANE_Int *) value); + break; + case opt_read_status_code: /* String (list) */ + case opt_test_picture: + case opt_string: + case opt_string_constraint_string_list: + case opt_string_constraint_long_string_list: + if (strcmp (test_device->val[option].s, value) == 0) + { + DBG (4, "sane_control_option: option %d (%s) not changed\n", + option, test_device->opt[option].name); + break; + } + strcpy (test_device->val[option].s, (SANE_String) value); + DBG (4, "sane_control_option: set option %d (%s) to `%s'\n", + option, test_device->opt[option].name, (SANE_String) value); + break; + case opt_int_array: /* Word array */ + case opt_int_array_constraint_range: + case opt_int_array_constraint_word_list: + memcpy (test_device->val[option].wa, value, + test_device->opt[option].size); + DBG (4, "sane_control_option: set option %d (%s) to %p\n", + option, test_device->opt[option].name, (void *) value); + break; + /* options with side-effects */ + case opt_print_options: + DBG (4, "sane_control_option: set option %d (%s)\n", + option, test_device->opt[option].name); + print_options (test_device); + break; + case opt_button: + DBG (0, "Yes! You pressed me!\n"); + DBG (4, "sane_control_option: set option %d (%s)\n", + option, test_device->opt[option].name); + break; + case opt_mode: + if (strcmp (test_device->val[option].s, value) == 0) + { + DBG (4, "sane_control_option: option %d (%s) not changed\n", + option, test_device->opt[option].name); + break; + } + strcpy (test_device->val[option].s, (SANE_String) value); + myinfo |= SANE_INFO_RELOAD_PARAMS; + myinfo |= SANE_INFO_RELOAD_OPTIONS; + if (strcmp (test_device->val[option].s, "Color") == 0) + { + test_device->opt[opt_three_pass].cap &= ~SANE_CAP_INACTIVE; + if (test_device->val[opt_three_pass].w == SANE_TRUE) + test_device->opt[opt_three_pass_order].cap + &= ~SANE_CAP_INACTIVE; + } + else + { + test_device->opt[opt_three_pass].cap |= SANE_CAP_INACTIVE; + test_device->opt[opt_three_pass_order].cap |= SANE_CAP_INACTIVE; + } + DBG (4, "sane_control_option: set option %d (%s) to %s\n", + option, test_device->opt[option].name, (SANE_String) value); + break; + case opt_three_pass: + if (test_device->val[option].w == *(SANE_Bool *) value) + { + DBG (4, "sane_control_option: option %d (%s) not changed\n", + option, test_device->opt[option].name); + break; + } + test_device->val[option].w = *(SANE_Bool *) value; + myinfo |= SANE_INFO_RELOAD_PARAMS; + myinfo |= SANE_INFO_RELOAD_OPTIONS; + if (test_device->val[option].w == SANE_TRUE) + test_device->opt[opt_three_pass_order].cap &= ~SANE_CAP_INACTIVE; + else + test_device->opt[opt_three_pass_order].cap |= SANE_CAP_INACTIVE; + DBG (4, "sane_control_option: set option %d (%s) to %s\n", + option, test_device->opt[option].name, + *(SANE_Bool *) value == SANE_TRUE ? "true" : "false"); + break; + case opt_hand_scanner: + if (test_device->val[option].w == *(SANE_Bool *) value) + { + DBG (4, "sane_control_option: option %d (%s) not changed\n", + option, test_device->opt[option].name); + break; + } + test_device->val[option].w = *(SANE_Bool *) value; + myinfo |= SANE_INFO_RELOAD_PARAMS; + myinfo |= SANE_INFO_RELOAD_OPTIONS; + if (test_device->val[option].w == SANE_TRUE) + { + test_device->opt[opt_tl_x].cap |= SANE_CAP_INACTIVE; + test_device->opt[opt_tl_y].cap |= SANE_CAP_INACTIVE; + test_device->opt[opt_br_x].cap |= SANE_CAP_INACTIVE; + test_device->opt[opt_br_y].cap |= SANE_CAP_INACTIVE; + } + else + { + test_device->opt[opt_tl_x].cap &= ~SANE_CAP_INACTIVE; + test_device->opt[opt_tl_y].cap &= ~SANE_CAP_INACTIVE; + test_device->opt[opt_br_x].cap &= ~SANE_CAP_INACTIVE; + test_device->opt[opt_br_y].cap &= ~SANE_CAP_INACTIVE; + } + DBG (4, "sane_control_option: set option %d (%s) to %s\n", + option, test_device->opt[option].name, + *(SANE_Bool *) value == SANE_TRUE ? "true" : "false"); + break; + case opt_read_limit: + if (test_device->val[option].w == *(SANE_Bool *) value) + { + DBG (4, "sane_control_option: option %d (%s) not changed\n", + option, test_device->opt[option].name); + break; + } + test_device->val[option].w = *(SANE_Bool *) value; + myinfo |= SANE_INFO_RELOAD_OPTIONS; + if (test_device->val[option].w == SANE_TRUE) + test_device->opt[opt_read_limit_size].cap &= ~SANE_CAP_INACTIVE; + else + test_device->opt[opt_read_limit_size].cap |= SANE_CAP_INACTIVE; + DBG (4, "sane_control_option: set option %d (%s) to %s\n", + option, test_device->opt[option].name, + *(SANE_Bool *) value == SANE_TRUE ? "true" : "false"); + break; + case opt_read_delay: + if (test_device->val[option].w == *(SANE_Bool *) value) + { + DBG (4, "sane_control_option: option %d (%s) not changed\n", + option, test_device->opt[option].name); + break; + } + test_device->val[option].w = *(SANE_Bool *) value; + myinfo |= SANE_INFO_RELOAD_OPTIONS; + if (test_device->val[option].w == SANE_TRUE) + test_device->opt[opt_read_delay_duration].cap + &= ~SANE_CAP_INACTIVE; + else + test_device->opt[opt_read_delay_duration].cap |= + SANE_CAP_INACTIVE; + DBG (4, "sane_control_option: set option %d (%s) to %s\n", option, + test_device->opt[option].name, + *(SANE_Bool *) value == SANE_TRUE ? "true" : "false"); + break; + case opt_enable_test_options: + { + int option_number; + if (test_device->val[option].w == *(SANE_Bool *) value) + { + DBG (4, "sane_control_option: option %d (%s) not changed\n", + option, test_device->opt[option].name); + break; + } + test_device->val[option].w = *(SANE_Bool *) value; + myinfo |= SANE_INFO_RELOAD_OPTIONS; + for (option_number = opt_bool_soft_select_soft_detect; + option_number < num_options; option_number++) + { + if (test_device->opt[option_number].type == SANE_TYPE_GROUP) + continue; + if (test_device->val[option].w == SANE_TRUE) + test_device->opt[option_number].cap &= ~SANE_CAP_INACTIVE; + else + test_device->opt[option_number].cap |= SANE_CAP_INACTIVE; + } + DBG (4, "sane_control_option: set option %d (%s) to %s\n", + option, test_device->opt[option].name, + *(SANE_Bool *) value == SANE_TRUE ? "true" : "false"); + break; + } + default: + DBG (1, "sane_control_option: trying to set unexpected option\n"); + return SANE_STATUS_INVAL; + } + break; + + case SANE_ACTION_GET_VALUE: + switch (option) + { + case opt_num_opts: + *(SANE_Word *) value = num_options; + DBG (4, "sane_control_option: get option 0, value = %d\n", + num_options); + break; + case opt_tl_x: /* Fixed options */ + case opt_tl_y: + case opt_br_x: + case opt_br_y: + case opt_resolution: + case opt_fixed: + case opt_fixed_constraint_range: + case opt_fixed_constraint_word_list: + { + *(SANE_Fixed *) value = test_device->val[option].w; + DBG (4, + "sane_control_option: get option %d (%s), value=%.1f %s\n", + option, test_device->opt[option].name, + SANE_UNFIX (*(SANE_Fixed *) value), + test_device->opt[option].unit == + SANE_UNIT_MM ? "mm" : SANE_UNIT_DPI ? "dpi" : ""); + break; + } + case opt_hand_scanner: /* Bool options */ + case opt_three_pass: + case opt_read_limit: + case opt_read_delay: + case opt_fuzzy_parameters: + case opt_non_blocking: + case opt_select_fd: + case opt_bool_soft_select_soft_detect: + case opt_bool_hard_select_soft_detect: + case opt_bool_soft_detect: + case opt_enable_test_options: + case opt_bool_soft_select_soft_detect_emulated: + case opt_bool_soft_select_soft_detect_auto: + *(SANE_Bool *) value = test_device->val[option].w; + DBG (4, + "sane_control_option: get option %d (%s), value=%s\n", + option, test_device->opt[option].name, + *(SANE_Bool *) value == SANE_TRUE ? "true" : "false"); + break; + case opt_mode: /* String (list) options */ + case opt_three_pass_order: + case opt_read_status_code: + case opt_test_picture: + case opt_string: + case opt_string_constraint_string_list: + case opt_string_constraint_long_string_list: + strcpy (value, test_device->val[option].s); + DBG (4, "sane_control_option: get option %d (%s), value=`%s'\n", + option, test_device->opt[option].name, (SANE_String) value); + break; + case opt_depth: /* Int + word list options */ + case opt_read_limit_size: + case opt_read_delay_duration: + case opt_int: + case opt_int_constraint_range: + case opt_int_constraint_word_list: + *(SANE_Int *) value = test_device->val[option].w; + DBG (4, "sane_control_option: get option %d (%s), value=%d\n", + option, test_device->opt[option].name, *(SANE_Int *) value); + break; + case opt_int_array: /* Int array */ + case opt_int_array_constraint_range: + case opt_int_array_constraint_word_list: + memcpy (value, test_device->val[option].wa, + test_device->opt[option].size); + DBG (4, "sane_control_option: get option %d (%s), value=%p\n", + option, test_device->opt[option].name, (void *) value); + break; + default: + DBG (1, "sane_control_option: trying to get unexpected option\n"); + return SANE_STATUS_INVAL; + } + break; + default: + DBG (1, "sane_control_option: trying unexpected action %d\n", action); + return SANE_STATUS_INVAL; + } + if (info) + *info = myinfo; + DBG (4, "sane_control_option: finished, info=%s %s %s \n", + myinfo & SANE_INFO_INEXACT ? "inexact" : "", + myinfo & SANE_INFO_RELOAD_PARAMS ? "reload_parameters" : "", + myinfo & SANE_INFO_RELOAD_OPTIONS ? "reload_options" : ""); + return SANE_STATUS_GOOD; +} + + +SANE_Status +sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) +{ + Test_Device *test_device = handle; + SANE_Parameters *p; + double res, tl_x = 0, tl_y = 0, br_x = 0, br_y = 0; + SANE_String text_format, mode; + + DBG (2, "sane_get_parameters: handle=%p, params=%p\n", handle, params); + if (!check_handle (handle)) + { + DBG (1, "sane_get_parameters: handle %p unknown\n", handle); + return SANE_STATUS_INVAL; + } + if (!test_device->open) + { + DBG (1, "sane_get_parameters: handle %p not open\n", handle); + return SANE_STATUS_INVAL; + } + + res = SANE_UNFIX (test_device->val[opt_resolution].w); + mode = test_device->val[opt_mode].s; + p = &test_device->params; + p->depth = test_device->val[opt_depth].w; + + if (test_device->val[opt_hand_scanner].w == SANE_TRUE) + { + tl_x = 0.0; + br_x = 110.0; + tl_y = 0.0; + br_y = 170.0; + p->lines = -1; + test_device->lines = (SANE_Word) (res * (br_y - tl_y) / MM_PER_INCH); + } + else + { + tl_x = SANE_UNFIX (test_device->val[opt_tl_x].w); + tl_y = SANE_UNFIX (test_device->val[opt_tl_y].w); + br_x = SANE_UNFIX (test_device->val[opt_br_x].w); + br_y = SANE_UNFIX (test_device->val[opt_br_y].w); + if (tl_x > br_x) + swap_double (&tl_x, &br_x); + if (tl_y > br_y) + swap_double (&tl_y, &br_y); + test_device->lines = (SANE_Word) (res * (br_y - tl_y) / MM_PER_INCH); + if (test_device->lines < 1) + test_device->lines = 1; + p->lines = test_device->lines; + if (test_device->val[opt_fuzzy_parameters].w == SANE_TRUE + && test_device->scanning == SANE_FALSE) + p->lines *= random_factor; + } + + if (strcmp (mode, "Gray") == 0) + { + p->format = SANE_FRAME_GRAY; + p->last_frame = SANE_TRUE; + } + else /* Color */ + { + if (test_device->val[opt_three_pass].w == SANE_TRUE) + { + if (test_device->val[opt_three_pass_order].s[test_device->pass] + == 'R') + p->format = SANE_FRAME_RED; + else if (test_device->val[opt_three_pass_order].s[test_device->pass] + == 'G') + p->format = SANE_FRAME_GREEN; + else + p->format = SANE_FRAME_BLUE; + if (test_device->pass > 1) + p->last_frame = SANE_TRUE; + else + p->last_frame = SANE_FALSE; + } + else + { + p->format = SANE_FRAME_RGB; + p->last_frame = SANE_TRUE; + } + } + + p->pixels_per_line = (SANE_Int) (res * (br_x - tl_x) / MM_PER_INCH); + if (p->pixels_per_line < 1) + p->pixels_per_line = 1; + + if (p->depth == 1) + { + /* Make sure ppl can be divided by 8 */ + p->pixels_per_line /= 8; + p->pixels_per_line *= 8; + if (p->pixels_per_line < 8) + p->pixels_per_line = 8; + } + test_device->bytes_per_line = p->pixels_per_line * p->depth / 8;; + if (test_device->val[opt_fuzzy_parameters].w == SANE_TRUE + && test_device->scanning == SANE_FALSE) + p->pixels_per_line *= random_factor; + + p->bytes_per_line = p->pixels_per_line * p->depth / 8; + if (p->format == SANE_FRAME_RGB) + { + p->bytes_per_line *= 3; + test_device->bytes_per_line *= 3; + } + + switch (p->format) + { + case SANE_FRAME_GRAY: + text_format = "gray"; + break; + case SANE_FRAME_RGB: + text_format = "rgb"; + break; + case SANE_FRAME_RED: + text_format = "red"; + break; + case SANE_FRAME_GREEN: + text_format = "green"; + break; + case SANE_FRAME_BLUE: + text_format = "blue"; + break; + default: + text_format = "unknown"; + break; + } + + DBG (2, "sane_get_parameters: format=%s\n", text_format); + DBG (2, "sane_get_parameters: last_frame=%s\n", + p->last_frame ? "true" : "false"); + DBG (2, "sane_get_parameters: lines=%d\n", p->lines); + DBG (2, "sane_get_parameters: depth=%d\n", p->depth); + DBG (2, "sane_get_parameters: pixels_per_line=%d\n", p->pixels_per_line); + DBG (2, "sane_get_parameters: bytes_per_line=%d\n", p->bytes_per_line); + + if (params) + *params = *p; + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_start (SANE_Handle handle) +{ + Test_Device *test_device = handle; + int pipe_descriptor[2]; + + DBG (2, "sane_start: handle=%p\n", handle); + if (!check_handle (handle)) + { + DBG (1, "sane_start: handle %p unknown\n", handle); + return SANE_STATUS_INVAL; + } + if (!test_device->open) + { + DBG (1, "sane_start: not open\n"); + return SANE_STATUS_INVAL; + } + if (test_device->scanning + && (test_device->val[opt_three_pass].w == SANE_FALSE + && strcmp (test_device->val[opt_mode].s, "Color") == 0)) + { + DBG (1, "sane_start: already scanning\n"); + return SANE_STATUS_INVAL; + } + if (strcmp (test_device->val[opt_mode].s, "Color") == 0 + && test_device->val[opt_three_pass].w == SANE_TRUE + && test_device->pass > 2) + { + DBG (1, "sane_start: already in last pass of three\n"); + return SANE_STATUS_INVAL; + } + test_device->scanning = SANE_TRUE; + sane_get_parameters (handle, 0); + if (test_device->params.lines == 0) + { + DBG (1, "sane_start: lines == 0\n"); + test_device->scanning = SANE_FALSE; + return SANE_STATUS_INVAL; + } + if (test_device->params.pixels_per_line == 0) + { + DBG (1, "sane_start: pixels_per_line == 0\n"); + test_device->scanning = SANE_FALSE; + return SANE_STATUS_INVAL; + } + if (test_device->params.bytes_per_line == 0) + { + DBG (1, "sane_start: bytes_per_line == 0\n"); + test_device->scanning = SANE_FALSE; + return SANE_STATUS_INVAL; + } + test_device->scanning = SANE_TRUE; + test_device->cancelled = SANE_FALSE; + test_device->eof = SANE_FALSE; + test_device->bytes_total = 0; + + if (pipe (pipe_descriptor) < 0) + { + DBG (1, "sane_start: pipe failed (%s)\n", strerror (errno)); + return SANE_STATUS_IO_ERROR; + } + + test_device->reader_pid = fork (); + if (test_device->reader_pid == 0) /* child */ + { + SANE_Status status; + sigset_t ignore_set; + struct SIGACTION act; + + /* block all signals but SIGTERM */ + sigfillset (&ignore_set); + sigdelset (&ignore_set, SIGTERM); + sigprocmask (SIG_SETMASK, &ignore_set, 0); + memset (&act, 0, sizeof (act)); + sigaction (SIGTERM, &act, 0); + + close (pipe_descriptor[0]); + status = reader_process (test_device, pipe_descriptor[1]); + DBG (2, "(child) sane_start: reader_process returned %s\n", + sane_strstatus (status)); + _exit (status); + } + else if (test_device->reader_pid < 0) + { + DBG (1, "sane_start: fork failed (%s)\n", strerror (errno)); + return SANE_STATUS_NO_MEM; + } + /* parent */ + close (pipe_descriptor[1]); + test_device->pipe = pipe_descriptor[0]; + test_device->pipe_handle = fdopen (pipe_descriptor[0], "r"); + + return SANE_STATUS_GOOD; +} + + +SANE_Status +sane_read (SANE_Handle handle, SANE_Byte * data, + SANE_Int max_length, SANE_Int * length) +{ + Test_Device *test_device = handle; + SANE_Int max_scan_length; + ssize_t bytes_read; + size_t read_count; + + DBG (4, "sane_read: handle=%p, data=%p, max_length = %d, length=%p\n", + handle, data, max_length, length); + + if (!check_handle (handle)) + { + DBG (1, "sane_read: handle %p unknown\n", handle); + return SANE_STATUS_INVAL; + } + if (!length) + { + DBG (1, "sane_read: length == NULL\n"); + return SANE_STATUS_INVAL; + } + + if (strcmp (test_device->val[opt_read_status_code].s, "Default") != 0) + { + SANE_String_Const sc = test_device->val[opt_read_status_code].s; + DBG (3, "sane_read: setting return status to %s\n", sc); + if (strcmp (sc, "SANE_STATUS_UNSUPPORTED") == 0) + return SANE_STATUS_UNSUPPORTED; + if (strcmp (sc, "SANE_STATUS_CANCELLED") == 0) + return SANE_STATUS_CANCELLED; + if (strcmp (sc, "SANE_STATUS_DEVICE_BUSY") == 0) + return SANE_STATUS_DEVICE_BUSY; + if (strcmp (sc, "SANE_STATUS_INVAL") == 0) + return SANE_STATUS_INVAL; + if (strcmp (sc, "SANE_STATUS_EOF") == 0) + return SANE_STATUS_EOF; + if (strcmp (sc, "SANE_STATUS_JAMMED") == 0) + return SANE_STATUS_JAMMED; + if (strcmp (sc, "SANE_STATUS_NO_DOCS") == 0) + return SANE_STATUS_NO_DOCS; + if (strcmp (sc, "SANE_STATUS_COVER_OPEN") == 0) + return SANE_STATUS_COVER_OPEN; + if (strcmp (sc, "SANE_STATUS_IO_ERROR") == 0) + return SANE_STATUS_IO_ERROR; + if (strcmp (sc, "SANE_STATUS_NO_MEM") == 0) + return SANE_STATUS_NO_MEM; + if (strcmp (sc, "SANE_STATUS_ACCESS_DENIED") == 0) + return SANE_STATUS_ACCESS_DENIED; + } + + max_scan_length = max_length; + if (test_device->val[opt_read_limit].w == SANE_TRUE + && test_device->val[opt_read_limit_size].w < max_scan_length) + { + max_scan_length = test_device->val[opt_read_limit_size].w; + DBG (3, "sane_read: limiting max_scan_length to %d bytes\n", + max_scan_length); + } + + *length = 0; + + if (!data) + { + DBG (1, "sane_read: data == NULL\n"); + return SANE_STATUS_INVAL; + } + if (!test_device->open) + { + DBG (1, "sane_read: not open\n"); + return SANE_STATUS_INVAL; + } + if (test_device->cancelled) + { + DBG (1, "sane_read: scan was cancelled\n"); + return SANE_STATUS_CANCELLED; + } + if (!test_device->scanning) + { + DBG (1, "sane_read: not scanning (call sane_start first)\n"); + return SANE_STATUS_INVAL; + } + if (test_device->eof) + { + DBG (2, "sane_read: EOF reached\n"); + return SANE_STATUS_EOF; + } + read_count = max_scan_length; + + bytes_read = fread (data, 1, read_count, test_device->pipe_handle); + if (feof (test_device->pipe_handle)) + { + SANE_Status status; + DBG (2, "sane_read: EOF reached\n"); + status = finish_pass (test_device); + if (status != SANE_STATUS_GOOD) + { + DBG (1, "sane_read: finish_pass returned `%s'\n", + sane_strstatus (status)); + return status; + } + test_device->eof = SANE_TRUE; + if (strcmp (test_device->val[opt_mode].s, "Color") == 0 + && test_device->val[opt_three_pass].w == SANE_TRUE) + test_device->pass++; + if (bytes_read == 0) + return SANE_STATUS_EOF; + } + else if (bytes_read == 0 && ferror (test_device->pipe_handle)) + { + if (errno == EAGAIN) + { + DBG (2, "sane_read: no data available, try again\n"); + return SANE_STATUS_GOOD; + } + else if (errno == 0) + { + DBG (2, "sane_read: huh? ferror set but errno == 0?\n"); + clearerr (test_device->pipe_handle); + } + else + { + DBG (1, "sane_read: fread returned error: %s\n", strerror (errno)); + return SANE_STATUS_IO_ERROR; + } + } + *length = bytes_read; + test_device->bytes_total += bytes_read; + + DBG (2, "sane_read: read %d bytes of %d, total %d\n", bytes_read, + max_length, test_device->bytes_total); + return SANE_STATUS_GOOD; +} + +void +sane_cancel (SANE_Handle handle) +{ + Test_Device *test_device = handle; + + DBG (2, "sane_cancel: handle = %p\n", handle); + if (!check_handle (handle)) + { + DBG (1, "sane_cancel: handle %p unknown\n", handle); + return; + } + if (!test_device->open) + { + DBG (1, "sane_cancel: not open\n"); + return; + } + if (test_device->cancelled) + { + DBG (1, "sane_read: scan already cancelled\n"); + return; + } + if (!test_device->scanning) + { + DBG (1, "sane_read: not scanning\n"); + return; + } + finish_pass (test_device); + test_device->cancelled = SANE_TRUE; + test_device->scanning = SANE_FALSE; + test_device->eof = SANE_FALSE; + test_device->pass = 0; + return; +} + +SANE_Status +sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) +{ + Test_Device *test_device = handle; + + DBG (2, "sane_set_io_mode: handle = %p, non_blocking = %d\n", handle, + non_blocking); + if (!check_handle (handle)) + { + DBG (1, "sane_set_io_mode: handle %p unknown\n", handle); + return SANE_STATUS_INVAL; + } + if (!test_device->open) + { + DBG (1, "sane_set_io_mode: not open\n"); + return SANE_STATUS_INVAL; + } + if (!test_device->scanning) + { + DBG (1, "sane_set_io_mode: not scanning\n"); + return SANE_STATUS_INVAL; + } + if (test_device->val[opt_non_blocking].w == SANE_TRUE) + { + if (fcntl (test_device->pipe, + F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0) + { + DBG (1, "sane_set_io_mode: can't set io mode"); + return SANE_STATUS_INVAL; + } + } + else + { + if (non_blocking) + return SANE_STATUS_UNSUPPORTED; + } + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) +{ + Test_Device *test_device = handle; + + DBG (2, "sane_get_select_fd: handle = %p, fd %s 0\n", handle, + fd ? "!=" : "="); + if (!check_handle (handle)) + { + DBG (1, "sane_get_select_fd: handle %p unknown\n", handle); + return SANE_STATUS_INVAL; + } + if (!test_device->open) + { + DBG (1, "sane_get_select_fd: not open\n"); + return SANE_STATUS_INVAL; + } + if (!test_device->scanning) + { + DBG (1, "sane_get_select_fd: not scanning\n"); + return SANE_STATUS_INVAL; + } + if (test_device->val[opt_select_fd].w == SANE_TRUE) + { + *fd = test_device->pipe; + return SANE_STATUS_GOOD; + } + return SANE_STATUS_UNSUPPORTED; +} diff --git a/backend/test.conf b/backend/test.conf new file mode 100644 index 000000000..60460c1db --- /dev/null +++ b/backend/test.conf @@ -0,0 +1,81 @@ +# test backend configuration file +######################################## + +# number of test devices +number_of_devices 2 + +# Initial option values and ranges +######################################## + +# Scan mode (Color, Gray) +mode Gray + +# Bit depth (1, 8, 16) +depth 8 + +# Hand-scanner mode (true, false) +hand-scanner false + +# Three-pass mode (true, false) +three-pass false + +# Three-pass order (RGB, RBG, GBR, GRB, BGR, BRG) +three-pass-order RGB + +# Resolution (dpi) +resolution_min 1.0 +resolution_max 1200.0 +resolution_quant 1.0 +resolution 50.0 + +# Draw test picture ("Solid black", "Solid white", "Color pattern", "Grid") +test-picture "Solid black" + +# Read-limit (true, false) +read-limit false + +# Read-limit size (1 - 65536 bytes) +read-limit-size 1 + +# Read-delay (true, false) +read-delay false + +# Read-delay duration (1000 - 200,000 microseconds) +read-delay-duration 1000 + +# Status code (return-value) of sane_read() ("Default", +# "SANE_STATUS_UNSUPPORTED", +# "SANE_STATUS_CANCELLED", "SANE_STATUS_DEVICE_BUSY", "SANE_STATUS_INVAL", +# "SANE_STATUS_EOF", "SANE_STATUS_JAMMED", "SANE_STATUS_NO_DOCS", +# "SANE_STATUS_COVER_OPEN", "SANE_STATUS_IO_ERROR", "SANE_STATUS_NO_MEM", +# "SANE_STATUS_ACCESS_DENIED" +read-status-code "Default" + +# Fuzzy parameters (true, false) +fuzzy-parameters false + +# Non-blocking io (true, false) +non-blocking false + +# Support select fd (true, false) +select-fd false + +# Enable test options (true, false) +enable-test-options false + +# Geometry (mm) +geometry_min 0.0 +geometry_max 200.0 +geometry_quant 1.0 + +# Top-left x position (mm) +tl_x 0.0 + +# Top-left y position (mm) +tl_y 0.0 + +# Bottom-right x position (mm) +br_x 80.0 + +# Bottom-right y position (mm) +br_y 100.0 diff --git a/backend/test.h b/backend/test.h new file mode 100644 index 000000000..87ad29649 --- /dev/null +++ b/backend/test.h @@ -0,0 +1,103 @@ +#ifndef test_h +#define test_h + +#define MM_PER_INCH 25.4 + +typedef enum +{ + param_none = 0, + param_bool, + param_int, + param_fixed, + param_string +} +parameter_type; + +typedef enum +{ + opt_num_opts = 0, + opt_mode_group, + opt_mode, + opt_depth, + opt_hand_scanner, + opt_three_pass, /* 5 */ + opt_three_pass_order, + opt_resolution, + opt_special_group, + opt_test_picture, + opt_read_limit, /* 10 */ + opt_read_limit_size, + opt_read_delay, + opt_read_delay_duration, + opt_read_status_code, + opt_fuzzy_parameters, + opt_non_blocking, + opt_select_fd, + opt_enable_test_options, + opt_print_options, + opt_geometry_group, + opt_tl_x, + opt_tl_y, + opt_br_x, + opt_br_y, + opt_bool_group, + opt_bool_soft_select_soft_detect, + opt_bool_hard_select_soft_detect, + opt_bool_hard_select, + opt_bool_soft_detect, + opt_bool_soft_select_soft_detect_emulated, + opt_bool_soft_select_soft_detect_auto, + opt_int_group, + opt_int, + opt_int_constraint_range, + opt_int_constraint_word_list, + opt_int_array, + opt_int_array_constraint_range, + opt_int_array_constraint_word_list, + opt_fixed_group, + opt_fixed, + opt_fixed_constraint_range, + opt_fixed_constraint_word_list, + opt_string_group, + opt_string, + opt_string_constraint_string_list, + opt_string_constraint_long_string_list, + opt_button_group, + opt_button, + /* must come last: */ + num_options +} +test_opts; + +typedef union +{ + SANE_Word w; + SANE_Word *wa; /* word array */ + SANE_String s; +} +Option_Value; + + +typedef struct Test_Device +{ + struct Test_Device *next; + SANE_Device sane; + SANE_Option_Descriptor opt[num_options]; + Option_Value val[num_options]; + SANE_Parameters params; + SANE_String name; + SANE_Int reader_pid; + SANE_Int pipe; + FILE *pipe_handle; + SANE_Word pass; + SANE_Word bytes_per_line; + SANE_Word lines; + SANE_Int bytes_total; + SANE_Bool open; + SANE_Bool scanning; + SANE_Bool cancelled; + SANE_Bool eof; +} +Test_Device; + +#endif /* test_h */