Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
iscan/backend/epkowa.c
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
6513 lines (5408 sloc)
180 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* epkowa.c - SANE backend for EPSON flatbed scanners | |
(Image Scan! version) | |
Based on the SANE Epson backend (originally from sane-1.0.3) | |
- updated to sane-backends-1.0.6 | |
- renamed from epson to epkowa to avoid confusion | |
- updated to sane-backends-1.0.12 | |
- updated to sane-backends-1.0.15 | |
Based on Kazuhiro Sasayama previous | |
Work on epson.[ch] file from the SANE package. | |
Original code taken from sane-0.71 | |
Copyright (C) 1997 Hypercore Software Design, Ltd. | |
modifications | |
Copyright (C) 1998-1999 Christian Bucher <bucher@vernetzt.at> | |
Copyright (C) 1998-1999 Kling & Hautzinger GmbH | |
Copyright (C) 1999 Norihiko Sawa <sawa@yb3.so-net.ne.jp> | |
Copyright (C) 2000 Mike Porter <mike@udel.edu> (mjp) | |
Copyright (C) 1999-2004 Karl Heinz Kremer <khk@khk.net> | |
Copyright (C) 2001-2016 SEIKO EPSON CORPORATION | |
This file is part of the EPKOWA SANE backend. | |
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. | |
*/ | |
#ifndef SANE_I18N | |
#define SANE_I18N(text) (text) | |
#endif | |
#ifdef HAVE_CONFIG_H | |
#include <config.h> | |
#endif | |
#include <limits.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <errno.h> | |
#include <math.h> | |
#include <sane/saneopts.h> | |
#ifndef BACKEND_NAME | |
#define BACKEND_NAME epkowa | |
#endif | |
#include "backend.h" | |
#include "command.h" | |
#include "hw-data.h" | |
#include "utils.h" | |
#include "timing.h" | |
#include "cfg-obj.h" | |
#include "dip-obj.h" | |
#include "model-info.h" | |
#include "utils.h" | |
#include "epkowa_scsi.h" | |
#include "sane/sanei_pio.h" | |
#include "epkowa_ip.h" /* interpreter-based scanner support */ | |
#include "sane/sanei.h" | |
#define DEFAULT_RESOLUTION 300 /* dpi */ | |
#define DEFAULT_X_RESOLUTION DEFAULT_RESOLUTION | |
#define DEFAULT_Y_RESOLUTION DEFAULT_RESOLUTION | |
#define S_ACK "\006" | |
#define S_CAN "\030" | |
/* Usable values when defining EPSON_LEVEL_DEFAULT | |
* There is also a function level "A5", used for the GT-300, a | |
* monochrome only scanner. This level is not supported. | |
*/ | |
#define EPSON_LEVEL_A1 0 | |
#define EPSON_LEVEL_A2 1 | |
#define EPSON_LEVEL_B1 2 | |
#define EPSON_LEVEL_B2 3 | |
#define EPSON_LEVEL_B3 4 | |
#define EPSON_LEVEL_B4 5 | |
#define EPSON_LEVEL_B5 6 | |
#define EPSON_LEVEL_B6 7 | |
#define EPSON_LEVEL_B7 8 | |
#define EPSON_LEVEL_B8 9 | |
#define EPSON_LEVEL_F5 10 | |
#define EPSON_LEVEL_D1 11 | |
#define EPSON_LEVEL_D2 12 | |
#define EPSON_LEVEL_D7 13 | |
#define EPSON_LEVEL_D8 14 | |
#define EPSON_LEVEL_DEFAULT EPSON_LEVEL_B3 | |
static EpsonCmdRec epson_cmd[] = { | |
/* | |
* request identity | |
* | request identity2 | |
* | | request status | |
* | | | request condition | |
* | | | | set color mode | |
* | | | | | start scanning | |
* | | | | | | set data format | |
* | | | | | | | set resolution | |
* | | | | | | | | set zoom | |
* | | | | | | | | | set scan area | |
* | | | | | | | | | | set brightness | |
* | | | | | | | | | | | set gamma | |
* | | | | | | | | | | | | set halftoning | |
* | | | | | | | | | | | | | set color correction | |
* | | | | | | | | | | | | | | initialize scanner | |
* | | | | | | | | | | | | | | | set speed | |
* | | | | | | | | | | | | | | | | set lcount | |
* | | | | | | | | | | | | | | | | | mirror image | |
* | | | | | | | | | | | | | | | | | | set gamma table | |
* | | | | | | | | | | | | | | | | | | | set outline emphasis | |
* | | | | | | | | | | | | | | | | | | | | set dither | |
* | | | | | | | | | | | | | | | | | | | | | set color correction coefficients | |
* | | | | | | | | | | | | | | | | | | | | | | request extension status | |
* | | | | | | | | | | | | | | | | | | | | | | | control an extension | |
* | | | | | | | | | | | | | | | | | | | | | | | | forward feed / eject | |
* | | | | | | | | | | | | | | | | | | | | | | | | | feed | |
* | | | | | | | | | | | | | | | | | | | | | | | | | | request push button status | |
* | | | | | | | | | | | | | | | | | | | | | | | | | | | control auto area segmentation | |
* | | | | | | | | | | | | | | | | | | | | | | | | | | | | set film type | |
* | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set exposure time | |
* | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set bay | |
* | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set threshold | |
* | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | set focus position | |
* | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | request focus position | |
* | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
* | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
*/ | |
{"A1",'I', 0 ,'F','S', 0 ,'G', 0 ,'R', 0 ,'A', 0 ,{ 0, 0, 0}, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }, | |
{"A2",'I', 0 ,'F','S', 0 ,'G','D','R','H','A','L',{-3, 3, 0},'Z','B', 0 ,'@', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }, | |
{"B1",'I', 0 ,'F','S','C','G','D','R', 0 ,'A', 0 ,{ 0, 0, 0}, 0 ,'B', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }, | |
{"B2",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z','B', 0 ,'@', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }, | |
{"B3",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z','B','M','@', 0 , 0 , 0 , 0 , 0 , 0 ,'m','f','e', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }, | |
{"B4",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z','B','M','@','g','d', 0 ,'z','Q','b','m','f','e', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }, | |
{"B5",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z','B','M','@','g','d','K','z','Q','b','m','f','e', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }, | |
{"B6",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z','B','M','@','g','d','K','z','Q','b','m','f','e', 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }, | |
{"B7",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z','B','M','@','g','d','K','z','Q','b','m','f','e','\f', 0 ,'!','s','N', 0 , 0 ,'t', 0 , 0 }, | |
{"B8",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z','B','M','@','g','d','K','z','Q','b','m','f','e','\f', 0x19,'!','s','N', 0 , 0 ,'t','p','q'}, | |
{"F5",'I', 0 ,'F','S','C','G','D','R','H','A','L',{-3, 3, 0},'Z', 0 ,'M','@','g','d','K','z','Q', 0 ,'m','f','e','\f', 0 , 0 , 0 ,'N','T','P', 0 , 0 , 0 }, | |
{"D1",'I','i','F', 0 ,'C','G','D','R', 0 ,'A', 0 ,{ 0, 0, 0},'Z', 0 , 0 ,'@','g','d', 0 ,'z', 0 , 0 , 0 ,'f', 0 , 0 , 0 ,'!', 0 , 0 , 0 , 0 ,'t', 0 , 0 }, | |
{"D2",'I','i','F', 0 ,'C','G','D','R', 0 ,'A', 0 ,{ 0, 0, 0},'Z', 0 , 0 ,'@','g','d', 0 ,'z', 0 , 0 , 0 ,'f','e', 0 , 0 ,'!', 0 ,'N', 0 , 0 ,'t', 0 , 0 }, | |
{"D7",'I','i','F', 0 ,'C','G','D','R', 0 ,'A', 0 ,{ 0, 0, 0},'Z', 0 , 0 ,'@','g','d', 0 ,'z', 0 , 0 , 0 ,'f','e','\f', 0 ,'!', 0 ,'N', 0 , 0 ,'t', 0 , 0 }, | |
{"D8",'I','i','F', 0 ,'C','G','D','R', 0 ,'A', 0 ,{ 0, 0, 0},'Z', 0 , 0 ,'@','g','d', 0 ,'z', 0 , 0 , 0 ,'f','e','\f', 0 ,'!', 0 ,'N', 0 , 0 ,'t', 0 , 0 }, | |
}; | |
/* Definition of the mode_param struct, that is used to | |
* specify the valid parameters for the different scan modes. | |
* | |
* The depth variable gets updated when the bit depth is modified. | |
*/ | |
struct mode_param | |
{ | |
int color; | |
int mode_flags; | |
int dropout_mask; | |
int depth; | |
}; | |
static struct mode_param mode_params[] = { | |
{0, 0x00, 0x30, 1}, | |
{0, 0x00, 0x30, 8}, | |
{1, 0x02, 0x00, 8} | |
}; | |
static const SANE_String_Const mode_list[] = { | |
SANE_I18N ("Binary"), | |
SANE_I18N ("Gray"), | |
SANE_I18N ("Color"), | |
NULL | |
}; | |
static const SANE_String_Const adf_mode_list[] = { | |
SANE_I18N ("Simplex"), | |
SANE_I18N ("Duplex"), | |
NULL | |
}; | |
static const SANE_String_Const dfd_sensitivity[] = { | |
SANE_I18N ("None"), | |
SANE_I18N ("Low"), | |
SANE_I18N ("High"), | |
NULL | |
}; | |
/* Define the different scan sources: | |
*/ | |
#define FBF_STR SANE_I18N("Flatbed") | |
#define TPU_STR SANE_I18N("Transparency Unit") | |
#define ADF_STR SANE_I18N("Automatic Document Feeder") | |
#define FILM_TYPE_POSITIVE (0) | |
#define FILM_TYPE_NEGATIVE (1) | |
static const SANE_String_Const film_list[] = { | |
SANE_I18N ("Positive Film"), | |
SANE_I18N ("Negative Film"), | |
NULL | |
}; | |
static const SANE_String_Const focus_list[] = { | |
SANE_I18N ("Focus on glass"), | |
SANE_I18N ("Focus 2.5mm above glass"), | |
NULL | |
}; | |
#define HALFTONE_NONE 0x01 | |
#define HALFTONE_TET 0x03 | |
static int halftone_params[] = { | |
HALFTONE_NONE, | |
0x00, | |
0x10, | |
0x20, | |
0x80, | |
0x90, | |
0xa0, | |
0xb0, | |
HALFTONE_TET, | |
0xc0, | |
0xd0 | |
}; | |
static const SANE_String_Const halftone_list[] = { | |
SANE_I18N ("None"), | |
SANE_I18N ("Halftone A (Hard Tone)"), | |
SANE_I18N ("Halftone B (Soft Tone)"), | |
SANE_I18N ("Halftone C (Net Screen)"), | |
NULL | |
}; | |
static const SANE_String_Const halftone_list_4[] = { | |
SANE_I18N ("None"), | |
SANE_I18N ("Halftone A (Hard Tone)"), | |
SANE_I18N ("Halftone B (Soft Tone)"), | |
SANE_I18N ("Halftone C (Net Screen)"), | |
SANE_I18N ("Dither A (4x4 Bayer)"), | |
SANE_I18N ("Dither B (4x4 Spiral)"), | |
SANE_I18N ("Dither C (4x4 Net Screen)"), | |
SANE_I18N ("Dither D (8x4 Net Screen)"), | |
NULL | |
}; | |
static const SANE_String_Const halftone_list_7[] = { | |
SANE_I18N ("None"), | |
SANE_I18N ("Halftone A (Hard Tone)"), | |
SANE_I18N ("Halftone B (Soft Tone)"), | |
SANE_I18N ("Halftone C (Net Screen)"), | |
SANE_I18N ("Dither A (4x4 Bayer)"), | |
SANE_I18N ("Dither B (4x4 Spiral)"), | |
SANE_I18N ("Dither C (4x4 Net Screen)"), | |
SANE_I18N ("Dither D (8x4 Net Screen)"), | |
SANE_I18N ("Text Enhanced Technology"), | |
SANE_I18N ("Download pattern A"), | |
SANE_I18N ("Download pattern B"), | |
NULL | |
}; | |
static int dropout_params[] = { | |
0x00, /* none */ | |
0x10, /* red */ | |
0x20, /* green */ | |
0x30 /* blue */ | |
}; | |
static const SANE_String_Const dropout_list[] = { | |
SANE_I18N ("None"), | |
SANE_I18N ("Red"), | |
SANE_I18N ("Green"), | |
SANE_I18N ("Blue"), | |
NULL | |
}; | |
static const SANE_String_Const brightness_method_list[] = { | |
SANE_I18N ("hardware"), /* needs to be first */ | |
SANE_I18N ("iscan"), | |
SANE_I18N ("gimp"), | |
NULL | |
}; | |
#define max_val 100 /* any integer value will do */ | |
static const SANE_Range brightness_range = { -max_val, max_val, 1}; | |
static const SANE_Range contrast_range = { -max_val, max_val, 1}; | |
#if 0 | |
/* We don't provide highlight and shadow options yet because we | |
* haven't worked out the GIMP part of that and how that should | |
* interact with the brightness-method option. For the "iscan" | |
* method, the values below are OK. | |
*/ | |
static const SANE_Range highlight_range = { 0, max_val, 0 }; | |
static const SANE_Range shadow_range = { 0, max_val, 0 }; | |
#endif | |
#undef max_val | |
/* Color correction: | |
* One array for the actual parameters that get sent to the scanner (color_params[]), | |
* one array for the strings that get displayed in the user interface (color_list[]) | |
* and one array to mark the user defined color correction (dolor_userdefined[]). | |
*/ | |
static int color_params_ab[] = { | |
0x00, | |
0x01, | |
0x10, | |
0x20, | |
0x40, | |
0x80 | |
}; | |
static SANE_Bool color_userdefined_ab[] = { | |
SANE_FALSE, | |
SANE_TRUE, | |
SANE_FALSE, | |
SANE_FALSE, | |
SANE_FALSE, | |
SANE_FALSE | |
}; | |
static const SANE_String_Const color_list_ab[] = { | |
SANE_I18N ("No Correction"), | |
SANE_I18N ("User defined"), | |
SANE_I18N ("Impact-dot printers"), | |
SANE_I18N ("Thermal printers"), | |
SANE_I18N ("Ink-jet printers"), | |
SANE_I18N ("CRT monitors"), | |
NULL | |
}; | |
static int color_params_d[] = { | |
0x00 | |
}; | |
static SANE_Bool color_userdefined_d[] = { | |
SANE_TRUE | |
}; | |
static const SANE_String_Const color_list_d[] = { | |
"User defined", | |
NULL | |
}; | |
/* Gamma correction: | |
* The A and B level scanners work differently than the D level scanners, therefore | |
* I define two different sets of arrays, plus one set of variables that get set to | |
* the actally used params and list arrays at runtime. | |
*/ | |
static int gamma_params_ab[] = { | |
0x01, | |
0x03, | |
0x04, | |
0x00, | |
0x10, | |
0x20 | |
}; | |
static const SANE_String_Const gamma_list_ab[] = { | |
SANE_I18N ("Default"), | |
SANE_I18N ("User defined (Gamma=1.0)"), | |
SANE_I18N ("User defined (Gamma=1.8)"), | |
SANE_I18N ("High density printing"), | |
SANE_I18N ("Low density printing"), | |
SANE_I18N ("High contrast printing"), | |
NULL | |
}; | |
static SANE_Bool gamma_userdefined_ab[] = { | |
SANE_FALSE, | |
SANE_TRUE, | |
SANE_TRUE, | |
SANE_FALSE, | |
SANE_FALSE, | |
SANE_FALSE, | |
}; | |
static int gamma_params_d[] = { | |
0x03, | |
0x04 | |
}; | |
static const SANE_String_Const gamma_list_d[] = { | |
SANE_I18N ("User defined (Gamma=1.0)"), | |
SANE_I18N ("User defined (Gamma=1.8)"), | |
NULL | |
}; | |
static SANE_Bool gamma_userdefined_d[] = { | |
SANE_TRUE, | |
SANE_TRUE | |
}; | |
/* Bay list: | |
* this is used for the FilmScan | |
*/ | |
static const SANE_String_Const bay_list[] = { | |
" 1 ", | |
" 2 ", | |
" 3 ", | |
" 4 ", | |
" 5 ", | |
" 6 ", | |
NULL | |
}; | |
static const SANE_Range u8_range = { 0, 255, 0 }; | |
static const SANE_Range s8_range = { -127, 127, 0 }; | |
static const SANE_Range zoom_range = { 50, 200, 0 }; | |
/* The "switch_params" are used for several boolean choices | |
*/ | |
static int switch_params[] = { | |
0, | |
1 | |
}; | |
#define mirror_params switch_params | |
#define speed_params switch_params | |
#define film_params switch_params | |
static const SANE_Range outline_emphasis_range = { -2, 2, 0 }; | |
enum | |
{ | |
EXT_SANE_STATUS_NONE, | |
EXT_SANE_STATUS_MULTI_FEED, | |
EXT_SANE_STATUS_TRAY_CLOSED, | |
EXT_SANE_STATUS_MAX, | |
}; | |
static const SANE_Range ext_sane_status = { | |
EXT_SANE_STATUS_NONE, | |
EXT_SANE_STATUS_MAX - 1, | |
0 | |
}; | |
typedef struct { | |
double width; /* in mm */ | |
double height; /* in mm */ | |
SANE_String_Const name; | |
} media_type; | |
static SANE_String_Const media_maximum = SANE_I18N ("Maximum"); | |
static SANE_String_Const media_automatic = SANE_I18N ("Automatic"); | |
/*! \brief List of preset media sizes | |
* This list is used to populate the constraint list for the | |
* scan-area option. | |
* | |
* \remark When adding Landscape and Portrait variants of a medium | |
* size, make sure to add three(!) entries using the same | |
* pattern as used for those already listed. The algorithm | |
* used to populate the constraint list depends on this | |
* convention to create the most user-friendly list. | |
*/ | |
static const media_type media_list[] = { | |
/* ISO A Series */ | |
{ 297, 420, SANE_I18N ("A3") }, | |
{ 297, 210, SANE_I18N ("A4 Landscape") }, | |
{ 210, 297, SANE_I18N ("A4 Portrait") }, | |
{ 210, 297, SANE_I18N ("A4") }, | |
{ 210, 148, SANE_I18N ("A5 Landscape") }, | |
{ 148, 210, SANE_I18N ("A5 Portrait") }, | |
{ 148, 210, SANE_I18N ("A5") }, | |
/* JIS B Series */ | |
{ 257, 364, SANE_I18N ("B4") }, | |
{ 257, 184, SANE_I18N ("B5 Landscape") }, | |
{ 184, 257, SANE_I18N ("B5 Portrait") }, | |
{ 184, 257, SANE_I18N ("B5") }, | |
/* North American sizes */ | |
{ 11.00 * MM_PER_INCH, 17.00 * MM_PER_INCH, SANE_I18N ("Ledger") }, | |
{ 8.50 * MM_PER_INCH, 14.00 * MM_PER_INCH, SANE_I18N ("Legal") }, | |
{ 11.00 * MM_PER_INCH, 8.50 * MM_PER_INCH, SANE_I18N ("Letter Landscape") }, | |
{ 8.50 * MM_PER_INCH, 11.00 * MM_PER_INCH, SANE_I18N ("Letter Portrait") }, | |
{ 8.50 * MM_PER_INCH, 11.00 * MM_PER_INCH, SANE_I18N ("Letter") }, | |
{ 10.50 * MM_PER_INCH, 7.25 * MM_PER_INCH, SANE_I18N ("Executive Landscape") }, | |
{ 7.25 * MM_PER_INCH, 10.50 * MM_PER_INCH, SANE_I18N ("Executive Portrait") }, | |
{ 7.25 * MM_PER_INCH, 10.50 * MM_PER_INCH, SANE_I18N ("Executive") }, | |
/* Miscellaneous */ | |
{ 120, 120, SANE_I18N ("CD")}, | |
}; | |
static SANE_Word *bitDepthList = NULL; | |
/* Some utility macros | |
*/ | |
/*! Returns the larger of the arguments \a a and \a b. | |
*/ | |
#define max(a, b) ((a) < (b) ? (b) : (a)) | |
/*! Returns the smaller of the arguments \a a and \a b. | |
*/ | |
#define min(a, b) ((a) < (b) ? (a) : (b)) | |
static size_t | |
max_string_size (const SANE_String_Const strings[]) | |
{ | |
size_t size, max_size = 0; | |
int i; | |
for (i = 0; strings[i]; i++) | |
{ | |
size = strlen (strings[i]) + 1; | |
if (size > max_size) | |
max_size = size; | |
} | |
return max_size; | |
} | |
static inline | |
bool | |
need_autocrop_override (const Epson_Scanner *s) | |
{ | |
return (SANE_OPTION_IS_ACTIVE (s->opt[OPT_AUTOCROP].cap) | |
&& s->val[OPT_AUTOCROP].b | |
&& 0 != autocrop_max_y (s->hw)); | |
} | |
typedef struct | |
{ | |
u_char code; | |
u_char status; | |
u_char count1; | |
u_char count2; | |
u_char type; | |
u_char level; | |
u_char buf[1]; | |
} EpsonIdentRec, *EpsonIdent; | |
typedef struct | |
{ | |
u_char code; | |
u_char status; | |
u_char count1; | |
u_char count2; | |
u_char buf[1]; | |
} EpsonHdrRec, *EpsonHdr; | |
static SANE_Status | |
read_image_info_block (device *hw); | |
static SANE_Status | |
get_identity_information (device *hw); | |
static SANE_Status | |
get_hardware_property (device *hw); | |
static SANE_Status | |
get_identity2_information (device *hw, Epson_Scanner *s); | |
static SANE_Status | |
check_warmup (device *hw); | |
static SANE_Status | |
check_ext_status (device *hw); | |
SANE_Status | |
control_option_unit (device *hw, SANE_Bool use_duplex); | |
static SANE_Status | |
initialize (device *hw); | |
static SANE_Status | |
get_resolution_constraints (device *hw, Epson_Scanner *s); | |
static SANE_Status | |
get_push_button_status (device *hw, SANE_Bool *button_pushed); | |
static SANE_Status color_shuffle (Epson_Scanner *s, int *new_length); | |
static void activateOption (Epson_Scanner * s, SANE_Int option, | |
SANE_Bool * change); | |
static void deactivateOption (Epson_Scanner * s, SANE_Int option, | |
SANE_Bool * change); | |
static void setOptionState (Epson_Scanner * s, SANE_Bool state, | |
SANE_Int option, SANE_Bool * change); | |
static void filter_resolution_list (Epson_Scanner * s); | |
static void scan_finish (Epson_Scanner * s); | |
static void get_colorcoeff_from_profile (double *profile, | |
unsigned char *color_coeff); | |
static void round_cct (double org_cct[], int rnd_cct[]); | |
static int get_roundup_index (double frac[], int n); | |
static int get_rounddown_index (double frac[], int n); | |
static void handle_mode (Epson_Scanner * s, SANE_Int optindex, | |
SANE_Bool * reload); | |
static void handle_resolution (Epson_Scanner * s, SANE_Int option, | |
SANE_Word value); | |
static void change_profile_matrix (Epson_Scanner * s); | |
static void handle_depth_halftone (Epson_Scanner * s, SANE_Int optindex, | |
SANE_Bool * reload); | |
static SANE_Status create_epson_device (device **dev, channel* ch); | |
static SANE_Status create_epson_scanner (device *dev, Epson_Scanner **scanner); | |
static SANE_Status create_sane_handle (Epson_Scanner **scanner, const char *name, const void *dip); | |
static SANE_Status init_options (Epson_Scanner * s); | |
static scan_area_t get_model_info_max_scan_area (device *hw, int adf_mode); | |
static SANE_Status handle_scan_area(Epson_Scanner *s, int adf_mode); | |
static SANE_Status handle_source (Epson_Scanner * s, | |
SANE_Int optindex, | |
char *value); | |
static void adf_handle_out_of_paper (Epson_Scanner * s); | |
static void adf_handle_adjust_alignment (Epson_Scanner *s, SANE_Bool finalize); | |
static void handle_autocrop (Epson_Scanner *s, SANE_Bool *value, SANE_Bool *reload); | |
static void handle_deskew (Epson_Scanner *s, SANE_Bool *value, SANE_Bool *reload); | |
static void handle_detect_doc_size (Epson_Scanner *s, SANE_Bool *value, SANE_Bool *reload); | |
static void handle_preview (Epson_Scanner *s, SANE_Bool *value, SANE_Bool *reload); | |
static SANE_Status | |
expect_ack (device *hw) | |
{ | |
u_char result[1]; | |
size_t len; | |
SANE_Status status; | |
log_call (); | |
len = sizeof (result); | |
channel_recv (hw->channel, result, len, &status); | |
if (SANE_STATUS_GOOD != status) | |
return status; | |
if (ACK != result[0]) | |
return SANE_STATUS_INVAL; | |
return SANE_STATUS_GOOD; | |
} | |
static SANE_Status | |
set_cmd (device *hw, u_char cmd, u_char val) | |
{ | |
SANE_Status status; | |
u_char params[2]; | |
if (!cmd) | |
return SANE_STATUS_UNSUPPORTED; | |
log_call ("(%c)", cmd); | |
/* Override with extended parameter command (FS W). | |
* Do not override A, R and e, they are handled separately. | |
*/ | |
if (hw->using_fs && strchr ("CDgdZLMBtsQKN", cmd)) | |
return dev_set_scanning_parameter (hw, cmd, &val); | |
params[0] = ESC; | |
params[1] = cmd; | |
channel_send (hw->channel, params, 2, &status); | |
if (SANE_STATUS_GOOD != (status = expect_ack (hw))) | |
return status; | |
params[0] = val; | |
channel_send (hw->channel, params, 1, &status); | |
status = expect_ack (hw); | |
return status; | |
} | |
static void | |
print_params (const SANE_Parameters params) | |
{ | |
log_data ("params.format = %d", params.format); | |
log_data ("params.last_frame = %d", params.last_frame); | |
log_data ("params.bytes_per_line = %d", params.bytes_per_line); | |
log_data ("params.pixels_per_line = %d", params.pixels_per_line); | |
log_data ("params.lines = %d", params.lines); | |
log_data ("params.depth = %d", params.depth); | |
} | |
#define set_focus_position(h,v) set_cmd (h,(h)->cmd->set_focus_position,v) | |
#define set_color_mode(h,v) set_cmd (h,(h)->cmd->set_color_mode,v) | |
#define set_data_format(h,v) set_cmd (h,(h)->cmd->set_data_format, v) | |
#define set_halftoning(h,v) set_cmd (h,(h)->cmd->set_halftoning, v) | |
#define set_gamma(h,v) set_cmd (h,(h)->cmd->set_gamma, v) | |
#define set_color_correction(h,v) set_cmd (h,(h)->cmd->set_color_correction, v) | |
#define set_lcount(h,v) set_cmd (h,(h)->cmd->set_lcount, v) | |
#define set_bright(h,v) set_cmd (h,(h)->cmd->set_bright, v) | |
#define mirror_image(h,v) set_cmd (h,(h)->cmd->mirror_image, v) | |
#define set_speed(h,v) set_cmd (h,(h)->cmd->set_speed, v) | |
#define set_outline_emphasis(h,v) set_cmd (h,(h)->cmd->set_outline_emphasis, v) | |
#define control_auto_area_segmentation(h,v) set_cmd (h,(h)->cmd->control_auto_area_segmentation, v) | |
#define set_film_type(h,v) set_cmd (h,(h)->cmd->set_film_type, v) | |
#define set_exposure_time(h,v) set_cmd (h,(h)->cmd->set_exposure_time, v) | |
#define set_bay(h,v) set_cmd (h,(h)->cmd->set_bay, v) | |
#define set_threshold(h,v) set_cmd (h,(h)->cmd->set_threshold, v) | |
static SANE_Status | |
set_zoom (device *hw, int x_zoom, int y_zoom) | |
{ | |
SANE_Status status; | |
u_char cmd[2]; | |
u_char params[2]; | |
if (!hw->cmd->set_zoom) | |
return SANE_STATUS_GOOD; | |
log_call (); | |
cmd[0] = ESC; | |
cmd[1] = hw->cmd->set_zoom; | |
channel_send (hw->channel, cmd, 2, &status); | |
status = expect_ack (hw); | |
if (status != SANE_STATUS_GOOD) | |
return status; | |
params[0] = x_zoom; | |
params[1] = y_zoom; | |
channel_send (hw->channel, params, 2, &status); | |
status = expect_ack (hw); | |
return status; | |
} | |
static SANE_Status | |
set_resolution (device *hw, int xres, int yres) | |
{ | |
SANE_Status status; | |
u_char params[4]; | |
if (!hw->cmd->set_resolution) | |
return SANE_STATUS_GOOD; | |
log_call (); | |
params[0] = ESC; | |
params[1] = hw->cmd->set_resolution; | |
channel_send (hw->channel, params, 2, &status); | |
status = expect_ack (hw); | |
if (status != SANE_STATUS_GOOD) | |
return status; | |
params[0] = xres; | |
params[1] = xres >> 8; | |
params[2] = yres; | |
params[3] = yres >> 8; | |
channel_send (hw->channel, params, 4, &status); | |
status = expect_ack (hw); | |
return status; | |
} | |
/* set_scan_area() | |
* | |
* Sends the "set scan area" command to the scanner with the currently selected | |
* scan area. This scan area is already corrected for "color shuffling" if | |
* necessary. | |
*/ | |
static SANE_Status | |
set_scan_area (device *hw, int x, int y, int width, int height) | |
{ | |
SANE_Status status; | |
u_char params[8]; | |
log_call ("(%d, %d, %d, %d)", x, y, width, height); | |
if (!hw->cmd->set_scan_area) | |
{ | |
err_major ("set_scan_area not supported"); | |
return SANE_STATUS_GOOD; | |
} | |
/* verify the scan area */ | |
if (x < 0 || y < 0 || width <= 0 || height <= 0) | |
return SANE_STATUS_INVAL; | |
params[0] = ESC; | |
params[1] = hw->cmd->set_scan_area; | |
channel_send (hw->channel, params, 2, &status); | |
status = expect_ack (hw); | |
if (status != SANE_STATUS_GOOD) | |
return status; | |
params[0] = x; | |
params[1] = x >> 8; | |
params[2] = y; | |
params[3] = y >> 8; | |
params[4] = width; | |
params[5] = width >> 8; | |
params[6] = height; | |
params[7] = height >> 8; | |
channel_send (hw->channel, params, 8, &status); | |
status = expect_ack (hw); | |
return status; | |
} | |
/* set_color_correction_coefficients() | |
* | |
* Sends the "set color correction coefficients" command with the currently selected | |
* parameters to the scanner. | |
*/ | |
static SANE_Status | |
set_color_correction_coefficients (device *hw, Epson_Scanner *s) | |
{ | |
SANE_Status status; | |
u_char cmd = hw->cmd->set_color_correction_coefficients; | |
u_char params[2]; | |
const int length = 9; | |
unsigned char cccoeff[9]; | |
log_call (); | |
s->cct[0] = SANE_UNFIX (s->val[OPT_CCT_1].w); | |
s->cct[1] = SANE_UNFIX (s->val[OPT_CCT_2].w); | |
s->cct[2] = SANE_UNFIX (s->val[OPT_CCT_3].w); | |
s->cct[3] = SANE_UNFIX (s->val[OPT_CCT_4].w); | |
s->cct[4] = SANE_UNFIX (s->val[OPT_CCT_5].w); | |
s->cct[5] = SANE_UNFIX (s->val[OPT_CCT_6].w); | |
s->cct[6] = SANE_UNFIX (s->val[OPT_CCT_7].w); | |
s->cct[7] = SANE_UNFIX (s->val[OPT_CCT_8].w); | |
s->cct[8] = SANE_UNFIX (s->val[OPT_CCT_9].w); | |
if (!cmd) /* effect will be emulated */ | |
return SANE_STATUS_GOOD; | |
params[0] = ESC; | |
params[1] = cmd; | |
channel_send (hw->channel, params, 2, &status); | |
if (SANE_STATUS_GOOD != (status = expect_ack (hw))) | |
return status; | |
get_colorcoeff_from_profile (s->cct, cccoeff); | |
log_data ("[ %d %d %d ][ %d %d %d ][ %d %d %d]", | |
cccoeff[0], cccoeff[1], cccoeff[2], | |
cccoeff[3], cccoeff[4], cccoeff[5], | |
cccoeff[6], cccoeff[7], cccoeff[8]); | |
channel_send (hw->channel, cccoeff, length, &status); | |
status = expect_ack (hw); | |
log_call ("exit"); | |
return status; | |
} | |
static SANE_Status | |
set_gamma_table (device *hw, const Epson_Scanner *s) | |
{ | |
SANE_Status status; | |
u_char cmd = hw->cmd->set_gamma_table; | |
u_char params[2]; | |
const int length = 257; | |
u_char gamma[257]; | |
int n; | |
int table; | |
static char gamma_cmds[] = { 'R', 'G', 'B' }; | |
if (!cmd) | |
return SANE_STATUS_UNSUPPORTED; | |
log_call (); | |
params[0] = ESC; | |
params[1] = cmd; | |
/* When handling inverted images, we must also invert the user | |
* supplied gamma function. This is *not* just 255-gamma - | |
* this gives a negative image. | |
*/ | |
if (strcmp_c ("GT-6600", hw->fw_name) == 0 || | |
strcmp_c ("Perfection 610", hw->fw_name) == 0) | |
{ | |
gamma_cmds[0] = 'R'; | |
gamma_cmds[1] = 'B'; | |
gamma_cmds[2] = 'G'; | |
} | |
for (table = 0; table < 3; table++) | |
{ | |
gamma[0] = gamma_cmds[table]; | |
if (s->invert_image) | |
{ | |
for (n = 0; n < 256; ++n) | |
{ | |
gamma[n + 1] = 255 - s->gamma_table[table][255 - n]; | |
} | |
} | |
else | |
{ | |
for (n = 0; n < 256; ++n) | |
{ | |
gamma[n + 1] = s->gamma_table[table][n]; | |
} | |
} | |
channel_send (hw->channel, params, 2, &status); | |
if (SANE_STATUS_GOOD != (status = expect_ack (hw))) | |
return status; | |
channel_send (hw->channel, gamma, length, &status); | |
if (SANE_STATUS_GOOD != (status = expect_ack (hw))) | |
return status; | |
} | |
log_call ("exit"); | |
return status; | |
} | |
static SANE_Status | |
check_warmup (device *hw) | |
{ | |
SANE_Status status = check_ext_status (hw); | |
log_call (); | |
if (status == SANE_STATUS_DEVICE_BUSY) | |
{ | |
int timeout; | |
for (timeout = 0; timeout < 60; timeout++) | |
{ | |
status = check_ext_status (hw); | |
if (status == SANE_STATUS_DEVICE_BUSY) | |
sleep (1); | |
else | |
{ | |
return status; | |
} | |
} | |
} | |
else | |
return status; | |
return status; | |
} | |
static SANE_Status | |
check_ext_status (device *hw) | |
{ | |
SANE_Status status; | |
log_call (); | |
require (hw); | |
status = dev_request_extended_status (hw); | |
if (EXT_STATUS_WU & hw->ext_status) | |
{ | |
log_info ("option: warming up"); | |
status = SANE_STATUS_DEVICE_BUSY; | |
} | |
if (EXT_STATUS_FER & hw->ext_status) | |
{ | |
log_info ("option: fatal error"); | |
status = SANE_STATUS_INVAL; | |
} | |
if (hw->adf) | |
{ | |
if (ADF_STATUS_ERR & hw->adf->status | |
|| ADF_EXT_STATUS_ERR & hw->adf->ext_status) | |
{ | |
log_info ("ADF: other error"); | |
status = SANE_STATUS_INVAL; | |
} | |
if (ADF_STATUS_PE & hw->adf->status) | |
{ | |
log_info ("ADF: no paper"); | |
status = SANE_STATUS_NO_DOCS; | |
} | |
if (ADF_STATUS_PJ & hw->adf->status) | |
{ | |
log_info ("ADF: paper jam"); | |
status = SANE_STATUS_JAMMED; | |
} | |
if (ADF_STATUS_OPN & hw->adf->status) | |
{ | |
log_info ("ADF: cover open"); | |
status = SANE_STATUS_COVER_OPEN; | |
} | |
if (ADF_EXT_STATUS_DFE & hw->adf->ext_status) | |
{ | |
log_info ("ADF: multi sheet feed"); | |
status = SANE_STATUS_JAMMED; | |
} | |
if (ADF_EXT_STATUS_TR_OPN & hw->adf->ext_status) | |
{ | |
log_info ("ADF: tray open"); | |
status = SANE_STATUS_COVER_OPEN; | |
} | |
} | |
if (hw->tpu) | |
{ | |
if (TPU_STATUS_ERR & hw->tpu->status) | |
{ | |
log_info ("TPU: other error"); | |
status = SANE_STATUS_INVAL; | |
} | |
} | |
if (hw->fbf) | |
{ | |
/* ??? is this for MFDs? */ | |
if (DV3_STATUS_OPN & hw->fbf->status) | |
{ | |
log_info ("UNIT: Scanner Unit open"); | |
status = SANE_STATUS_COVER_OPEN; | |
} | |
} | |
return status; | |
} | |
static SANE_Status | |
initialize (device *hw) | |
{ | |
SANE_Status status; | |
u_char param[2]; | |
log_call (); | |
if (!hw->cmd->initialize_scanner) | |
return SANE_STATUS_GOOD; | |
param[0] = ESC; | |
param[1] = hw->cmd->initialize_scanner; | |
channel_send (hw->channel, param, 2, &status); | |
status = expect_ack (hw); | |
return status; | |
} | |
static Epson_Scanner *first_handle = NULL; | |
static EpsonHdr | |
command (device *hw, const u_char * cmd, size_t cmd_size, | |
SANE_Status * status) | |
{ | |
EpsonHdr head; | |
u_char *buf; | |
int count; | |
log_call (); | |
if (NULL == (head = t_malloc (1,EpsonHdrRec))) | |
{ | |
err_fatal ("%s", strerror (errno)); | |
*status = SANE_STATUS_NO_MEM; | |
return (EpsonHdr) 0; | |
} | |
channel_send (hw->channel, cmd, cmd_size, status); | |
if (SANE_STATUS_GOOD != *status) | |
{ | |
/* this is necessary for the GT-8000. I don't know why, but | |
it seems to fix the problem. It should not have any | |
ill effects on other scanners. */ | |
*status = SANE_STATUS_GOOD; | |
channel_send (hw->channel, cmd, cmd_size, status); | |
if (SANE_STATUS_GOOD != *status) | |
return (EpsonHdr) 0; | |
} | |
buf = (u_char *) head; | |
buf += channel_recv (hw->channel, buf, 4, status); | |
if (SANE_STATUS_GOOD != *status) | |
{ | |
delete (head); | |
return (EpsonHdr) 0; | |
} | |
switch (head->code) | |
{ | |
case NAK: | |
/* fall through */ | |
/* !!! is this really sufficient to report an error ? */ | |
case ACK: | |
break; /* no need to read any more data after ACK or NAK */ | |
case STX: | |
hw->status = head->status; | |
if (SANE_STATUS_GOOD != *status) | |
{ | |
delete (head); | |
return (EpsonHdr) 0; | |
} | |
count = head->count2 * 255 + head->count1; | |
log_info ("need to read %d data bytes", count); | |
/* Grow head so it has enough space to hold an additional count | |
* bytes of payload. We can _not_ use t_realloc here. | |
*/ | |
if (NULL == (head = realloc (head, sizeof (EpsonHdrRec) + count))) | |
{ | |
err_fatal ("%s", strerror (errno)); | |
*status = SANE_STATUS_NO_MEM; | |
return (EpsonHdr) 0; | |
} | |
buf = head->buf; | |
channel_recv (hw->channel, buf, count, status); | |
if (SANE_STATUS_GOOD != *status) | |
{ | |
delete (head); | |
return (EpsonHdr) 0; | |
} | |
break; | |
default: | |
if (0 == head->code) | |
{ err_major ("Incompatible printer port (probably bi/directional)"); } | |
else if (cmd[cmd_size - 1] == head->code) | |
{ err_major ("Incompatible printer port (probably not bi/directional)"); } | |
err_major ("Illegal response of scanner for command: %02x", head->code); | |
break; | |
} | |
return head; | |
} | |
static SANE_Status | |
create_epson_device (device **devp, channel* ch) | |
{ | |
SANE_Status status = SANE_STATUS_GOOD; | |
device *dev = NULL; | |
require (devp && ch); | |
dev = t_calloc (1, device); | |
if (!dev) | |
{ | |
err_fatal ("%s", strerror (errno)); | |
return SANE_STATUS_NO_MEM; | |
} | |
dev->channel = ch; | |
init_resolution_info (&dev->res , NULL); | |
init_resolution_info (&dev->res_x, NULL); | |
init_resolution_info (&dev->res_y, NULL); | |
dev->cmd = &epson_cmd[EPSON_LEVEL_DEFAULT]; | |
dev->fw_name = get_fw_name (dev->channel); | |
{ | |
void *cfg = cfg_init (NULL, NULL); | |
dev->using_fs = !cfg_has_value (cfg, CFG_KEY_FS_BLACKLIST, dev->fw_name); | |
} | |
if (!dev->using_fs && CHAN_NET == ch->type) | |
{ | |
err_fatal ("Network channel does not support scanning via ESC commands"); | |
return SANE_STATUS_IO_ERROR; | |
} | |
{ | |
size_t protocol_max = (dev->using_fs ? UINT32_MAX : UINT16_MAX); | |
ch->set_max_request_size (ch, min (protocol_max, 512 * 1024)); | |
} | |
initialize (dev); | |
if (dev->cmd->request_identity != 0) | |
{ | |
status = get_identity_information (dev); | |
if (status != SANE_STATUS_GOOD) | |
{ | |
return status; | |
} | |
} | |
if (dev->cmd->request_identity2 != 0) | |
{ | |
get_hardware_property (dev); | |
if (status != SANE_STATUS_GOOD) | |
{ | |
return status; | |
} | |
} | |
/* Check for the max. supported color depth and assign the values to | |
* the bitDepthList. Note that bitDepthList is a SANE word list and | |
* therefore has an initial element that indicates the number of its | |
* elements. We store up to 2 bit depths (8 and only one of 16, 14, | |
* and 12). | |
*/ | |
bitDepthList = t_malloc (1 + 2, SANE_Word); | |
if (bitDepthList == NULL) | |
{ | |
err_fatal ("%s", strerror (errno)); | |
return SANE_STATUS_NO_MEM; | |
} | |
bitDepthList[0] = 1; /* we start with one element in the list */ | |
bitDepthList[1] = 8; /* 8bit is the default */ | |
if (set_data_format (dev, 16) == SANE_STATUS_GOOD) | |
{ | |
dev->maxDepth = 16; | |
bitDepthList[0]++; | |
bitDepthList[bitDepthList[0]] = 16; | |
} | |
else if (set_data_format (dev, 14) == SANE_STATUS_GOOD) | |
{ | |
dev->maxDepth = 14; | |
bitDepthList[0]++; | |
bitDepthList[bitDepthList[0]] = 14; | |
} | |
else if (set_data_format (dev, 12) == SANE_STATUS_GOOD) | |
{ | |
dev->maxDepth = 12; | |
bitDepthList[0]++; | |
bitDepthList[bitDepthList[0]] = 12; | |
} | |
else | |
{ | |
dev->maxDepth = 8; | |
/* the default depth is already in the list */ | |
} | |
log_info ("maximum color depth = %d", dev->maxDepth); | |
status = dev_request_extended_status (dev); | |
if (SANE_STATUS_GOOD != status) | |
{ | |
if (SANE_STATUS_UNSUPPORTED != status) | |
{ | |
err_minor ("failure processing extended status request"); | |
} | |
} | |
else | |
{ | |
const void *info = model_info_cache_get_info (dev->fw_name, &status); | |
if (SANE_STATUS_GOOD == status && info) | |
{ | |
dev->scan_hard = model_info_get_profile (info); | |
model_info_customise_commands (info, dev->cmd); | |
dev->uses_locking = model_info_has_lock_commands (info); | |
} | |
else | |
{ | |
err_minor ("failure getting model info (%s)", sane_strstatus (status)); | |
} | |
} | |
if (0 == strcmp_c ("GT-8200", dev->fw_name)) | |
{ | |
/* Version 1.08 of the firmware is said to only report half of the | |
vertical size of the scan area. We should double that if it is | |
smaller than the horizontal scan area. Although we already try | |
doing this by fixing up the extended status reply, that doesn't | |
do the whole trick because we are dealing with a device type 0 | |
scanner. In that case we get the FBF scan area via the 'ESC I' | |
command which does not include model info so we can not safely | |
fix up that reply. | |
*/ | |
if (dev->fbf && (dev->fbf->max_y < dev->fbf->max_x)) | |
{ | |
err_minor ("Fixing up buggy FBF max scan dimensions."); | |
dev->fbf->max_y *= 2; | |
update_ranges (dev, dev->fbf); | |
dev->need_color_reorder = SANE_TRUE; | |
} | |
} | |
if (dev->fbf) | |
{ | |
log_info (" FBF: TL (%.2f, %.2f) -- BR (%.2f, %.2f) [in mm]", | |
SANE_UNFIX (dev->fbf->x_range.min), | |
SANE_UNFIX (dev->fbf->y_range.min), | |
SANE_UNFIX (dev->fbf->x_range.max), | |
SANE_UNFIX (dev->fbf->y_range.max)); | |
// check for auto size detection support and cache the results | |
dev->fbf->has_size_check = ((0 != dev->fbf->doc_x) | |
&& (0 != dev->fbf->doc_y)); | |
} | |
if (dev->adf) | |
{ | |
log_info (" ADF: TL (%.2f, %.2f) -- BR (%.2f, %.2f) [in mm]", | |
SANE_UNFIX (dev->adf->x_range.min), | |
SANE_UNFIX (dev->adf->y_range.min), | |
SANE_UNFIX (dev->adf->x_range.max), | |
SANE_UNFIX (dev->adf->y_range.max)); | |
log_info (" ADF: %s, %s feed type", | |
(EXT_STATUS_ADFS & dev->ext_status ? "duplex" : "simplex"), | |
(EXT_STATUS_ADFT & dev->ext_status ? "page" : "sheet")); | |
// check for auto size detection support and cache the results | |
dev->adf->has_size_check = ((0 != dev->adf->doc_x) | |
&& (0 != dev->adf->doc_y)); | |
// handle the special case where some scanners claim to have auto | |
// document size detection support for flatbed but not for adf | |
if (dev->fbf && (dev->fbf->has_size_check && !dev->adf->has_size_check)) | |
{ | |
cmd_control_option_unit (dev, 0x01); // turn on ADF | |
dev_request_extended_status (dev); // now re-query scanner | |
// re-check for document size detection support | |
dev->adf->has_size_check = ((0 != dev->adf->doc_x) | |
&& (0 != dev->adf->doc_y)); | |
cmd_control_option_unit (dev, 0x00); // turn off ADF | |
} | |
} | |
if (dev->tpu) | |
{ | |
log_info (" TPU: TL (%.2f, %.2f) -- BR (%.2f, %.2f) [in mm]", | |
SANE_UNFIX (dev->tpu->x_range.min), | |
SANE_UNFIX (dev->tpu->y_range.min), | |
SANE_UNFIX (dev->tpu->x_range.max), | |
SANE_UNFIX (dev->tpu->y_range.max)); | |
// check for auto size detection support and cache the results | |
dev->tpu->has_size_check = ((0 != dev->tpu->doc_x) | |
&& (0 != dev->tpu->doc_y)); | |
} | |
/* establish defaults */ | |
dev->need_reset_on_source_change = SANE_FALSE; | |
if (strcmp_c ("ES-9000H", dev->fw_name) == 0 || | |
strcmp_c ("GT-30000", dev->fw_name) == 0) | |
{ | |
dev->cmd->set_focus_position = 0; | |
dev->cmd->feed = 0x19; | |
} | |
else if (strcmp_c ("GT-8200", dev->fw_name) == 0 || | |
strcmp_c ("Perfection1640", dev->fw_name) == 0 || | |
strcmp_c ("GT-8700", dev->fw_name) == 0) | |
{ | |
dev->cmd->feed = 0; | |
dev->cmd->set_focus_position = 0; | |
dev->need_reset_on_source_change = SANE_TRUE; | |
} | |
{ /* set up supported scan sources */ | |
int i = 0; | |
if (dev->fbf) dev->sources[i++] = FBF_STR; | |
if (dev->adf) dev->sources[i++] = ADF_STR; | |
if (dev->tpu) dev->sources[i++] = TPU_STR; | |
dev->sources[i] = NULL; /* terminator */ | |
} | |
if (!dev->src) dev->src = (extension *) dev->fbf; | |
if (!dev->src) dev->src = (extension *) dev->adf; | |
if (!dev->src) dev->src = (extension *) dev->tpu; | |
require (dev->src); | |
if (!using (dev, fbf)) | |
dev_set_option_unit (dev, false); | |
dev->polling_time = DEFAULT_POLLING_TIME; | |
if (push_button_needs_polling (dev) | |
|| maintenance_is_supported (dev)) | |
{ | |
dev->polling_time = (250 * 1000); | |
} | |
*devp = dev; | |
return SANE_STATUS_GOOD; | |
} /* create epson device */ | |
static SANE_Status | |
create_epson_scanner (device *dev, Epson_Scanner **scanner) | |
{ | |
Epson_Scanner *s = t_calloc (1, Epson_Scanner); | |
if (!s) | |
{ | |
err_fatal ("%s", strerror (errno)); | |
return SANE_STATUS_NO_MEM; | |
} | |
s->hw = dev; | |
s->src = &s->raw; | |
if (s->hw->cmd->request_identity2 != 0) | |
{ | |
/* reset constraints because we are pointing to different lists now | |
FIXME: Only needs to be done the first time we (successfully) | |
send the ESC i command. This should be done when constructing | |
the device and is therefore done by the time we construct a | |
scanner. While the content and size of the lists may vary | |
depending on the selected option, the list nature is constant. | |
Hmm, we may actually be zapping the lists ... | |
*/ | |
s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; | |
s->opt[OPT_X_RESOLUTION].constraint.word_list = s->hw->res_x.list; | |
s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; | |
s->opt[OPT_Y_RESOLUTION].constraint.word_list = s->hw->res_y.list; | |
} | |
s->frame_count = 0; | |
promise (s->hw); | |
promise (s->src); | |
*scanner = s; | |
return SANE_STATUS_GOOD; | |
} | |
/*! \todo Release resources in error recovery. | |
*/ | |
static SANE_Status | |
create_sane_handle (Epson_Scanner **handle, const char *name, const void *dip) | |
{ | |
SANE_Status status = SANE_STATUS_GOOD; | |
device *dev = NULL; | |
Epson_Scanner *s = NULL; | |
channel *ch = NULL; | |
dev = NULL; | |
ch = channel_create (name, &status); | |
if (SANE_STATUS_GOOD != status) return status; | |
ch->open (ch, &status); | |
if (SANE_STATUS_GOOD != status) return status; | |
status = create_epson_device (&dev, ch); | |
if (SANE_STATUS_GOOD != status) | |
return status; | |
status = create_epson_scanner (dev, &s); | |
if (!s || SANE_STATUS_GOOD != status) | |
return status; | |
s->dip = dip; | |
init_options (s); | |
*handle = s; | |
return SANE_STATUS_GOOD; | |
} | |
static SANE_Status | |
init_options (Epson_Scanner * s) | |
{ | |
int i; | |
SANE_Bool dummy; | |
SANE_Bool reload; | |
log_call (); | |
for (i = 0; i < NUM_OPTIONS; ++i) | |
{ | |
s->opt[i].size = sizeof (SANE_Word); | |
s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; | |
} | |
s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; | |
s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; | |
s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; | |
s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; | |
/* "Scan Mode" group: */ | |
s->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode"); | |
s->opt[OPT_MODE_GROUP].desc = ""; | |
s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; | |
s->opt[OPT_MODE_GROUP].cap = 0; | |
/* scan mode */ | |
s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; | |
s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE; | |
s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE; | |
s->opt[OPT_MODE].type = SANE_TYPE_STRING; | |
s->opt[OPT_MODE].size = max_string_size (mode_list); | |
s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; | |
s->opt[OPT_MODE].constraint.string_list = mode_list; | |
s->val[OPT_MODE].w = 2; /* Color */ | |
/* bit depth */ | |
s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH; | |
s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH; | |
s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH; | |
s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT; | |
s->opt[OPT_BIT_DEPTH].unit = SANE_UNIT_NONE; | |
s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST; | |
s->opt[OPT_BIT_DEPTH].constraint.word_list = bitDepthList; | |
s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; | |
s->val[OPT_BIT_DEPTH].w = bitDepthList[1]; /* the first "real" element is the default */ | |
if (bitDepthList[0] == 1) /* only one element in the list -> hide the option */ | |
s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; | |
/* halftone */ | |
s->opt[OPT_HALFTONE].name = SANE_NAME_HALFTONE; | |
s->opt[OPT_HALFTONE].title = SANE_TITLE_HALFTONE; | |
s->opt[OPT_HALFTONE].desc = SANE_I18N ("Selects the halftone."); | |
s->opt[OPT_HALFTONE].type = SANE_TYPE_STRING; | |
s->opt[OPT_HALFTONE].size = max_string_size (halftone_list_7); | |
s->opt[OPT_HALFTONE].constraint_type = SANE_CONSTRAINT_STRING_LIST; | |
if (s->hw->level >= 7) | |
s->opt[OPT_HALFTONE].constraint.string_list = halftone_list_7; | |
else if (s->hw->level >= 4) | |
s->opt[OPT_HALFTONE].constraint.string_list = halftone_list_4; | |
else | |
s->opt[OPT_HALFTONE].constraint.string_list = halftone_list; | |
s->val[OPT_HALFTONE].w = 1; /* Halftone A */ | |
if (!s->hw->cmd->set_halftoning) | |
{ | |
s->opt[OPT_HALFTONE].cap |= SANE_CAP_INACTIVE; | |
} | |
/* dropout */ | |
s->opt[OPT_DROPOUT].name = "dropout"; | |
s->opt[OPT_DROPOUT].title = SANE_I18N ("Dropout"); | |
s->opt[OPT_DROPOUT].desc = SANE_I18N ("Selects the dropout."); | |
s->opt[OPT_DROPOUT].type = SANE_TYPE_STRING; | |
s->opt[OPT_DROPOUT].size = max_string_size (dropout_list); | |
s->opt[OPT_DROPOUT].cap |= SANE_CAP_ADVANCED; | |
s->opt[OPT_DROPOUT].constraint_type = SANE_CONSTRAINT_STRING_LIST; | |
s->opt[OPT_DROPOUT].constraint.string_list = dropout_list; | |
s->val[OPT_DROPOUT].w = 0; /* None */ | |
/* brightness algorithm selection */ | |
s->opt[OPT_BRIGHTNESS_METHOD].name = "brightness-method"; | |
s->opt[OPT_BRIGHTNESS_METHOD].title = SANE_I18N ("Brightness Method"); | |
s->opt[OPT_BRIGHTNESS_METHOD].desc = SANE_I18N ("Selects a method to change the brightness of the acquired image."); | |
s->opt[OPT_BRIGHTNESS_METHOD].type = SANE_TYPE_STRING; | |
s->opt[OPT_BRIGHTNESS_METHOD].unit = SANE_UNIT_NONE; | |
s->opt[OPT_BRIGHTNESS_METHOD].size = max_string_size (brightness_method_list); | |
s->opt[OPT_BRIGHTNESS_METHOD].cap |= SANE_CAP_ADVANCED; | |
s->opt[OPT_BRIGHTNESS_METHOD].constraint_type = SANE_CONSTRAINT_STRING_LIST; | |
s->opt[OPT_BRIGHTNESS_METHOD].constraint.string_list = brightness_method_list; s->val[OPT_BRIGHTNESS_METHOD].w = 0; /* first supported */ | |
if (!s->hw->cmd->set_bright) /* skip "hardware" */ | |
++s->opt[OPT_BRIGHTNESS_METHOD].constraint.string_list; | |
/* brightness */ | |
s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; | |
s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; | |
s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; | |
s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT; | |
s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE; | |
s->opt[OPT_BRIGHTNESS].size = sizeof (SANE_Int); | |
s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; | |
s->val[OPT_BRIGHTNESS].w = 0; /* neutral */ | |
if (s->hw->cmd->set_bright) | |
{ | |
s->opt[OPT_BRIGHTNESS].constraint.range = &s->hw->cmd->bright_range; | |
} | |
else | |
{ | |
s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_EMULATED; | |
s->opt[OPT_BRIGHTNESS].constraint.range = &brightness_range; | |
} | |
/* contrast */ | |
s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST; | |
s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST; | |
s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST; | |
s->opt[OPT_CONTRAST].type = SANE_TYPE_INT; | |
s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE; | |
s->opt[OPT_CONTRAST].size = sizeof (SANE_Int); | |
s->opt[OPT_CONTRAST].cap |= SANE_CAP_EMULATED; | |
s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; | |
s->opt[OPT_CONTRAST].constraint.range = &contrast_range; | |
s->val[OPT_CONTRAST].w = 0; /* neutral */ | |
/* sharpness */ | |
s->opt[OPT_SHARPNESS].name = "sharpness"; | |
s->opt[OPT_SHARPNESS].title = SANE_I18N ("Sharpness"); | |
s->opt[OPT_SHARPNESS].desc = ""; | |
s->opt[OPT_SHARPNESS].type = SANE_TYPE_INT; | |
s->opt[OPT_SHARPNESS].unit = SANE_UNIT_NONE; | |
s->opt[OPT_SHARPNESS].constraint_type = SANE_CONSTRAINT_RANGE; | |
s->opt[OPT_SHARPNESS].constraint.range = &outline_emphasis_range; | |
s->val[OPT_SHARPNESS].w = 0; /* Normal */ | |
if (!s->hw->cmd->set_outline_emphasis) | |
{ | |
s->opt[OPT_SHARPNESS].cap |= SANE_CAP_INACTIVE; | |
} | |
/* gamma */ | |
s->opt[OPT_GAMMA_CORRECTION].name = SANE_NAME_GAMMA_CORRECTION; | |
s->opt[OPT_GAMMA_CORRECTION].title = SANE_TITLE_GAMMA_CORRECTION; | |
s->opt[OPT_GAMMA_CORRECTION].desc = SANE_DESC_GAMMA_CORRECTION; | |
s->opt[OPT_GAMMA_CORRECTION].type = SANE_TYPE_STRING; | |
s->opt[OPT_GAMMA_CORRECTION].constraint_type = SANE_CONSTRAINT_STRING_LIST; | |
/* | |
* special handling for D1 function level - at this time I'm not | |
* testing for D1, I'm just assuming that all D level scanners will | |
* behave the same way. This has to be confirmed with the next D-level | |
* scanner | |
*/ | |
if (s->hw->cmd->level[0] == 'D') | |
{ | |
s->opt[OPT_GAMMA_CORRECTION].size = max_string_size (gamma_list_d); | |
s->opt[OPT_GAMMA_CORRECTION].constraint.string_list = gamma_list_d; | |
s->val[OPT_GAMMA_CORRECTION].w = 1; /* Default */ | |
s->hw->gamma_user_defined = gamma_userdefined_d; | |
s->hw->gamma_type = gamma_params_d; | |
} | |
else | |
{ | |
s->opt[OPT_GAMMA_CORRECTION].size = max_string_size (gamma_list_ab); | |
s->opt[OPT_GAMMA_CORRECTION].constraint.string_list = gamma_list_ab; | |
s->val[OPT_GAMMA_CORRECTION].w = 0; /* Default */ | |
s->hw->gamma_user_defined = gamma_userdefined_ab; | |
s->hw->gamma_type = gamma_params_ab; | |
} | |
if (!s->hw->cmd->set_gamma) | |
{ | |
s->opt[OPT_GAMMA_CORRECTION].cap |= SANE_CAP_INACTIVE; | |
} | |
/* red gamma vector */ | |
s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R; | |
s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R; | |
s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R; | |
s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT; | |
s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE; | |
s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word); | |
s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE; | |
s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range; | |
s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[0][0]; | |
/* green gamma vector */ | |
s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G; | |
s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G; | |
s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G; | |
s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT; | |
s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE; | |
s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word); | |
s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE; | |
s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range; | |
s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[1][0]; | |
/* red gamma vector */ | |
s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B; | |
s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B; | |
s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B; | |
s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT; | |
s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE; | |
s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word); | |
s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE; | |
s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range; | |
s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[2][0]; | |
if (s->hw->cmd->set_gamma_table && | |
s->hw->gamma_user_defined[s->val[OPT_GAMMA_CORRECTION].w]) | |
{ | |
s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE; | |
s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE; | |
s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE; | |
} | |
else | |
{ | |
s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE; | |
s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE; | |
s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE; | |
} | |
/* initialize the Gamma tables */ | |
memset (&s->gamma_table[0], 0, 256 * sizeof (SANE_Word)); | |
memset (&s->gamma_table[1], 0, 256 * sizeof (SANE_Word)); | |
memset (&s->gamma_table[2], 0, 256 * sizeof (SANE_Word)); | |
for (i = 0; i < 256; i++) | |
{ | |
s->gamma_table[0][i] = i; | |
s->gamma_table[1][i] = i; | |
s->gamma_table[2][i] = i; | |
} | |
/* color correction */ | |
s->opt[OPT_COLOR_CORRECTION].name = "color-correction"; | |
s->opt[OPT_COLOR_CORRECTION].title = SANE_I18N ("Color correction"); | |
s->opt[OPT_COLOR_CORRECTION].desc = | |
SANE_I18N | |
("Sets the color correction table for the selected output device."); | |
s->opt[OPT_COLOR_CORRECTION].type = SANE_TYPE_STRING; | |
s->opt[OPT_COLOR_CORRECTION].size = 32; | |
s->opt[OPT_COLOR_CORRECTION].cap |= SANE_CAP_ADVANCED; | |
s->opt[OPT_COLOR_CORRECTION].constraint_type = SANE_CONSTRAINT_STRING_LIST; | |
if (s->hw->cmd->level[0] == 'D') | |
{ | |
s->opt[OPT_COLOR_CORRECTION].size = max_string_size (color_list_d); | |
s->opt[OPT_COLOR_CORRECTION].constraint.string_list = color_list_d; | |
s->val[OPT_COLOR_CORRECTION].w = 0; /* Default */ | |
s->hw->color_user_defined = color_userdefined_d; | |
s->hw->color_type = color_params_d; | |
} | |
else | |
{ | |
s->opt[OPT_COLOR_CORRECTION].size = max_string_size (color_list_ab); | |
s->opt[OPT_COLOR_CORRECTION].constraint.string_list = color_list_ab; | |
s->val[OPT_COLOR_CORRECTION].w = 5; /* scanner default: CRT monitors */ | |
s->hw->color_user_defined = color_userdefined_ab; | |
s->hw->color_type = color_params_ab; | |
} | |
if (!s->hw->cmd->set_color_correction) | |
{ | |
s->opt[OPT_COLOR_CORRECTION].cap |= SANE_CAP_INACTIVE; | |
} | |
/* resolution */ | |
s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; | |
s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; | |
s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; | |
s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; | |
s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; | |
if (s->hw->cmd->level[0] != 'D') | |
{ | |
s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; | |
s->opt[OPT_RESOLUTION].constraint.range = &s->hw->dpi_range; | |
} | |
else | |
{ | |
s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; | |
s->opt[OPT_RESOLUTION].constraint.word_list = s->hw->res.list; | |
} | |
handle_resolution (s, OPT_RESOLUTION, DEFAULT_RESOLUTION); | |
#undef SANE_NAME_SCAN_X_RESOLUTION | |
#define SANE_NAME_SCAN_X_RESOLUTION "x-resolution" | |
/* resolution in main scan direction */ | |
s->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_X_RESOLUTION; | |
s->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_X_RESOLUTION; | |
s->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_X_RESOLUTION; | |
s->opt[OPT_X_RESOLUTION].type = SANE_TYPE_INT; | |
s->opt[OPT_X_RESOLUTION].unit = SANE_UNIT_DPI; | |
if (s->hw->cmd->level[0] != 'D') | |
{ | |
s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; | |
s->opt[OPT_X_RESOLUTION].constraint.range = &s->hw->dpi_range; | |
} | |
else | |
{ | |
s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; | |
s->opt[OPT_X_RESOLUTION].constraint.word_list = s->hw->res_x.list; | |
s->opt[OPT_X_RESOLUTION].cap |= SANE_CAP_ADVANCED; | |
} | |
handle_resolution (s, OPT_X_RESOLUTION, DEFAULT_X_RESOLUTION); | |
/* resolution in sub scan direction */ | |
s->opt[OPT_Y_RESOLUTION].name = SANE_NAME_SCAN_Y_RESOLUTION; | |
s->opt[OPT_Y_RESOLUTION].title = SANE_TITLE_SCAN_Y_RESOLUTION; | |
s->opt[OPT_Y_RESOLUTION].desc = SANE_DESC_SCAN_Y_RESOLUTION; | |
s->opt[OPT_Y_RESOLUTION].type = SANE_TYPE_INT; | |
s->opt[OPT_Y_RESOLUTION].unit = SANE_UNIT_DPI; | |
if (s->hw->cmd->level[0] != 'D') | |
{ | |
s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE; | |
s->opt[OPT_Y_RESOLUTION].constraint.range = &s->hw->dpi_range; | |
} | |
else | |
{ | |
s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; | |
s->opt[OPT_Y_RESOLUTION].constraint.word_list = s->hw->res_y.list; | |
s->opt[OPT_Y_RESOLUTION].cap |= SANE_CAP_ADVANCED; | |
} | |
handle_resolution (s, OPT_Y_RESOLUTION, DEFAULT_Y_RESOLUTION); | |
switch (s->hw->productID) | |
{ | |
/* We already use kludges for a number of models and don't want to | |
be burdened with rechecking their functionality. Really fixing | |
their support requires more changes than merely adding main/sub | |
resolution lists. | |
*/ | |
case 0x0116: | |
case 0x0118: | |
case 0x0119: | |
case 0x012b: | |
s->opt[OPT_X_RESOLUTION].cap |= SANE_CAP_INACTIVE; | |
s->opt[OPT_Y_RESOLUTION].cap |= SANE_CAP_INACTIVE; | |
break; | |
default: | |
/* use the default capability set at the top of this function */ | |
; | |
} | |
/* threshold */ | |
s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD; | |
s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD; | |
s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD; | |
s->opt[OPT_THRESHOLD].type = SANE_TYPE_INT; | |
s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE; | |
s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; | |
s->opt[OPT_THRESHOLD].constraint.range = &u8_range; | |
s->val[OPT_THRESHOLD].w = 0x80; | |
if (!s->hw->cmd->set_threshold) | |
{ | |
s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; | |
} | |
s->opt[OPT_CCT_GROUP].title = SANE_I18N ("Color correction coefficients"); | |
s->opt[OPT_CCT_GROUP].desc = SANE_I18N ("Matrix multiplication of RGB"); | |
s->opt[OPT_CCT_GROUP].type = SANE_TYPE_GROUP; | |
s->opt[OPT_CCT_GROUP].cap = SANE_CAP_ADVANCED; | |
/* color correction coefficients */ | |
s->opt[OPT_CCT_1].name = "cct-1"; | |
s->opt[OPT_CCT_2].name = "cct-2"; | |
s->opt[OPT_CCT_3].name = "cct-3"; | |
s->opt[OPT_CCT_4].name = "cct-4"; | |
s->opt[OPT_CCT_5].name = "cct-5"; | |
s->opt[OPT_CCT_6].name = "cct-6"; | |
s->opt[OPT_CCT_7].name = "cct-7"; | |
s->opt[OPT_CCT_8].name = "cct-8"; | |
s->opt[OPT_CCT_9].name = "cct-9"; | |
s->opt[OPT_CCT_1].title = SANE_I18N ("Red"); | |
s->opt[OPT_CCT_2].title = SANE_I18N ("Shift green to red"); | |
s->opt[OPT_CCT_3].title = SANE_I18N ("Shift blue to red"); | |
s->opt[OPT_CCT_4].title = SANE_I18N ("Shift red to green"); | |
s->opt[OPT_CCT_5].title = SANE_I18N ("Green"); | |
s->opt[OPT_CCT_6].title = SANE_I18N ("Shift blue to green"); | |
s->opt[OPT_CCT_7].title = SANE_I18N ("Shift red to blue"); | |
s->opt[OPT_CCT_8].title = SANE_I18N ("Shift green to blue"); | |
s->opt[OPT_CCT_9].title = SANE_I18N ("Blue"); | |
s->opt[OPT_CCT_1].desc = SANE_I18N ("Controls red level"); | |
s->opt[OPT_CCT_2].desc = SANE_I18N ("Adds to red based on green level"); | |
s->opt[OPT_CCT_3].desc = SANE_I18N ("Adds to red based on blue level"); | |
s->opt[OPT_CCT_4].desc = SANE_I18N ("Adds to green based on red level"); | |
s->opt[OPT_CCT_5].desc = SANE_I18N ("Controls green level"); | |
s->opt[OPT_CCT_6].desc = SANE_I18N ("Adds to green based on blue level"); | |
s->opt[OPT_CCT_7].desc = SANE_I18N ("Adds to blue based on red level"); | |
s->opt[OPT_CCT_8].desc = SANE_I18N ("Adds to blue based on green level"); | |
s->opt[OPT_CCT_9].desc = SANE_I18N ("Control blue level"); | |
s->opt[OPT_CCT_1].type = SANE_TYPE_FIXED; | |
s->opt[OPT_CCT_2].type = SANE_TYPE_FIXED; | |
s->opt[OPT_CCT_3].type = SANE_TYPE_FIXED; | |
s->opt[OPT_CCT_4].type = SANE_TYPE_FIXED; | |
s->opt[OPT_CCT_5].type = SANE_TYPE_FIXED; | |
s->opt[OPT_CCT_6].type = SANE_TYPE_FIXED; | |
s->opt[OPT_CCT_7].type = SANE_TYPE_FIXED; | |
s->opt[OPT_CCT_8].type = SANE_TYPE_FIXED; | |
s->opt[OPT_CCT_9].type = SANE_TYPE_FIXED; | |
s->opt[OPT_CCT_1].cap |= SANE_CAP_ADVANCED; | |
s->opt[OPT_CCT_2].cap |= SANE_CAP_ADVANCED; | |
s->opt[OPT_CCT_3].cap |= SANE_CAP_ADVANCED; | |
s->opt[OPT_CCT_4].cap |= SANE_CAP_ADVANCED; | |
s->opt[OPT_CCT_5].cap |= SANE_CAP_ADVANCED; | |
s->opt[OPT_CCT_6].cap |= SANE_CAP_ADVANCED; | |
s->opt[OPT_CCT_7].cap |= SANE_CAP_ADVANCED; | |
s->opt[OPT_CCT_8].cap |= SANE_CAP_ADVANCED; | |
s->opt[OPT_CCT_9].cap |= SANE_CAP_ADVANCED; | |
s->opt[OPT_CCT_1].cap |= SANE_CAP_INACTIVE; | |
s->opt[OPT_CCT_2].cap |= SANE_CAP_INACTIVE; | |
s->opt[OPT_CCT_3].cap |= SANE_CAP_INACTIVE; | |
s->opt[OPT_CCT_4].cap |= SANE_CAP_INACTIVE; | |
s->opt[OPT_CCT_5].cap |= SANE_CAP_INACTIVE; | |
s->opt[OPT_CCT_6].cap |= SANE_CAP_INACTIVE; | |
s->opt[OPT_CCT_7].cap |= SANE_CAP_INACTIVE; | |
s->opt[OPT_CCT_8].cap |= SANE_CAP_INACTIVE; | |
s->opt[OPT_CCT_9].cap |= SANE_CAP_INACTIVE; | |
if (!s->hw->cmd->set_color_correction_coefficients) | |
{ | |
s->opt[OPT_CCT_1].cap |= SANE_CAP_EMULATED; | |
s->opt[OPT_CCT_2].cap |= SANE_CAP_EMULATED; | |
s->opt[OPT_CCT_3].cap |= SANE_CAP_EMULATED; | |
s->opt[OPT_CCT_4].cap |= SANE_CAP_EMULATED; | |
s->opt[OPT_CCT_5].cap |= SANE_CAP_EMULATED; | |
s->opt[OPT_CCT_6].cap |= SANE_CAP_EMULATED; | |
s->opt[OPT_CCT_7].cap |= SANE_CAP_EMULATED; | |
s->opt[OPT_CCT_8].cap |= SANE_CAP_EMULATED; | |
s->opt[OPT_CCT_9].cap |= SANE_CAP_EMULATED; | |
} | |
s->opt[OPT_CCT_1].unit = SANE_UNIT_NONE; | |
s->opt[OPT_CCT_2].unit = SANE_UNIT_NONE; | |
s->opt[OPT_CCT_3].unit = SANE_UNIT_NONE; | |
s->opt[OPT_CCT_4].unit = SANE_UNIT_NONE; | |
s->opt[OPT_CCT_5].unit = SANE_UNIT_NONE; | |
s->opt[OPT_CCT_6].unit = SANE_UNIT_NONE; | |
s->opt[OPT_CCT_7].unit = SANE_UNIT_NONE; | |
s->opt[OPT_CCT_8].unit = SANE_UNIT_NONE; | |
s->opt[OPT_CCT_9].unit = SANE_UNIT_NONE; | |
s->opt[OPT_CCT_1].constraint_type = SANE_CONSTRAINT_RANGE; | |
s->opt[OPT_CCT_2].constraint_type = SANE_CONSTRAINT_RANGE; | |
s->opt[OPT_CCT_3].constraint_type = SANE_CONSTRAINT_RANGE; | |
s->opt[OPT_CCT_4].constraint_type = SANE_CONSTRAINT_RANGE; | |
s->opt[OPT_CCT_5].constraint_type = SANE_CONSTRAINT_RANGE; | |
s->opt[OPT_CCT_6].constraint_type = SANE_CONSTRAINT_RANGE; | |
s->opt[OPT_CCT_7].constraint_type = SANE_CONSTRAINT_RANGE; | |
s->opt[OPT_CCT_8].constraint_type = SANE_CONSTRAINT_RANGE; | |
s->opt[OPT_CCT_9].constraint_type = SANE_CONSTRAINT_RANGE; | |
s->hw->matrix_range.min = SANE_FIX (-2.0); | |
s->hw->matrix_range.max = SANE_FIX (2.0); | |
s->hw->matrix_range.quant = 0; | |
s->opt[OPT_CCT_1].constraint.range = &s->hw->matrix_range; | |
s->opt[OPT_CCT_2].constraint.range = &s->hw->matrix_range; | |
s->opt[OPT_CCT_3].constraint.range = &s->hw->matrix_range; | |
s->opt[OPT_CCT_4].constraint.range = &s->hw->matrix_range; | |
s->opt[OPT_CCT_5].constraint.range = &s->hw->matrix_range; | |
s->opt[OPT_CCT_6].constraint.range = &s->hw->matrix_range; | |
s->opt[OPT_CCT_7].constraint.range = &s->hw->matrix_range; | |
s->opt[OPT_CCT_8].constraint.range = &s->hw->matrix_range; | |
s->opt[OPT_CCT_9].constraint.range = &s->hw->matrix_range; | |
s->val[OPT_CCT_1].w = SANE_FIX (1.0); | |
s->val[OPT_CCT_2].w = 0; | |
s->val[OPT_CCT_3].w = 0; | |
s->val[OPT_CCT_4].w = 0; | |
s->val[OPT_CCT_5].w = SANE_FIX (1.0); | |
s->val[OPT_CCT_6].w = 0; | |
s->val[OPT_CCT_7].w = 0; | |
s->val[OPT_CCT_8].w = 0; | |
s->val[OPT_CCT_9].w = SANE_FIX (1.0); | |
/* "Advanced" group: */ | |
s->opt[OPT_ADVANCED_GROUP].title = SANE_I18N ("Advanced"); | |
s->opt[OPT_ADVANCED_GROUP].desc = ""; | |
s->opt[OPT_ADVANCED_GROUP].type = SANE_TYPE_GROUP; | |
s->opt[OPT_ADVANCED_GROUP].cap = SANE_CAP_ADVANCED; | |
/* mirror */ | |
s->opt[OPT_MIRROR].name = "mirror"; | |
s->opt[OPT_MIRROR].title = SANE_I18N ("Mirror image"); | |
s->opt[OPT_MIRROR].desc = SANE_I18N ("Mirror the image."); | |
s->opt[OPT_MIRROR].type = SANE_TYPE_BOOL; | |
s->val[OPT_MIRROR].w = SANE_FALSE; | |
if (!s->hw->cmd->mirror_image) | |
{ | |
s->opt[OPT_MIRROR].cap |= SANE_CAP_INACTIVE; | |
} | |
/* speed */ | |
s->opt[OPT_SPEED].name = SANE_NAME_SCAN_SPEED; | |
s->opt[OPT_SPEED].title = SANE_TITLE_SCAN_SPEED; | |
s->opt[OPT_SPEED].desc = SANE_DESC_SCAN_SPEED; | |
s->opt[OPT_SPEED].type = SANE_TYPE_BOOL; | |
s->val[OPT_SPEED].w = SANE_FALSE; | |
if (!s->hw->cmd->set_speed) | |
{ | |
s->opt[OPT_SPEED].cap |= SANE_CAP_INACTIVE; | |
} | |
/* preview speed */ | |
s->opt[OPT_PREVIEW_SPEED].name = "preview-speed"; | |
s->opt[OPT_PREVIEW_SPEED].title = SANE_I18N ("Speed"); | |
s->opt[OPT_PREVIEW_SPEED].desc = ""; | |
s->opt[OPT_PREVIEW_SPEED].type = SANE_TYPE_BOOL; | |
s->val[OPT_PREVIEW_SPEED].w = SANE_FALSE; | |
if (!s->hw->cmd->set_speed) | |
{ | |
s->opt[OPT_PREVIEW_SPEED].cap |= SANE_CAP_INACTIVE; | |
} | |
/* auto area segmentation */ | |
s->opt[OPT_AAS].name = "auto-area-segmentation"; | |
s->opt[OPT_AAS].title = SANE_I18N ("Auto area segmentation"); | |
s->opt[OPT_AAS].desc = ""; | |
s->opt[OPT_AAS].type = SANE_TYPE_BOOL; | |
s->val[OPT_AAS].w = SANE_TRUE; | |
if (!s->hw->cmd->control_auto_area_segmentation) | |
{ | |
s->opt[OPT_AAS].cap |= SANE_CAP_INACTIVE; | |
} | |
/* limit resolution list */ | |
s->opt[OPT_LIMIT_RESOLUTION].name = "short-resolution"; | |
s->opt[OPT_LIMIT_RESOLUTION].title = SANE_I18N ("Short resolution list"); | |
s->opt[OPT_LIMIT_RESOLUTION].desc = | |
SANE_I18N ("Display short resolution list"); | |
s->opt[OPT_LIMIT_RESOLUTION].type = SANE_TYPE_BOOL; | |
s->val[OPT_LIMIT_RESOLUTION].w = SANE_FALSE; | |
if (SANE_CONSTRAINT_WORD_LIST != s->opt[OPT_RESOLUTION].constraint_type) | |
{ | |
s->opt[OPT_LIMIT_RESOLUTION].cap |= SANE_CAP_INACTIVE; | |
} | |
/* zoom */ | |
s->opt[OPT_ZOOM].name = "zoom"; | |
s->opt[OPT_ZOOM].title = SANE_I18N ("Zoom"); | |
s->opt[OPT_ZOOM].desc = | |
SANE_I18N ("Defines the zoom factor the scanner will use"); | |
s->opt[OPT_ZOOM].type = SANE_TYPE_INT; | |
s->opt[OPT_ZOOM].unit = SANE_UNIT_NONE; | |
s->opt[OPT_ZOOM].constraint_type = SANE_CONSTRAINT_RANGE; | |
s->opt[OPT_ZOOM].constraint.range = &zoom_range; | |
s->val[OPT_ZOOM].w = 100; | |
if (s->hw->using_fs) s->hw->cmd->set_zoom = 0; | |
if (!s->hw->cmd->set_zoom) | |
{ | |
s->opt[OPT_ZOOM].cap |= SANE_CAP_INACTIVE; | |
} | |
/* "Preview settings" group: */ | |
s->opt[OPT_PREVIEW_GROUP].title = SANE_TITLE_PREVIEW; | |
s->opt[OPT_PREVIEW_GROUP].desc = ""; | |
s->opt[OPT_PREVIEW_GROUP].type = SANE_TYPE_GROUP; | |
s->opt[OPT_PREVIEW_GROUP].cap = SANE_CAP_ADVANCED; | |
/* preview */ | |
s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; | |
s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; | |
s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; | |
s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; | |
s->val[OPT_PREVIEW].w = SANE_FALSE; | |
/* "Geometry" group: */ | |
s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry"); | |
s->opt[OPT_GEOMETRY_GROUP].desc = ""; | |
s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; | |
s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; | |
/* media size oriented scan area setting */ | |
s->opt[OPT_SCAN_AREA].name = "scan-area"; | |
s->opt[OPT_SCAN_AREA].title = SANE_I18N ("Scan area"); | |
s->opt[OPT_SCAN_AREA].desc = | |
SANE_I18N ("Select an area to scan based on well-known media sizes."); | |
s->opt[OPT_SCAN_AREA].type = SANE_TYPE_STRING; | |
s->opt[OPT_SCAN_AREA].size = 0; | |
s->opt[OPT_SCAN_AREA].cap |= SANE_CAP_INACTIVE; | |
s->opt[OPT_SCAN_AREA].constraint_type = SANE_CONSTRAINT_STRING_LIST; | |
s->opt[OPT_SCAN_AREA].constraint.string_list = NULL; | |
s->val[OPT_SCAN_AREA].w = 0; | |
/* Quick format */ | |
s->opt[OPT_QUICK_FORMAT].name = "quick-format"; | |
s->opt[OPT_QUICK_FORMAT].title = SANE_I18N ("Quick format"); | |
s->opt[OPT_QUICK_FORMAT].desc = | |
SANE_I18N ("Select an area to scan based on well-known media sizes. (DEPRECATED)"); | |
s->opt[OPT_QUICK_FORMAT].type = SANE_TYPE_STRING; | |
s->opt[OPT_QUICK_FORMAT].size = 0; | |
s->opt[OPT_QUICK_FORMAT].cap |= SANE_CAP_INACTIVE; | |
s->opt[OPT_QUICK_FORMAT].constraint_type = SANE_CONSTRAINT_STRING_LIST; | |
s->opt[OPT_QUICK_FORMAT].constraint.string_list = NULL; | |
s->val[OPT_QUICK_FORMAT].w = 0; | |
handle_scan_area (s, 0); /* divines device/setting dependent bits */ | |
/* top-left x */ | |
s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; | |
s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; | |
s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; | |
s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; | |
s->opt[OPT_TL_X].unit = SANE_UNIT_MM; | |
s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; | |
s->opt[OPT_TL_X].constraint.range = &(s->hw->src->x_range); | |
s->val[OPT_TL_X].w = 0; | |
/* top-left y */ | |
s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; | |
s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; | |
s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; | |
s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; | |
s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; | |
s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; | |
s->opt[OPT_TL_Y].constraint.range = &(s->hw->src->y_range); | |
s->val[OPT_TL_Y].w = 0; | |
/* bottom-right x */ | |
s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; | |
s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; | |
s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; | |
s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; | |
s->opt[OPT_BR_X].unit = SANE_UNIT_MM; | |
s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; | |
s->opt[OPT_BR_X].constraint.range = &(s->hw->src->x_range); | |
s->val[OPT_BR_X].w = s->hw->src->x_range.max; | |
/* bottom-right y */ | |
s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; | |
s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; | |
s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; | |
s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; | |
s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; | |
s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; | |
s->opt[OPT_BR_Y].constraint.range = &(s->hw->src->y_range); | |
s->val[OPT_BR_Y].w = s->hw->src->y_range.max; | |
/* "Optional equipment" group: */ | |
s->opt[OPT_EQU_GROUP].title = SANE_I18N ("Optional equipment"); | |
s->opt[OPT_EQU_GROUP].desc = ""; | |
s->opt[OPT_EQU_GROUP].type = SANE_TYPE_GROUP; | |
s->opt[OPT_EQU_GROUP].cap = SANE_CAP_ADVANCED; | |
/* source */ | |
s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE; | |
s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE; | |
s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE; | |
s->opt[OPT_SOURCE].type = SANE_TYPE_STRING; | |
s->opt[OPT_SOURCE].size = max_string_size (s->hw->sources); | |
s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; | |
s->opt[OPT_SOURCE].constraint.string_list = s->hw->sources; | |
if (!s->hw->sources[1]) /* two or more scan sources */ | |
{ | |
s->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE; | |
} | |
s->val[OPT_SOURCE].w = 0; | |
/* film type */ | |
s->opt[OPT_FILM_TYPE].name = "film-type"; | |
s->opt[OPT_FILM_TYPE].title = SANE_I18N ("Film type"); | |
s->opt[OPT_FILM_TYPE].desc = ""; | |
s->opt[OPT_FILM_TYPE].type = SANE_TYPE_STRING; | |
s->opt[OPT_FILM_TYPE].size = max_string_size (film_list); | |
s->opt[OPT_FILM_TYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST; | |
s->opt[OPT_FILM_TYPE].constraint.string_list = film_list; | |
s->val[OPT_FILM_TYPE].w = 0; | |
deactivateOption (s, OPT_FILM_TYPE, &dummy); /* default is inactive */ | |
/* focus position */ | |
s->opt[OPT_FOCUS].name = SANE_EPSON_FOCUS_NAME; | |
s->opt[OPT_FOCUS].title = SANE_EPSON_FOCUS_TITLE; | |
s->opt[OPT_FOCUS].desc = SANE_EPSON_FOCUS_DESC; | |
s->opt[OPT_FOCUS].type = SANE_TYPE_STRING; | |
s->opt[OPT_FOCUS].size = max_string_size (focus_list); | |
s->opt[OPT_FOCUS].constraint_type = SANE_CONSTRAINT_STRING_LIST; | |
s->opt[OPT_FOCUS].constraint.string_list = focus_list; | |
s->val[OPT_FOCUS].w = 0; | |
s->opt[OPT_FOCUS].cap |= SANE_CAP_ADVANCED; | |
if (s->hw->tpu && s->hw->tpu->has_focus) | |
{ | |
s->opt[OPT_FOCUS].cap &= ~SANE_CAP_INACTIVE; | |
} | |
else | |
{ | |
s->opt[OPT_FOCUS].cap |= SANE_CAP_INACTIVE; | |
} | |
/* forward feed / eject */ | |
s->opt[OPT_EJECT].name = "eject"; | |
s->opt[OPT_EJECT].title = SANE_I18N ("Eject"); | |
s->opt[OPT_EJECT].desc = SANE_I18N ("Eject the sheet in the ADF"); | |
s->opt[OPT_EJECT].type = SANE_TYPE_BUTTON; | |
if ((!s->hw->adf) && (!s->hw->cmd->set_bay)) | |
{ /* Hack: Using set_bay to indicate. */ | |
s->opt[OPT_EJECT].cap |= SANE_CAP_INACTIVE; | |
} | |
/* auto forward feed / eject */ | |
s->opt[OPT_AUTO_EJECT].name = "auto-eject"; | |
s->opt[OPT_AUTO_EJECT].title = SANE_I18N ("Auto eject"); | |
s->opt[OPT_AUTO_EJECT].desc = SANE_I18N ("Eject document after scanning"); | |
s->opt[OPT_AUTO_EJECT].type = SANE_TYPE_BOOL; | |
s->val[OPT_AUTO_EJECT].w = SANE_TRUE; | |
if (s->hw->adf) s->hw->adf->auto_eject = SANE_TRUE; | |
if (!s->hw->adf) | |
{ | |
s->opt[OPT_AUTO_EJECT].cap |= SANE_CAP_INACTIVE; | |
} | |
s->opt[OPT_ADF_MODE].name = "adf-mode"; | |
s->opt[OPT_ADF_MODE].title = SANE_I18N ("ADF Mode"); | |
s->opt[OPT_ADF_MODE].desc = | |
SANE_I18N ("Selects the ADF mode (simplex/duplex)"); | |
s->opt[OPT_ADF_MODE].type = SANE_TYPE_STRING; | |
s->opt[OPT_ADF_MODE].size = max_string_size (adf_mode_list); | |
s->opt[OPT_ADF_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; | |
s->opt[OPT_ADF_MODE].constraint.string_list = adf_mode_list; | |
s->val[OPT_ADF_MODE].w = 0; /* simplex */ | |
if (!(using (s->hw, adf) && (EXT_STATUS_ADFS & s->hw->ext_status))) | |
{ | |
s->opt[OPT_ADF_MODE].cap |= SANE_CAP_INACTIVE; | |
} | |
/* select bay */ | |
s->opt[OPT_BAY].name = "bay"; | |
s->opt[OPT_BAY].title = SANE_I18N ("Bay"); | |
s->opt[OPT_BAY].desc = SANE_I18N ("Select bay to scan"); | |
s->opt[OPT_BAY].type = SANE_TYPE_STRING; | |
s->opt[OPT_BAY].size = max_string_size (bay_list); | |
s->opt[OPT_BAY].constraint_type = SANE_CONSTRAINT_STRING_LIST; | |
s->opt[OPT_BAY].constraint.string_list = bay_list; | |
s->val[OPT_BAY].w = 0; /* Bay 1 */ | |
if (!s->hw->cmd->set_bay) | |
{ | |
s->opt[OPT_BAY].cap |= SANE_CAP_INACTIVE; | |
} | |
s->opt[OPT_WAIT_FOR_BUTTON].name = SANE_EPSON_WAIT_FOR_BUTTON_NAME; | |
s->opt[OPT_WAIT_FOR_BUTTON].title = SANE_EPSON_WAIT_FOR_BUTTON_TITLE; | |
s->opt[OPT_WAIT_FOR_BUTTON].desc = SANE_EPSON_WAIT_FOR_BUTTON_DESC; | |
s->opt[OPT_WAIT_FOR_BUTTON].type = SANE_TYPE_BOOL; | |
s->opt[OPT_WAIT_FOR_BUTTON].unit = SANE_UNIT_NONE; | |
s->opt[OPT_WAIT_FOR_BUTTON].constraint_type = SANE_CONSTRAINT_NONE; | |
s->opt[OPT_WAIT_FOR_BUTTON].constraint.range = NULL; | |
s->opt[OPT_WAIT_FOR_BUTTON].cap |= SANE_CAP_ADVANCED; | |
s->val[OPT_WAIT_FOR_BUTTON].w = SANE_FALSE; | |
if (!s->hw->cmd->request_push_button_status | |
|| push_button_is_black_listed (s->hw)) | |
{ | |
s->opt[OPT_WAIT_FOR_BUTTON].cap |= SANE_CAP_INACTIVE; | |
} | |
else if (push_button_needs_polling (s->hw)) | |
{ | |
s->val[OPT_WAIT_FOR_BUTTON].w = SANE_TRUE; | |
} | |
s->opt[OPT_MONITOR_BUTTON].name = SANE_EPSON_MONITOR_BUTTON_NAME; | |
s->opt[OPT_MONITOR_BUTTON].title = SANE_EPSON_MONITOR_BUTTON_TITLE; | |
s->opt[OPT_MONITOR_BUTTON].desc = SANE_EPSON_MONITOR_BUTTON_DESC; | |
s->opt[OPT_MONITOR_BUTTON].type = SANE_TYPE_BOOL; | |
s->opt[OPT_MONITOR_BUTTON].unit = SANE_UNIT_NONE; | |
s->opt[OPT_MONITOR_BUTTON].constraint_type = SANE_CONSTRAINT_NONE; | |
s->opt[OPT_MONITOR_BUTTON].constraint.range = NULL; | |
s->opt[OPT_MONITOR_BUTTON].cap |= SANE_CAP_ADVANCED; | |
s->opt[OPT_MONITOR_BUTTON].cap &= ~SANE_CAP_SOFT_SELECT; | |
s->val[OPT_MONITOR_BUTTON].w = SANE_FALSE; | |
if (!s->hw->cmd->request_push_button_status | |
|| push_button_is_black_listed (s->hw)) | |
{ | |
s->opt[OPT_MONITOR_BUTTON].cap |= SANE_CAP_INACTIVE; | |
} | |
s->opt[OPT_POLLING_TIME].name = SANE_EPSON_POLLING_TIME_NAME; | |
s->opt[OPT_POLLING_TIME].title = SANE_EPSON_POLLING_TIME_TITLE; | |
s->opt[OPT_POLLING_TIME].desc = SANE_EPSON_POLLING_TIME_DESC; | |
s->opt[OPT_POLLING_TIME].type = SANE_TYPE_INT; | |
s->opt[OPT_POLLING_TIME].unit = SANE_UNIT_MICROSECOND; | |
s->opt[OPT_POLLING_TIME].constraint_type = SANE_CONSTRAINT_NONE; | |
s->opt[OPT_POLLING_TIME].cap |= SANE_CAP_ADVANCED; | |
s->opt[OPT_POLLING_TIME].cap &= ~SANE_CAP_SOFT_SELECT; | |
s->val[OPT_POLLING_TIME].w = s->hw->polling_time; | |
if (!s->hw->cmd->request_push_button_status | |
|| push_button_is_black_listed (s->hw)) | |
{ | |
s->opt[OPT_POLLING_TIME].cap |= SANE_CAP_INACTIVE; | |
} | |
s->opt[OPT_NEEDS_POLLING].name = SANE_EPSON_NEEDS_POLLING_NAME; | |
s->opt[OPT_NEEDS_POLLING].title = SANE_EPSON_NEEDS_POLLING_TITLE; | |
s->opt[OPT_NEEDS_POLLING].desc = SANE_EPSON_NEEDS_POLLING_DESC; | |
s->opt[OPT_NEEDS_POLLING].type = SANE_TYPE_BOOL; | |
s->opt[OPT_NEEDS_POLLING].unit = SANE_UNIT_NONE; | |
s->opt[OPT_NEEDS_POLLING].constraint_type = SANE_CONSTRAINT_NONE; | |
s->opt[OPT_NEEDS_POLLING].cap |= SANE_CAP_ADVANCED; | |
s->opt[OPT_NEEDS_POLLING].cap &= ~SANE_CAP_SOFT_SELECT; | |
s->val[OPT_NEEDS_POLLING].w = SANE_FALSE; | |
if (!s->hw->cmd->request_push_button_status | |
|| push_button_is_black_listed (s->hw)) | |
{ | |
s->opt[OPT_NEEDS_POLLING].cap |= SANE_CAP_INACTIVE; | |
} | |
else if (push_button_needs_polling (s->hw)) | |
{ | |
s->val[OPT_NEEDS_POLLING].w = SANE_TRUE; | |
} | |
s->opt[OPT_DETECT_DOC_SIZE].name = SANE_EPSON_DETECT_DOC_SIZE_NAME; | |
s->opt[OPT_DETECT_DOC_SIZE].title = SANE_EPSON_DETECT_DOC_SIZE_TITLE; | |
s->opt[OPT_DETECT_DOC_SIZE].desc = SANE_EPSON_DETECT_DOC_SIZE_DESC; | |
s->opt[OPT_DETECT_DOC_SIZE].type = SANE_TYPE_BOOL; | |
s->opt[OPT_DETECT_DOC_SIZE].unit = SANE_UNIT_NONE; | |
s->opt[OPT_DETECT_DOC_SIZE].constraint_type = SANE_CONSTRAINT_NONE; | |
s->opt[OPT_DETECT_DOC_SIZE].constraint.range = NULL; | |
s->opt[OPT_DETECT_DOC_SIZE].cap |= SANE_CAP_ADVANCED; | |
s->val[OPT_DETECT_DOC_SIZE].w = SANE_FALSE; | |
if (!has_size_check_support (s->hw->src)) | |
{ | |
s->opt[OPT_DETECT_DOC_SIZE].cap |= SANE_CAP_INACTIVE; | |
} | |
s->opt[OPT_SCAN_AREA_IS_VALID].name = SANE_EPSON_SCAN_AREA_IS_VALID_NAME; | |
s->opt[OPT_SCAN_AREA_IS_VALID].title = SANE_EPSON_SCAN_AREA_IS_VALID_TITLE; | |
s->opt[OPT_SCAN_AREA_IS_VALID].desc = SANE_EPSON_SCAN_AREA_IS_VALID_DESC; | |
s->opt[OPT_SCAN_AREA_IS_VALID].type = SANE_TYPE_BOOL; | |
s->opt[OPT_SCAN_AREA_IS_VALID].unit = SANE_UNIT_NONE; | |
s->opt[OPT_SCAN_AREA_IS_VALID].constraint_type = SANE_CONSTRAINT_NONE; | |
s->opt[OPT_SCAN_AREA_IS_VALID].constraint.range = NULL; | |
s->opt[OPT_SCAN_AREA_IS_VALID].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; | |
s->val[OPT_SCAN_AREA_IS_VALID].w = SANE_FALSE; | |
s->opt[OPT_ADF_DUPLEX_DIRECTION_MATCHES].name = | |
SANE_EPSON_ADF_DUPLEX_DIRECTION_MATCHES_NAME; | |
s->opt[OPT_ADF_DUPLEX_DIRECTION_MATCHES].title = | |
SANE_EPSON_ADF_DUPLEX_DIRECTION_MATCHES_TITLE; | |
s->opt[OPT_ADF_DUPLEX_DIRECTION_MATCHES].desc = | |
SANE_EPSON_ADF_DUPLEX_DIRECTION_MATCHES_DESC; | |
s->opt[OPT_ADF_DUPLEX_DIRECTION_MATCHES].type = SANE_TYPE_BOOL; | |
s->opt[OPT_ADF_DUPLEX_DIRECTION_MATCHES].unit = SANE_UNIT_NONE; | |
s->opt[OPT_ADF_DUPLEX_DIRECTION_MATCHES].constraint_type = | |
SANE_CONSTRAINT_NONE; | |
s->opt[OPT_ADF_DUPLEX_DIRECTION_MATCHES].constraint.range = NULL; | |
s->opt[OPT_ADF_DUPLEX_DIRECTION_MATCHES].cap = | |
SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; | |
s->val[OPT_ADF_DUPLEX_DIRECTION_MATCHES].w = SANE_FALSE; | |
if (!(using (s->hw, adf) && (EXT_STATUS_ADFS & s->hw->ext_status))) | |
{ | |
s->opt[OPT_ADF_DUPLEX_DIRECTION_MATCHES].cap |= SANE_CAP_INACTIVE; | |
} | |
s->opt[OPT_ADF_AUTO_SCAN].name = SANE_EPSON_ADF_AUTO_SCAN_NAME; | |
s->opt[OPT_ADF_AUTO_SCAN].title = SANE_EPSON_ADF_AUTO_SCAN_TITLE; | |
s->opt[OPT_ADF_AUTO_SCAN].desc = SANE_EPSON_ADF_AUTO_SCAN_DESC; | |
s->opt[OPT_ADF_AUTO_SCAN].type = SANE_TYPE_BOOL; | |
s->opt[OPT_ADF_AUTO_SCAN].unit = SANE_UNIT_NONE; | |
s->opt[OPT_ADF_AUTO_SCAN].constraint_type = SANE_CONSTRAINT_NONE; | |
s->opt[OPT_ADF_AUTO_SCAN].constraint.range = NULL; | |
s->opt[OPT_ADF_AUTO_SCAN].cap |= SANE_CAP_ADVANCED; | |
s->val[OPT_ADF_AUTO_SCAN].w = SANE_FALSE; | |
if (!using (s->hw, adf) || !(FSI_CAP_ADFAS & s->hw->fsi_cap_2)) | |
{ | |
s->opt[OPT_ADF_AUTO_SCAN].cap |= SANE_CAP_INACTIVE; | |
} | |
s->opt[OPT_ADF_DFD_SENSITIVITY].name = SANE_EPSON_ADF_DFD_SENSITIVITY_NAME; | |
s->opt[OPT_ADF_DFD_SENSITIVITY].title = SANE_EPSON_ADF_DFD_SENSITIVITY_TITLE; | |
s->opt[OPT_ADF_DFD_SENSITIVITY].desc = SANE_EPSON_ADF_DFD_SENSITIVITY_DESC; | |
s->opt[OPT_ADF_DFD_SENSITIVITY].type = SANE_TYPE_STRING; | |
s->opt[OPT_ADF_DFD_SENSITIVITY].unit = SANE_UNIT_NONE; | |
s->opt[OPT_ADF_DFD_SENSITIVITY].size = max_string_size (dfd_sensitivity); | |
s->opt[OPT_ADF_DFD_SENSITIVITY].constraint_type = SANE_CONSTRAINT_STRING_LIST; | |
s->opt[OPT_ADF_DFD_SENSITIVITY].constraint.string_list = dfd_sensitivity; | |
s->opt[OPT_ADF_DFD_SENSITIVITY].cap |= SANE_CAP_ADVANCED; | |
s->val[OPT_ADF_DFD_SENSITIVITY].w = 0; | |
if (!using (s->hw, adf) || !(FSI_CAP_DFD & s->hw->fsi_cap_2)) | |
{ | |
s->opt[OPT_ADF_DFD_SENSITIVITY].cap |= SANE_CAP_INACTIVE; | |
} | |
s->opt[OPT_EXT_SANE_STATUS].name = "ext-sane-status"; | |
s->opt[OPT_EXT_SANE_STATUS].title = "Extended SANE Status"; | |
s->opt[OPT_EXT_SANE_STATUS].desc = "Ugly kludge to provide additional status message strings to a frontend."; | |
s->opt[OPT_EXT_SANE_STATUS].type = SANE_TYPE_INT; | |
s->opt[OPT_EXT_SANE_STATUS].unit = SANE_UNIT_NONE; | |
s->opt[OPT_EXT_SANE_STATUS].constraint_type = SANE_CONSTRAINT_RANGE; | |
s->opt[OPT_EXT_SANE_STATUS].constraint.range = &ext_sane_status; | |
s->opt[OPT_EXT_SANE_STATUS].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; | |
s->val[OPT_EXT_SANE_STATUS].w = 0; | |
/* deskew */ | |
s->opt[OPT_DESKEW].name = "deskew"; | |
s->opt[OPT_DESKEW].title = SANE_I18N ("Deskew"); | |
s->opt[OPT_DESKEW].desc = SANE_I18N ("Rotate image so it appears upright."); | |
s->opt[OPT_DESKEW].type = SANE_TYPE_BOOL; | |
s->opt[OPT_DESKEW].unit = SANE_UNIT_NONE; | |
s->opt[OPT_DESKEW].size = sizeof (SANE_Bool); | |
s->opt[OPT_DESKEW].cap |= SANE_CAP_EMULATED; | |
s->opt[OPT_DESKEW].cap |= SANE_CAP_ADVANCED; | |
if ( dip_has_deskew (s->dip, s->hw) ) | |
{ | |
s->opt[OPT_DESKEW].cap &= ~SANE_CAP_INACTIVE; | |
} | |
else | |
{ | |
s->opt[OPT_DESKEW].cap |= SANE_CAP_INACTIVE; | |
} | |
s->opt[OPT_DESKEW].constraint_type = SANE_CONSTRAINT_NONE; | |
s->val[OPT_DESKEW].w = SANE_FALSE; | |
/* auto-crop */ | |
s->opt[OPT_AUTOCROP].name = "autocrop"; | |
s->opt[OPT_AUTOCROP].title = SANE_I18N ("Trim image to paper size"); | |
s->opt[OPT_AUTOCROP].desc = SANE_I18N ("Determines empty margins in the scanned image and removes them. This normally reduces the image to the size of the original document but may remove more."); | |
s->opt[OPT_AUTOCROP].type = SANE_TYPE_BOOL; | |
s->opt[OPT_AUTOCROP].unit = SANE_UNIT_NONE; | |
s->opt[OPT_AUTOCROP].size = sizeof (SANE_Bool); | |
s->opt[OPT_AUTOCROP].cap |= SANE_CAP_EMULATED; | |
s->opt[OPT_AUTOCROP].cap |= SANE_CAP_ADVANCED; | |
if ( dip_has_autocrop (s->dip, s->hw) ) | |
{ | |
s->opt[OPT_AUTOCROP].cap &= ~SANE_CAP_INACTIVE; | |
} | |
else | |
{ | |
s->opt[OPT_AUTOCROP].cap |= SANE_CAP_INACTIVE; | |
} | |
s->opt[OPT_AUTOCROP].constraint_type = SANE_CONSTRAINT_NONE; | |
s->val[OPT_AUTOCROP].w = SANE_FALSE; | |
s->opt[OPT_CALIBRATE].name = SANE_EPSON_CALIBRATE_NAME; | |
s->opt[OPT_CALIBRATE].title = SANE_EPSON_CALIBRATE_TITLE; | |
s->opt[OPT_CALIBRATE].desc = SANE_EPSON_CALIBRATE_DESC; | |
s->opt[OPT_CALIBRATE].type = SANE_TYPE_BUTTON; | |
s->opt[OPT_CALIBRATE].unit = SANE_UNIT_NONE; | |
s->opt[OPT_CALIBRATE].cap |= SANE_CAP_ADVANCED; | |
s->opt[OPT_CALIBRATE].constraint_type = SANE_CONSTRAINT_NONE; | |
if (!maintenance_is_supported (s->hw)) | |
{ | |
s->opt[OPT_CALIBRATE].cap |= SANE_CAP_INACTIVE; | |
} | |
s->opt[OPT_CLEAN].name = SANE_EPSON_CLEAN_NAME; | |
s->opt[OPT_CLEAN].title = SANE_EPSON_CLEAN_TITLE; | |
s->opt[OPT_CLEAN].desc = SANE_EPSON_CLEAN_DESC; | |
s->opt[OPT_CLEAN].type = SANE_TYPE_BUTTON; | |
s->opt[OPT_CLEAN].unit = SANE_UNIT_NONE; | |
s->opt[OPT_CLEAN].cap |= SANE_CAP_ADVANCED; | |
s->opt[OPT_CLEAN].constraint_type = SANE_CONSTRAINT_NONE; | |
if (!maintenance_is_supported (s->hw)) | |
{ | |
s->opt[OPT_CLEAN].cap |= SANE_CAP_INACTIVE; | |
} | |
handle_mode (s, s->val[OPT_MODE].w, &reload); | |
change_profile_matrix (s); | |
/* adjust default settings based on configuration options */ | |
{ | |
void *cfg = cfg_init (NULL, NULL); | |
if (cfg_has_value (cfg, CFG_KEY_OPTION, "prefer-adf")) | |
{ | |
const char *found = NULL; | |
int i = 0; | |
while ((found = s->hw->sources[i]) | |
&& 0 != strcmp_c (ADF_STR, found)) | |
{ | |
++i; | |
} | |
if (found) handle_source (s, i, ADF_STR); | |
} | |
} | |
return SANE_STATUS_GOOD; | |
} | |
SANE_Status | |
epkowa_open (const char *name, SANE_Handle *handle, const void *dip) | |
{ | |
SANE_Status status = SANE_STATUS_GOOD; | |
Epson_Scanner *s = NULL; | |
status = create_sane_handle (&s, name, dip); | |
if (SANE_STATUS_GOOD != status) return status; | |
/* insert newly opened handle into list of open handles */ | |
s->next = first_handle; | |
first_handle = s; | |
*handle = (SANE_Handle) s; | |
return SANE_STATUS_GOOD; | |
} | |
void | |
sane_close (SANE_Handle handle) | |
{ | |
Epson_Scanner *s, *prev; | |
size_t i; | |
/* Test if there is still data pending from the scanner. If so, then | |
* do a cancel. | |
*/ | |
log_call (); | |
s = (Epson_Scanner *) handle; | |
/* remove handle from list of open handles */ | |
prev = 0; | |
for (s = first_handle; s; s = s->next) | |
{ | |
if (s == handle) | |
break; | |
prev = s; | |
} | |
if (!s) | |
{ | |
err_fatal ("invalid handle (0x%p)", handle); | |
return; | |
} | |
if (prev) | |
prev->next = s->next; | |
else | |
first_handle = s->next; | |
s->hw = dev_dtor (s->hw); | |
const_delete (s->opt[OPT_BIT_DEPTH].constraint.word_list, SANE_Word *); | |
const_delete (s->opt[OPT_SCAN_AREA].constraint.string_list, | |
SANE_String_Const *); | |
/* image data acquisition related resources */ | |
delete (s->raw.buf); | |
delete (s->img.buf); | |
for (i = 0; i < LINES_SHUFFLE_MAX; ++i) | |
delete (s->line_buffer[i]); | |
dip_destroy_LUT (s->dip, s->lut); | |
delete (s); | |
} | |
const SANE_Option_Descriptor * | |
sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) | |
{ | |
Epson_Scanner *s = (Epson_Scanner *) handle; | |
if (option < 0 || option >= NUM_OPTIONS) | |
{ | |
log_call ("(%d)", option); | |
return NULL; | |
} | |
log_call ("(%s)", s->opt[option].name); | |
return (s->opt + option); | |
} | |
static const SANE_String_Const * | |
search_string_list (const SANE_String_Const * list, SANE_String value) | |
{ | |
log_call ("(%s)", value); | |
while (*list != NULL && strcmp_c (value, *list) != 0) | |
{ | |
++list; | |
} | |
return ((*list == NULL) ? NULL : list); | |
} | |
/* Activate, deactivate an option. Subroutines so we can add | |
debugging info if we want. The change flag is set to TRUE | |
if we changed an option. If we did not change an option, | |
then the value of the changed flag is not modified. | |
*/ | |
static void | |
activateOption (Epson_Scanner * s, SANE_Int option, SANE_Bool * change) | |
{ | |
log_call ("(%s)", s->opt[option].name); | |
if (!SANE_OPTION_IS_ACTIVE (s->opt[option].cap)) | |
{ | |
s->opt[option].cap &= ~SANE_CAP_INACTIVE; | |
*change = SANE_TRUE; | |
} | |
} | |
static void | |
deactivateOption (Epson_Scanner * s, SANE_Int option, SANE_Bool * change) | |
{ | |
log_call ("(%s)", s->opt[option].name); | |
if (SANE_OPTION_IS_ACTIVE (s->opt[option].cap)) | |
{ | |
s->opt[option].cap |= SANE_CAP_INACTIVE; | |
*change = SANE_TRUE; | |
} | |
} | |
static void | |
setOptionState (Epson_Scanner * s, SANE_Bool state, | |
SANE_Int option, SANE_Bool * change) | |
{ | |
if (state) | |
{ | |
activateOption (s, option, change); | |
} | |
else | |
{ | |
deactivateOption (s, option, change); | |
} | |
} | |
static void | |
calculate_scan_area_offset (const Option_Value *v, int *left, int *top) | |
{ | |
*left = | |
SANE_UNFIX (v[OPT_TL_X].w) / MM_PER_INCH * | |
v[OPT_X_RESOLUTION].w * v[OPT_ZOOM].w / 100 + 0.5; | |
*top = | |
SANE_UNFIX (v[OPT_TL_Y].w) / MM_PER_INCH * | |
v[OPT_Y_RESOLUTION].w * v[OPT_ZOOM].w / 100 + 0.5; | |
} | |
static void | |
calculate_scan_area_max (const Epson_Scanner *s, int *x, int *y) | |
{ | |
/* Cast first value to double to force the whole computation to be | |
done in double. Works around integer overflows. | |
*/ | |
*x = ((double) s->hw->src->max_x * s->val[OPT_X_RESOLUTION].w * | |
s->val[OPT_ZOOM].w / (s->hw->base_res * 100)); | |
*y = ((double) s->hw->src->max_y * s->val[OPT_Y_RESOLUTION].w * | |
s->val[OPT_ZOOM].w / (s->hw->base_res * 100)); | |
} | |
static bool | |
scan_area_is_valid (Epson_Scanner *s) | |
{ | |
int left = 0; | |
int top = 0; | |
int max_x = 0; | |
int max_y = 0; | |
bool rv = true; | |
/* finalize parameters before we validate*/ | |
estimate_parameters (s, NULL); | |
calculate_scan_area_max (s, &max_x, &max_y); | |
calculate_scan_area_offset (s->val, &left, &top); | |
if ( s->raw.ctx.pixels_per_line > max_x) rv = false; | |
if ((left + s->raw.ctx.pixels_per_line) > max_x) rv = false; | |
if (!need_autocrop_override (s)) | |
{ | |
if ( s->raw.ctx.lines > max_y ) rv = false; | |
if ((top + s->raw.ctx.lines) > max_y) rv = false; | |
} | |
/* check physical channel limitations */ | |
{ | |
size_t max_req = s->hw->channel->max_request_size (s->hw->channel); | |
if (s->raw.ctx.bytes_per_line > max_req) rv = false; | |
} | |
if (s->hw->using_fs) | |
{ | |
if (s->raw.ctx.pixels_per_line > s->hw->scan_width_limit) rv = false; | |
return rv; | |
} | |
if (SANE_FRAME_RGB == s->raw.ctx.format) /* max x according to to spec */ | |
if (s->raw.ctx.pixels_per_line > 21840) rv = false; | |
if (top > 65530) rv = false; | |
if (left > 65530) rv = false; | |
return rv; | |
} | |
static SANE_Status | |
getvalue (Epson_Scanner *s, SANE_Int option, void *value) | |
{ | |
SANE_Option_Descriptor *sopt = &(s->opt[option]); | |
Option_Value *sval = &(s->val[option]); | |
log_call ("(%s)", sopt->name); | |
switch (option) | |
{ | |
case OPT_GAMMA_VECTOR_R: | |
case OPT_GAMMA_VECTOR_G: | |
case OPT_GAMMA_VECTOR_B: | |
memcpy (value, sval->wa, sopt->size); | |
break; | |
case OPT_NUM_OPTS: | |
case OPT_RESOLUTION: | |
case OPT_X_RESOLUTION: | |
case OPT_Y_RESOLUTION: | |
case OPT_TL_X: | |
case OPT_TL_Y: | |
case OPT_BR_X: | |
case OPT_BR_Y: | |
case OPT_MIRROR: | |
case OPT_SPEED: | |
case OPT_PREVIEW_SPEED: | |
case OPT_AAS: | |
case OPT_PREVIEW: | |
case OPT_BRIGHTNESS: | |
case OPT_CONTRAST: | |
case OPT_SHARPNESS: | |
case OPT_AUTO_EJECT: | |
case OPT_CCT_1: | |
case OPT_CCT_2: | |
case OPT_CCT_3: | |
case OPT_CCT_4: | |
case OPT_CCT_5: | |
case OPT_CCT_6: | |
case OPT_CCT_7: | |
case OPT_CCT_8: | |
case OPT_CCT_9: | |
case OPT_THRESHOLD: | |
case OPT_ZOOM: | |
case OPT_BIT_DEPTH: | |
case OPT_WAIT_FOR_BUTTON: | |
case OPT_DETECT_DOC_SIZE: | |
case OPT_LIMIT_RESOLUTION: | |
case OPT_ADF_AUTO_SCAN: | |
case OPT_DESKEW: | |
case OPT_AUTOCROP: | |
case OPT_NEEDS_POLLING: | |
*((SANE_Word *) value) = sval->w; | |
break; | |
case OPT_MODE: | |
case OPT_ADF_MODE: | |
case OPT_HALFTONE: | |
case OPT_DROPOUT: | |
case OPT_BRIGHTNESS_METHOD: | |
case OPT_SCAN_AREA: | |
case OPT_SOURCE: | |
case OPT_FILM_TYPE: | |
case OPT_GAMMA_CORRECTION: | |
case OPT_COLOR_CORRECTION: | |
case OPT_BAY: | |
case OPT_FOCUS: | |
case OPT_ADF_DFD_SENSITIVITY: | |
strcpy ((char *) value, sopt->constraint.string_list[sval->w]); | |
break; | |
case OPT_QUICK_FORMAT: | |
getvalue (s, OPT_SCAN_AREA, value); | |
break; | |
case OPT_EXT_SANE_STATUS: | |
if (using (s->hw, adf) | |
&& (ADF_EXT_STATUS_DFE & s->hw->adf->ext_status)) | |
sval->w = EXT_SANE_STATUS_MULTI_FEED; | |
if (using (s->hw, adf) | |
&& (ADF_EXT_STATUS_TR_OPN & s->hw->adf->ext_status)) | |
sval->w = EXT_SANE_STATUS_TRAY_CLOSED; | |
*((SANE_Word *) value) = sval->w; | |
sval->w = 0; | |
break; | |
case OPT_MONITOR_BUTTON: | |
if (SANE_OPTION_IS_ACTIVE (option)) | |
{ | |
SANE_Bool pressed; | |
SANE_Status status = SANE_STATUS_GOOD; | |
if (SANE_STATUS_GOOD == status) | |
{ | |
status = get_push_button_status (s->hw, &pressed); | |
if (SANE_STATUS_GOOD == status) | |
{ | |
*((SANE_Bool *) value) = pressed; | |
} | |
} | |
return status; | |
} | |
else | |
{ | |
return SANE_STATUS_UNSUPPORTED; | |
} | |
break; | |
case OPT_POLLING_TIME: | |
*((SANE_Word *) value) = sval->w; | |
break; | |
case OPT_SCAN_AREA_IS_VALID: | |
{ | |
sval->w = scan_area_is_valid (s); | |
*((SANE_Word *) value) = sval->w; | |
} | |
break; | |
case OPT_ADF_DUPLEX_DIRECTION_MATCHES: | |
{ | |
sval->w = adf_duplex_direction_matches (s->hw); | |
*((SANE_Word *) value) = sval->w; | |
} | |
break; | |
default: | |
return SANE_STATUS_INVAL; | |
} | |
return SANE_STATUS_GOOD; | |
} | |
static void | |
handle_mode (Epson_Scanner * s, SANE_Int optindex, SANE_Bool * reload) | |
{ | |
SANE_Bool dropout, aas, halftone, threshold, cct; | |
SANE_Bool brightness, contrast; | |
log_call (); | |
*reload = SANE_FALSE; | |
switch (optindex) | |
{ | |
case 0: /* b & w */ | |
dropout = SANE_TRUE; | |
aas = SANE_TRUE; | |
halftone = SANE_TRUE; | |
threshold = SANE_TRUE; | |
cct = SANE_FALSE; | |
brightness = SANE_FALSE; | |
contrast = SANE_FALSE; | |
break; | |
case 1: /* gray */ | |
dropout = SANE_TRUE; | |
aas = SANE_FALSE; | |
halftone = SANE_FALSE; | |
threshold = SANE_FALSE; | |
cct = SANE_FALSE; | |
brightness = SANE_TRUE; | |
contrast = SANE_TRUE; | |
break; | |
case 2: /* color */ | |
dropout = SANE_FALSE; | |
aas = SANE_FALSE; | |
halftone = SANE_FALSE; | |
threshold = SANE_FALSE; | |
cct = SANE_TRUE; | |
brightness = SANE_TRUE; | |
contrast = SANE_TRUE; | |
break; | |
default: | |
return; | |
} | |
if (s->hw->cmd->level[0] == 'D') | |
{ | |
dropout = SANE_FALSE; | |
aas = SANE_FALSE; | |
halftone = SANE_FALSE; | |
} | |
setOptionState (s, dropout, OPT_DROPOUT, reload); | |
s->val[OPT_DROPOUT].w = 0; | |
setOptionState (s, halftone, OPT_HALFTONE, reload); | |
s->val[OPT_HALFTONE].w = 0; | |
setOptionState (s, aas, OPT_AAS, reload); | |
s->val[OPT_AAS].w = SANE_FALSE; | |
setOptionState (s, threshold, OPT_THRESHOLD, reload); | |
setOptionState (s, brightness, OPT_BRIGHTNESS, reload); | |
setOptionState (s, contrast, OPT_CONTRAST, reload); | |
setOptionState (s, cct, OPT_CCT_1, reload); | |
setOptionState (s, cct, OPT_CCT_2, reload); | |
setOptionState (s, cct, OPT_CCT_3, reload); | |
setOptionState (s, cct, OPT_CCT_4, reload); | |
setOptionState (s, cct, OPT_CCT_5, reload); | |
setOptionState (s, cct, OPT_CCT_6, reload); | |
setOptionState (s, cct, OPT_CCT_7, reload); | |
setOptionState (s, cct, OPT_CCT_8, reload); | |
setOptionState (s, cct, OPT_CCT_9, reload); | |
/* if binary, then disable the bit depth selection */ | |
if (optindex == 0) | |
{ | |
s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; | |
} | |
else | |
{ | |
if (bitDepthList[0] == 1) | |
s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE; | |
else | |
{ | |
s->opt[OPT_BIT_DEPTH].cap &= ~SANE_CAP_INACTIVE; | |
s->val[OPT_BIT_DEPTH].w = mode_params[optindex].depth; | |
} | |
} | |
if (optindex == 0) /* b & w */ | |
handle_depth_halftone (s, 0, reload); | |
*reload = SANE_TRUE; | |
} | |
static void | |
change_profile_matrix (Epson_Scanner * s) | |
{ | |
int index = 0; | |
log_call (); | |
require (s->hw->scan_hard); | |
if (using (s->hw, tpu)) /* TPU */ | |
{ | |
if (s->val[OPT_FILM_TYPE].w == 0) /* posi */ | |
index = 3; | |
else | |
index = 1; | |
} | |
else /* Flatbed or ADF */ | |
{ | |
index = 0; | |
} | |
s->val[OPT_CCT_1].w = SANE_FIX (s->hw->scan_hard->color_profile[index][0]); | |
s->val[OPT_CCT_2].w = SANE_FIX (s->hw->scan_hard->color_profile[index][1]); | |
s->val[OPT_CCT_3].w = SANE_FIX (s->hw->scan_hard->color_profile[index][2]); | |
s->val[OPT_CCT_4].w = SANE_FIX (s->hw->scan_hard->color_profile[index][3]); | |
s->val[OPT_CCT_5].w = SANE_FIX (s->hw->scan_hard->color_profile[index][4]); | |
s->val[OPT_CCT_6].w = SANE_FIX (s->hw->scan_hard->color_profile[index][5]); | |
s->val[OPT_CCT_7].w = SANE_FIX (s->hw->scan_hard->color_profile[index][6]); | |
s->val[OPT_CCT_8].w = SANE_FIX (s->hw->scan_hard->color_profile[index][7]); | |
s->val[OPT_CCT_9].w = SANE_FIX (s->hw->scan_hard->color_profile[index][8]); | |
} | |
static unsigned char | |
int2cpt (int val) | |
{ | |
if (val >= 0) | |
{ | |
if (val > 127) | |
val = 127; | |
return (unsigned char) val; | |
} | |
else | |
{ | |
val = -val; | |
if (val > 127) | |
val = 127; | |
return (unsigned char) (0x80 | val); | |
} | |
} | |
static void | |
get_colorcoeff_from_profile (double *profile, unsigned char *color_coeff) | |
{ | |
int cc_idx[] = { 4, 1, 7, 3, 0, 6, 5, 2, 8 }; | |
int color_table[9]; | |
int i; | |
round_cct (profile, color_table); | |
for (i = 0; i < 9; i++) | |
color_coeff[i] = int2cpt (color_table[cc_idx[i]]); | |
} | |
static void | |
round_cct (double org_cct[], int rnd_cct[]) | |
{ | |
int i, j, index; | |
double mult_cct[9], frac[9]; | |
int sum[3]; | |
int loop; | |
for (i = 0; i < 9; i++) | |
mult_cct[i] = org_cct[i] * 32; | |
for (i = 0; i < 9; i++) | |
rnd_cct[i] = (int) floor (org_cct[i] * 32 + 0.5); | |
loop = 0; | |
do | |
{ | |
for (i = 0; i < 3; i++) | |
{ | |
if ((rnd_cct[i * 3] == 11) && | |
(rnd_cct[i * 3] == rnd_cct[i * 3 + 1]) && | |
(rnd_cct[i * 3] == rnd_cct[i * 3 + 2])) | |
{ | |
rnd_cct[i * 3 + i]--; | |
mult_cct[i * 3 + i] = rnd_cct[i * 3 + i]; | |
} | |
} | |
for (i = 0; i < 3; i++) | |
{ | |
sum[i] = 0; | |
for (j = 0; j < 3; j++) | |
sum[i] += rnd_cct[i * 3 + j]; | |
} | |
for (i = 0; i < 9; i++) | |
frac[i] = mult_cct[i] - rnd_cct[i]; | |
for (i = 0; i < 3; i++) | |
{ | |
if (sum[i] < 32) | |
{ | |
index = get_roundup_index (&frac[i * 3], 3); | |
if (index != -1) | |
{ | |
rnd_cct[i * 3 + index]++; | |
mult_cct[i * 3 + index] = rnd_cct[i * 3 + index]; | |
sum[i]++; | |
} | |
} | |
else if (sum[i] > 32) | |
{ | |
index = get_rounddown_index (&frac[i * 3], 3); | |
if (index != -1) | |
{ | |
rnd_cct[i * 3 + index]--; | |
mult_cct[i * 3 + index] = rnd_cct[i * 3 + index]; | |
sum[i]--; | |
} | |
} | |
} | |
} | |
while ((++loop < 2) | |
&& ((sum[0] != 32) || (sum[1] != 32) || (sum[2] != 32))); | |
} | |
static int | |
get_roundup_index (double frac[], int n) | |
{ | |
int i, index = -1; | |
double max_val = 0.0; | |
for (i = 0; i < n; i++) | |
{ | |
if (frac[i] < 0) | |
continue; | |
if (max_val < frac[i]) | |
{ | |
index = i; | |
max_val = frac[i]; | |
} | |
} | |
return index; | |
} | |
static int | |
get_rounddown_index (double frac[], int n) | |
{ | |
int i, index = -1; | |
double min_val = 1.0; | |
for (i = 0; i < n; i++) | |
{ | |
if (frac[i] > 0) | |
continue; | |
if (min_val > frac[i]) | |
{ | |
index = i; | |
min_val = frac[i]; | |
} | |
} | |
return index; | |
} | |
/* This routine handles common options between OPT_MODE and | |
OPT_HALFTONE. These options are TET (a HALFTONE mode), AAS | |
- auto area segmentation, and threshold. Apparently AAS | |
is some method to differentiate between text and photos. | |
Or something like that. | |
AAS is available when the scan color depth is 1 and the | |
halftone method is not TET. | |
Threshold is available when halftone is NONE, and depth is 1. | |
*/ | |
static void | |
handle_depth_halftone (Epson_Scanner * s, SANE_Int optindex, | |
SANE_Bool * reload) | |
{ | |
SANE_Bool threshold, aas, dropout; | |
log_call (); | |
*reload = SANE_FALSE; | |
switch (halftone_params[optindex]) | |
{ | |
case HALFTONE_NONE: | |
threshold = SANE_TRUE; | |
aas = SANE_TRUE; | |
dropout = SANE_TRUE; | |
break; | |
case HALFTONE_TET: | |
threshold = SANE_FALSE; | |
aas = SANE_FALSE; | |
dropout = SANE_FALSE; | |
break; | |
default: | |
threshold = SANE_FALSE; | |
aas = SANE_TRUE; | |
dropout = SANE_TRUE; | |
} | |
setOptionState (s, threshold, OPT_THRESHOLD, reload); | |
setOptionState (s, aas, OPT_AAS, reload); | |
setOptionState (s, dropout, OPT_DROPOUT, reload); | |
*reload = SANE_TRUE; | |
} | |
static void | |
handle_resolution (Epson_Scanner * s, SANE_Int option, SANE_Word value) | |
{ | |
SANE_Int *last = NULL; | |
SANE_Int size = 0; | |
SANE_Word *list = NULL; | |
int f = 0; | |
int k = 0; | |
int n = 0; | |
log_call ("(%s, %d)", s->opt[option].name, value); | |
switch (option) | |
{ | |
case OPT_RESOLUTION: | |
last = &s->hw->res.last; | |
size = s->hw->res.size; | |
list = s->hw->res.list; | |
break; | |
case OPT_X_RESOLUTION: | |
last = &s->hw->res_x.last; | |
size = s->hw->res_x.size; | |
list = s->hw->res_x.list; | |
break; | |
case OPT_Y_RESOLUTION: | |
last = &s->hw->res_y.last; | |
size = s->hw->res_y.size; | |
list = s->hw->res_y.list; | |
break; | |
default: | |
err_fatal ("%s", strerror (EINVAL)); | |
exit (EXIT_FAILURE); | |
} | |
if (SANE_CONSTRAINT_RANGE == s->opt[option].constraint_type) | |
{ | |
sanei_constrain_value (&(s->opt[option]), &value, NULL); | |
s->val[option].w = value; | |
} | |
else | |
{ | |
SANE_Int best = list[size]; | |
int min_d = INT_MAX; | |
/* find supported resolution closest to that requested */ | |
for (n = 1; n <= size; n++) | |
{ | |
int d = abs (value - list[n]); | |
if (d < min_d) | |
{ | |
min_d = d; | |
k = n; | |
best = list[n]; | |
} | |
} | |
/* FIXME? what's this trying to do? Use a resolution close to the | |
last one used if best is far away? Why??? */ | |
if ((value != best) && *last) | |
{ | |
for (f = 1; f <= size; f++) | |
if (*last == list[f]) | |
break; | |
if (f != k && f != k - 1 && f != k + 1) | |
{ | |
if (k > f) | |
best = list[f + 1]; | |
else if (k < f) | |
best = list[f - 1]; | |
} | |
} | |
*last = best; | |
s->val[option].w = (SANE_Word) best; | |
} | |
if (OPT_RESOLUTION == option) | |
{ | |
s->val[OPT_X_RESOLUTION].w = s->val[option].w; | |
s->val[OPT_Y_RESOLUTION].w = s->val[option].w; | |
s->hw->res_x.last = s->hw->res.last; | |
s->hw->res_y.last = s->hw->res.last; | |
} | |
{ | |
SANE_Bool dummy; | |
handle_deskew (s, NULL, &dummy); | |
} | |
} | |
static void | |
limit_adf_res (Epson_Scanner * s) | |
{ | |
SANE_Constraint_Type type = s->opt[OPT_RESOLUTION].constraint_type; | |
int limit = large_res_kills_adf_scan (s->hw); | |
if (using (s->hw, adf)) | |
{ | |
dev_limit_res (s->hw, type, limit); | |
/* constrain the current values to the new limit */ | |
handle_resolution (s, OPT_RESOLUTION, s->val[OPT_RESOLUTION].w); | |
handle_resolution (s, OPT_X_RESOLUTION, s->val[OPT_X_RESOLUTION].w); | |
handle_resolution (s, OPT_Y_RESOLUTION, s->val[OPT_Y_RESOLUTION].w); | |
} | |
else | |
{ | |
dev_restore_res (s->hw, type); | |
} | |
} | |
/* | |
Handles setting the source (flatbed, transparency adapter (TPU), | |
or auto document feeder (ADF)). | |
For newer scanners it also sets the focus according to the | |
glass / TPU settings. | |
*/ | |
static SANE_Status | |
handle_source (Epson_Scanner * s, SANE_Int optindex, char *value) | |
{ | |
SANE_Bool dummy; | |
SANE_Status status = SANE_STATUS_GOOD; | |
log_call ("(%s)", value); | |
if (s->val[OPT_SOURCE].w == optindex) | |
return SANE_STATUS_GOOD; | |
if (s->hw->adf && strcmp_c (ADF_STR, value) == 0) | |
{ | |
s->val[OPT_SOURCE].w = optindex; | |
s->hw->src = (const extension *) s->hw->adf; | |
deactivateOption (s, OPT_FILM_TYPE, &dummy); | |
s->val[OPT_FOCUS].w = 0; | |
if (EXT_STATUS_ADFS & s->hw->ext_status) | |
{ | |
activateOption (s, OPT_ADF_MODE, &dummy); | |
activateOption (s, OPT_ADF_DUPLEX_DIRECTION_MATCHES, &dummy); | |
} | |
else | |
{ | |
deactivateOption (s, OPT_ADF_MODE, &dummy); | |
s->val[OPT_ADF_MODE].w = 0; | |
deactivateOption (s, OPT_ADF_DUPLEX_DIRECTION_MATCHES, &dummy); | |
} | |
if (FSI_CAP_ADFAS & s->hw->fsi_cap_2) | |
{ | |
activateOption (s, OPT_ADF_AUTO_SCAN, &dummy); | |
} | |
if (FSI_CAP_DFD & s->hw->fsi_cap_2) | |
{ | |
activateOption (s, OPT_ADF_DFD_SENSITIVITY, &dummy); | |
} | |
else | |
{ | |
deactivateOption (s, OPT_ADF_DFD_SENSITIVITY, &dummy); | |
s->val[OPT_ADF_DFD_SENSITIVITY].w = 0; | |
} | |
} | |
else if (s->hw->tpu && strcmp_c (TPU_STR, value) == 0) | |
{ | |
s->val[OPT_SOURCE].w = optindex; | |
s->hw->src = (const extension *) s->hw->tpu; | |
deactivateOption (s, OPT_ADF_MODE, &dummy); | |
deactivateOption (s, OPT_ADF_AUTO_SCAN, &dummy); | |
deactivateOption (s, OPT_ADF_DFD_SENSITIVITY, &dummy); | |
deactivateOption (s, OPT_EJECT, &dummy); | |
deactivateOption (s, OPT_AUTO_EJECT, &dummy); | |
deactivateOption (s, OPT_ADF_DUPLEX_DIRECTION_MATCHES, &dummy); | |
} | |
else if (s->hw->fbf) | |
{ | |
s->val[OPT_SOURCE].w = optindex; | |
s->hw->src = (const extension *) s->hw->fbf; | |
s->val[OPT_FOCUS].w = 0; | |
deactivateOption (s, OPT_ADF_MODE, &dummy); | |
deactivateOption (s, OPT_ADF_AUTO_SCAN, &dummy); | |
deactivateOption (s, OPT_ADF_DFD_SENSITIVITY, &dummy); | |
deactivateOption (s, OPT_ADF_DUPLEX_DIRECTION_MATCHES, &dummy); | |
} | |
else | |
{ | |
err_fatal ("internal inconsistency"); | |
return SANE_STATUS_INVAL; | |
} | |
/* reset the scanner when we are changing the source setting - | |
this is necessary for the Perfection 1650 */ | |
if (s->hw->need_reset_on_source_change) | |
initialize (s->hw); | |
handle_detect_doc_size (s, NULL, &dummy); | |
/*change*/ | |
status = handle_scan_area(s, s->val[OPT_ADF_MODE].w); | |
change_profile_matrix (s); | |
setOptionState (s, using (s->hw, tpu), OPT_FILM_TYPE, &dummy); | |
setOptionState (s, using (s->hw, adf), OPT_AUTO_EJECT, &dummy); | |
setOptionState (s, using (s->hw, adf), OPT_EJECT, &dummy); | |
if (s->hw->cmd->set_focus_position) | |
{ | |
if (using (s->hw, tpu)) | |
{ | |
s->val[OPT_FOCUS].w = 1; | |
setOptionState (s, SANE_TRUE, OPT_FOCUS, &dummy); | |
} | |
else if (using (s->hw, adf)) | |
{ | |
s->val[OPT_FOCUS].w = 0; | |
setOptionState (s, SANE_FALSE, OPT_FOCUS, &dummy); | |
} | |
else | |
{ | |
s->val[OPT_FOCUS].w = 0; | |
setOptionState (s, SANE_TRUE, OPT_FOCUS, &dummy); | |
} | |
} | |
status = get_resolution_constraints (s->hw, s); | |
if (SANE_STATUS_GOOD != status) | |
{ | |
return status; | |
} | |
if (s->hw->adf) | |
{ | |
if (large_res_kills_adf_scan (s->hw)) limit_adf_res (s); | |
if (zoom_kills_adf_scan (s->hw)) | |
{ | |
if (using (s->hw, adf)) | |
{ | |
s->val[OPT_ZOOM].w = 100; | |
deactivateOption (s, OPT_ZOOM, &dummy); | |
} | |
else | |
{ | |
if (s->hw->cmd->set_zoom) | |
activateOption (s, OPT_ZOOM, &dummy); | |
} | |
} | |
} | |
return status; | |
} | |
static void | |
handle_filmtype (Epson_Scanner * s, SANE_Int optindex, char *value) | |
{ | |
log_call (); | |
value = value; | |
if (!s->hw->tpu || s->val[OPT_FILM_TYPE].w == optindex) | |
return; | |
s->val[OPT_FILM_TYPE].w = optindex; | |