sane-project-backends/backend/kvs1025_opt.c

1581 wiersze
45 KiB
C

/*
Copyright (C) 2008, Panasonic Russia Ltd.
*/
/* sane - Scanner Access Now Easy.
Panasonic KV-S1020C / KV-S1025C USB scanners.
*/
#define DEBUG_DECLARE_ONLY
#include "../include/sane/config.h"
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "../include/sane/sane.h"
#include "../include/sane/saneopts.h"
#include "../include/sane/sanei.h"
#include "../include/sane/sanei_usb.h"
#include "../include/sane/sanei_backend.h"
#include "../include/sane/sanei_config.h"
#include "../include/lassert.h"
#include "kvs1025.h"
#include "kvs1025_low.h"
#include "../include/sane/sanei_debug.h"
/* Option lists */
static SANE_String_Const go_scan_mode_list[] = {
SANE_I18N ("bw"),
SANE_I18N ("halftone"),
SANE_I18N ("gray"),
SANE_I18N ("color"),
NULL
};
/*
static int go_scan_mode_val[] = {
0x00,
0x01,
0x02,
0x05
};*/
static const SANE_Word go_resolutions_list[] = {
11, /* list size */
100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600
};
/* List of scan sources */
static SANE_String_Const go_scan_source_list[] = {
SANE_I18N ("adf"),
SANE_I18N ("fb"),
NULL
};
static const int go_scan_source_val[] = {
0,
0x1
};
/* List of feeder modes */
static SANE_String_Const go_feeder_mode_list[] = {
SANE_I18N ("single"),
SANE_I18N ("continuous"),
NULL
};
static const int go_feeder_mode_val[] = {
0x00,
0xff
};
/* List of manual feed mode */
static SANE_String_Const go_manual_feed_list[] = {
SANE_I18N ("off"),
SANE_I18N ("wait_doc"),
SANE_I18N ("wait_key"),
NULL
};
static const int go_manual_feed_val[] = {
0x00,
0x01,
0x02
};
/* List of paper sizes */
static SANE_String_Const go_paper_list[] = {
SANE_I18N ("user_def"),
SANE_I18N ("business_card"),
SANE_I18N ("Check"),
/*SANE_I18N ("A3"), */
SANE_I18N ("A4"),
SANE_I18N ("A5"),
SANE_I18N ("A6"),
SANE_I18N ("Letter"),
/*SANE_I18N ("Double letter 11x17 in"),
SANE_I18N ("B4"), */
SANE_I18N ("B5"),
SANE_I18N ("B6"),
SANE_I18N ("Legal"),
NULL
};
static const int go_paper_val[] = {
0x00,
0x01,
0x02,
/*0x03, *//* A3 : not supported */
0x04,
0x05,
0x06,
0x07,
/*0x09,
0x0C, *//* Dbl letter and B4 : not supported */
0x0D,
0x0E,
0x0F
};
static const KV_PAPER_SIZE go_paper_sizes[] = {
{210, 297}, /* User defined, default=A4 */
{54, 90}, /* Business card */
{80, 170}, /* Check (China business) */
/*{297, 420}, *//* A3 */
{210, 297}, /* A4 */
{148, 210}, /* A5 */
{105, 148}, /* A6 */
{216, 280}, /* US Letter 8.5 x 11 in */
/*{280, 432}, *//* Double Letter 11 x 17 in */
/*{250, 353}, *//* B4 */
{176, 250}, /* B5 */
{125, 176}, /* B6 */
{216, 356} /* US Legal */
};
static const int default_paper_size_idx = 3; /* A4 */
/* Lists of supported halftone. They are only valid with
* for the Black&White mode. */
static SANE_String_Const go_halftone_pattern_list[] = {
SANE_I18N ("bayer_64"),
SANE_I18N ("bayer_16"),
SANE_I18N ("halftone_32"),
SANE_I18N ("halftone_64"),
SANE_I18N ("diffusion"),
NULL
};
static const int go_halftone_pattern_val[] = {
0x00,
0x01,
0x02,
0x03,
0x04
};
/* List of automatic threshold options */
static SANE_String_Const go_automatic_threshold_list[] = {
SANE_I18N ("normal"),
SANE_I18N ("light"),
SANE_I18N ("dark"),
NULL
};
static const int go_automatic_threshold_val[] = {
0,
0x11,
0x1f
};
/* List of white level base. */
static SANE_String_Const go_white_level_list[] = {
SANE_I18N ("From scanner"),
SANE_I18N ("From paper"),
SANE_I18N ("Automatic"),
NULL
};
static const int go_white_level_val[] = {
0x00,
0x80,
0x81
};
/* List of noise reduction options. */
static SANE_String_Const go_noise_reduction_list[] = {
SANE_I18N ("default"),
"1x1",
"2x2",
"3x3",
"4x4",
"5x5",
NULL
};
static const int go_noise_reduction_val[] = {
0x00,
0x01,
0x02,
0x03,
0x04,
0x05
};
/* List of image emphasis options, 5 steps */
static SANE_String_Const go_image_emphasis_list[] = {
SANE_I18N ("smooth"),
SANE_I18N ("none"),
SANE_I18N ("low"),
SANE_I18N ("medium"), /* default */
SANE_I18N ("high"),
NULL
};
static const int go_image_emphasis_val[] = {
0x14,
0x00,
0x11,
0x12,
0x13
};
/* List of gamma */
static SANE_String_Const go_gamma_list[] = {
SANE_I18N ("normal"),
SANE_I18N ("crt"),
SANE_I18N ("linear"),
NULL
};
static const int go_gamma_val[] = {
0x00,
0x01,
0x02
};
/* List of lamp color dropout */
static SANE_String_Const go_lamp_list[] = {
SANE_I18N ("normal"),
SANE_I18N ("red"),
SANE_I18N ("green"),
SANE_I18N ("blue"),
NULL
};
static const int go_lamp_val[] = {
0x00,
0x01,
0x02,
0x03
};
static SANE_Range go_value_range = { 0, 255, 0 };
static SANE_Range go_jpeg_compression_range = { 0, 0x64, 0 };
static SANE_Range go_rotate_range = { 0, 270, 90 };
static SANE_Range go_swdespeck_range = { 0, 9, 1 };
static SANE_Range go_swskip_range = { SANE_FIX(0), SANE_FIX(100), 1 };
static const char *go_option_name[] = {
"OPT_NUM_OPTS",
/* General options */
"OPT_MODE_GROUP",
"OPT_MODE", /* scanner modes */
"OPT_RESOLUTION", /* X and Y resolution */
"OPT_DUPLEX", /* Duplex mode */
"OPT_SCAN_SOURCE", /* Scan source, fixed to ADF */
"OPT_FEEDER_MODE", /* Feeder mode, fixed to Continuous */
"OPT_LONGPAPER", /* Long paper mode */
"OPT_LENGTHCTL", /* Length control mode */
"OPT_MANUALFEED", /* Manual feed mode */
"OPT_FEED_TIMEOUT", /* Feed timeout */
"OPT_DBLFEED", /* Double feed detection mode */
"OPT_FIT_TO_PAGE", /* Scanner shrinks image to fit scanned page */
/* Geometry group */
"OPT_GEOMETRY_GROUP",
"OPT_PAPER_SIZE", /* Paper size */
"OPT_LANDSCAPE", /* true if landscape */
"OPT_TL_X", /* upper left X */
"OPT_TL_Y", /* upper left Y */
"OPT_BR_X", /* bottom right X */
"OPT_BR_Y", /* bottom right Y */
"OPT_ENHANCEMENT_GROUP",
"OPT_BRIGHTNESS", /* Brightness */
"OPT_CONTRAST", /* Contrast */
"OPT_AUTOMATIC_THRESHOLD", /* Binary threshold */
"OPT_HALFTONE_PATTERN", /* Halftone pattern */
"OPT_AUTOMATIC_SEPARATION", /* Automatic separation */
"OPT_WHITE_LEVEL", /* White level */
"OPT_NOISE_REDUCTION", /* Noise reduction */
"OPT_IMAGE_EMPHASIS", /* Image emphasis */
"OPT_GAMMA", /* Gamma */
"OPT_LAMP", /* Lamp -- color drop out */
"OPT_INVERSE", /* Inverse image */
"OPT_MIRROR", /* Mirror image */
"OPT_JPEG", /* JPEG Compression */
"OPT_ROTATE", /* Rotate image */
"OPT_SWDESKEW", /* Software deskew */
"OPT_SWDESPECK", /* Software despeckle */
"OPT_SWDEROTATE", /* Software detect/correct 90 deg. rotation */
"OPT_SWCROP", /* Software autocrop */
"OPT_SWSKIP", /* Software blank page skip */
/* must come last: */
"OPT_NUM_OPTIONS"
};
/* Round to boundry, return 1 if value modified */
static int
round_to_boundry (SANE_Word * pval, SANE_Word boundry,
SANE_Word minv, SANE_Word maxv)
{
SANE_Word lower, upper, k, v;
v = *pval;
k = v / boundry;
lower = k * boundry;
upper = (k + 1) * boundry;
if (v - lower <= upper - v)
{
*pval = lower;
}
else
{
*pval = upper;
}
if ((*pval) < minv)
*pval = minv;
if ((*pval) > maxv)
*pval = maxv;
return ((*pval) != v);
}
/* Returns the length of the longest string, including the terminating
* character. */
static size_t
max_string_size (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;
}
/* Lookup a string list from one array and return its index. */
static int
get_string_list_index (const SANE_String_Const * list, SANE_String_Const name)
{
int index;
index = 0;
while (list[index] != NULL)
{
if (strcmp (list[index], name) == 0)
{
return (index);
}
index++;
}
DBG (DBG_error, "System bug: option %s not found in list\n", name);
return (-1); /* not found */
}
/* Lookup a string list from one array and return the correnpond value. */
int
get_optval_list (const PKV_DEV dev, int idx,
const SANE_String_Const * str_list, const int *val_list)
{
int index;
index = get_string_list_index (str_list, dev->val[idx].s);
if (index < 0)
index = 0;
return val_list[index];
}
/* Get device mode from device options */
KV_SCAN_MODE
kv_get_mode (const PKV_DEV dev)
{
int i;
i = get_string_list_index (go_scan_mode_list, dev->val[OPT_MODE].s);
switch (i)
{
case 0:
return SM_BINARY;
case 1:
return SM_DITHER;
case 2:
return SM_GRAYSCALE;
case 3:
return SM_COLOR;
default:
assert (0 == 1);
return 0;
}
}
void
kv_calc_paper_size (const PKV_DEV dev, int *w, int *h)
{
int i = get_string_list_index (go_paper_list,
dev->val[OPT_PAPER_SIZE].s);
if (i == 0)
{ /* Non-standard document */
int x_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_X].w));
int y_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_Y].w));
int x_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_X].w));
int y_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_Y].w));
*w = x_br - x_tl;
*h = y_br - y_tl;
}
else
{
if (dev->val[OPT_LANDSCAPE].s)
{
*h = mmToIlu (go_paper_sizes[i].width);
*w = mmToIlu (go_paper_sizes[i].height);
}
else
{
*w = mmToIlu (go_paper_sizes[i].width);
*h = mmToIlu (go_paper_sizes[i].height);
}
}
}
/* Get bit depth from scan mode */
int
kv_get_depth (KV_SCAN_MODE mode)
{
switch (mode)
{
case SM_BINARY:
case SM_DITHER:
return 1;
case SM_GRAYSCALE:
return 8;
case SM_COLOR:
return 24;
default:
assert (0 == 1);
return 0;
}
}
const SANE_Option_Descriptor *
kv_get_option_descriptor (PKV_DEV dev, SANE_Int option)
{
DBG (DBG_proc, "sane_get_option_descriptor: enter, option %s\n",
go_option_name[option]);
if ((unsigned) option >= OPT_NUM_OPTIONS)
{
return NULL;
}
DBG (DBG_proc, "sane_get_option_descriptor: exit\n");
return dev->opt + option;
}
/* Reset the options for that scanner. */
void
kv_init_options (PKV_DEV dev)
{
int i;
if (dev->option_set)
return;
DBG (DBG_proc, "kv_init_options: enter\n");
/* Pre-initialize the options. */
memset (dev->opt, 0, sizeof (dev->opt));
memset (dev->val, 0, sizeof (dev->val));
for (i = 0; i < OPT_NUM_OPTIONS; ++i)
{
dev->opt[i].size = sizeof (SANE_Word);
dev->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
}
/* Number of options. */
dev->opt[OPT_NUM_OPTS].name = "";
dev->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
dev->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
dev->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
dev->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
dev->val[OPT_NUM_OPTS].w = OPT_NUM_OPTIONS;
/* Mode group */
dev->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode");
dev->opt[OPT_MODE_GROUP].desc = ""; /* not valid for a group */
dev->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
dev->opt[OPT_MODE_GROUP].cap = 0;
dev->opt[OPT_MODE_GROUP].size = 0;
dev->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
/* Scanner supported modes */
dev->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
dev->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
dev->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
dev->opt[OPT_MODE].type = SANE_TYPE_STRING;
dev->opt[OPT_MODE].size = max_string_size (go_scan_mode_list);
dev->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
dev->opt[OPT_MODE].constraint.string_list = go_scan_mode_list;
dev->val[OPT_MODE].s = strdup (""); /* will be set later */
/* X and Y resolution */
dev->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
dev->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
dev->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
dev->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
dev->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
dev->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
dev->opt[OPT_RESOLUTION].constraint.word_list = go_resolutions_list;
dev->val[OPT_RESOLUTION].w = go_resolutions_list[3];
/* Duplex */
dev->opt[OPT_DUPLEX].name = SANE_NAME_DUPLEX;
dev->opt[OPT_DUPLEX].title = SANE_TITLE_DUPLEX;
dev->opt[OPT_DUPLEX].desc = SANE_DESC_DUPLEX;
dev->opt[OPT_DUPLEX].type = SANE_TYPE_BOOL;
dev->opt[OPT_DUPLEX].unit = SANE_UNIT_NONE;
dev->val[OPT_DUPLEX].w = SANE_FALSE;
if (!dev->support_info.support_duplex)
dev->opt[OPT_DUPLEX].cap |= SANE_CAP_INACTIVE;
/* Scan source */
dev->opt[OPT_SCAN_SOURCE].name = SANE_NAME_SCAN_SOURCE;
dev->opt[OPT_SCAN_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
dev->opt[OPT_SCAN_SOURCE].desc = SANE_I18N ("Sets the scan source");
dev->opt[OPT_SCAN_SOURCE].type = SANE_TYPE_STRING;
dev->opt[OPT_SCAN_SOURCE].size = max_string_size (go_scan_source_list);
dev->opt[OPT_SCAN_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
dev->opt[OPT_SCAN_SOURCE].constraint.string_list = go_scan_source_list;
dev->val[OPT_SCAN_SOURCE].s = strdup (go_scan_source_list[0]);
dev->opt[OPT_SCAN_SOURCE].cap &= ~SANE_CAP_SOFT_SELECT;
/* for KV-S1020C / KV-S1025C, scan source is fixed to ADF */
/* Feeder mode */
dev->opt[OPT_FEEDER_MODE].name = "feeder-mode";
dev->opt[OPT_FEEDER_MODE].title = SANE_I18N ("Feeder mode");
dev->opt[OPT_FEEDER_MODE].desc = SANE_I18N ("Sets the feeding mode");
dev->opt[OPT_FEEDER_MODE].type = SANE_TYPE_STRING;
dev->opt[OPT_FEEDER_MODE].size = max_string_size (go_feeder_mode_list);
dev->opt[OPT_FEEDER_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
dev->opt[OPT_FEEDER_MODE].constraint.string_list = go_feeder_mode_list;
dev->val[OPT_FEEDER_MODE].s = strdup (go_feeder_mode_list[1]);
/* Long paper */
dev->opt[OPT_LONGPAPER].name = SANE_NAME_LONGPAPER;
dev->opt[OPT_LONGPAPER].title = SANE_TITLE_LONGPAPER;
dev->opt[OPT_LONGPAPER].desc = SANE_I18N ("Enable/Disable long paper mode");
dev->opt[OPT_LONGPAPER].type = SANE_TYPE_BOOL;
dev->opt[OPT_LONGPAPER].unit = SANE_UNIT_NONE;
dev->val[OPT_LONGPAPER].w = SANE_FALSE;
/* Length control */
dev->opt[OPT_LENGTHCTL].name = SANE_NAME_LENGTHCTL;
dev->opt[OPT_LENGTHCTL].title = SANE_TITLE_LENGTHCTL;
dev->opt[OPT_LENGTHCTL].desc =
SANE_I18N ("Enable/Disable length control mode");
dev->opt[OPT_LENGTHCTL].type = SANE_TYPE_BOOL;
dev->opt[OPT_LENGTHCTL].unit = SANE_UNIT_NONE;
dev->val[OPT_LENGTHCTL].w = SANE_TRUE;
/* Manual feed */
dev->opt[OPT_MANUALFEED].name = SANE_NAME_MANUALFEED;
dev->opt[OPT_MANUALFEED].title = SANE_TITLE_MANUALFEED;
dev->opt[OPT_MANUALFEED].desc = SANE_I18N ("Sets the manual feed mode");
dev->opt[OPT_MANUALFEED].type = SANE_TYPE_STRING;
dev->opt[OPT_MANUALFEED].size = max_string_size (go_manual_feed_list);
dev->opt[OPT_MANUALFEED].constraint_type = SANE_CONSTRAINT_STRING_LIST;
dev->opt[OPT_MANUALFEED].constraint.string_list = go_manual_feed_list;
dev->val[OPT_MANUALFEED].s = strdup (go_manual_feed_list[0]);
/*Manual feed timeout */
dev->opt[OPT_FEED_TIMEOUT].name = SANE_NAME_FEED_TIMEOUT;
dev->opt[OPT_FEED_TIMEOUT].title = SANE_TITLE_FEED_TIMEOUT;
dev->opt[OPT_FEED_TIMEOUT].desc =
SANE_I18N ("Sets the manual feed timeout in seconds");
dev->opt[OPT_FEED_TIMEOUT].type = SANE_TYPE_INT;
dev->opt[OPT_FEED_TIMEOUT].unit = SANE_UNIT_NONE;
dev->opt[OPT_FEED_TIMEOUT].size = sizeof (SANE_Int);
dev->opt[OPT_FEED_TIMEOUT].constraint_type = SANE_CONSTRAINT_RANGE;
dev->opt[OPT_FEED_TIMEOUT].constraint.range = &(go_value_range);
dev->opt[OPT_FEED_TIMEOUT].cap |= SANE_CAP_INACTIVE;
dev->val[OPT_FEED_TIMEOUT].w = 30;
/* Double feed */
dev->opt[OPT_DBLFEED].name = SANE_NAME_DBLFEED;
dev->opt[OPT_DBLFEED].title = SANE_TITLE_DBLFEED;
dev->opt[OPT_DBLFEED].desc =
SANE_I18N ("Enable/Disable double feed detection");
dev->opt[OPT_DBLFEED].type = SANE_TYPE_BOOL;
dev->opt[OPT_DBLFEED].unit = SANE_UNIT_NONE;
dev->val[OPT_DBLFEED].w = SANE_FALSE;
/* Fit to page */
dev->opt[OPT_FIT_TO_PAGE].name = SANE_I18N ("fit-to-page");
dev->opt[OPT_FIT_TO_PAGE].title = SANE_I18N ("Fit to page");
dev->opt[OPT_FIT_TO_PAGE].desc =
SANE_I18N ("Scanner shrinks image to fit scanned page");
dev->opt[OPT_FIT_TO_PAGE].type = SANE_TYPE_BOOL;
dev->opt[OPT_FIT_TO_PAGE].unit = SANE_UNIT_NONE;
dev->val[OPT_FIT_TO_PAGE].w = SANE_FALSE;
/* Geometry group */
dev->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry");
dev->opt[OPT_GEOMETRY_GROUP].desc = ""; /* not valid for a group */
dev->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
dev->opt[OPT_GEOMETRY_GROUP].cap = 0;
dev->opt[OPT_GEOMETRY_GROUP].size = 0;
dev->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
/* Paper sizes list */
dev->opt[OPT_PAPER_SIZE].name = SANE_NAME_PAPER_SIZE;
dev->opt[OPT_PAPER_SIZE].title = SANE_TITLE_PAPER_SIZE;
dev->opt[OPT_PAPER_SIZE].desc = SANE_DESC_PAPER_SIZE;
dev->opt[OPT_PAPER_SIZE].type = SANE_TYPE_STRING;
dev->opt[OPT_PAPER_SIZE].size = max_string_size (go_paper_list);
dev->opt[OPT_PAPER_SIZE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
dev->opt[OPT_PAPER_SIZE].constraint.string_list = go_paper_list;
dev->val[OPT_PAPER_SIZE].s = strdup (""); /* will be set later */
/* Landscape */
dev->opt[OPT_LANDSCAPE].name = SANE_NAME_LANDSCAPE;
dev->opt[OPT_LANDSCAPE].title = SANE_TITLE_LANDSCAPE;
dev->opt[OPT_LANDSCAPE].desc =
SANE_I18N ("Set paper position : "
"true for landscape, false for portrait");
dev->opt[OPT_LANDSCAPE].type = SANE_TYPE_BOOL;
dev->opt[OPT_LANDSCAPE].unit = SANE_UNIT_NONE;
dev->val[OPT_LANDSCAPE].w = SANE_FALSE;
/* Upper left X */
dev->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
dev->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
dev->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
dev->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
dev->opt[OPT_TL_X].unit = SANE_UNIT_MM;
dev->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
dev->opt[OPT_TL_X].constraint.range = &(dev->x_range);
/* Upper left Y */
dev->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
dev->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
dev->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
dev->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
dev->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
dev->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
dev->opt[OPT_TL_Y].constraint.range = &(dev->y_range);
/* Bottom-right x */
dev->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
dev->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
dev->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
dev->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
dev->opt[OPT_BR_X].unit = SANE_UNIT_MM;
dev->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
dev->opt[OPT_BR_X].constraint.range = &(dev->x_range);
/* Bottom-right y */
dev->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
dev->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
dev->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
dev->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
dev->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
dev->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
dev->opt[OPT_BR_Y].constraint.range = &(dev->y_range);
/* Enhancement group */
dev->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
dev->opt[OPT_ENHANCEMENT_GROUP].desc = ""; /* not valid for a group */
dev->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
dev->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED;
dev->opt[OPT_ENHANCEMENT_GROUP].size = 0;
dev->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
/* Brightness */
dev->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
dev->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
dev->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
dev->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
dev->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
dev->opt[OPT_BRIGHTNESS].size = sizeof (SANE_Int);
dev->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
dev->opt[OPT_BRIGHTNESS].constraint.range = &(go_value_range);
dev->val[OPT_BRIGHTNESS].w = 128;
/* Contrast */
dev->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
dev->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
dev->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
dev->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
dev->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
dev->opt[OPT_CONTRAST].size = sizeof (SANE_Int);
dev->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
dev->opt[OPT_CONTRAST].constraint.range = &(go_value_range);
dev->val[OPT_CONTRAST].w = 128;
/* Automatic threshold */
dev->opt[OPT_AUTOMATIC_THRESHOLD].name = "automatic-threshold";
dev->opt[OPT_AUTOMATIC_THRESHOLD].title = SANE_I18N ("Automatic threshold");
dev->opt[OPT_AUTOMATIC_THRESHOLD].desc =
SANE_I18N
("Automatically sets brightness, contrast, white level, "
"gamma, noise reduction and image emphasis");
dev->opt[OPT_AUTOMATIC_THRESHOLD].type = SANE_TYPE_STRING;
dev->opt[OPT_AUTOMATIC_THRESHOLD].size =
max_string_size (go_automatic_threshold_list);
dev->opt[OPT_AUTOMATIC_THRESHOLD].constraint_type =
SANE_CONSTRAINT_STRING_LIST;
dev->opt[OPT_AUTOMATIC_THRESHOLD].constraint.string_list =
go_automatic_threshold_list;
dev->val[OPT_AUTOMATIC_THRESHOLD].s =
strdup (go_automatic_threshold_list[0]);
/* Halftone pattern */
dev->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN;
dev->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN;
dev->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN;
dev->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_STRING;
dev->opt[OPT_HALFTONE_PATTERN].size =
max_string_size (go_halftone_pattern_list);
dev->opt[OPT_HALFTONE_PATTERN].constraint_type =
SANE_CONSTRAINT_STRING_LIST;
dev->opt[OPT_HALFTONE_PATTERN].constraint.string_list =
go_halftone_pattern_list;
dev->val[OPT_HALFTONE_PATTERN].s = strdup (go_halftone_pattern_list[0]);
/* Automatic separation */
dev->opt[OPT_AUTOMATIC_SEPARATION].name = SANE_NAME_AUTOSEP;
dev->opt[OPT_AUTOMATIC_SEPARATION].title = SANE_TITLE_AUTOSEP;
dev->opt[OPT_AUTOMATIC_SEPARATION].desc = SANE_DESC_AUTOSEP;
dev->opt[OPT_AUTOMATIC_SEPARATION].type = SANE_TYPE_BOOL;
dev->opt[OPT_AUTOMATIC_SEPARATION].unit = SANE_UNIT_NONE;
dev->val[OPT_AUTOMATIC_SEPARATION].w = SANE_FALSE;
/* White level base */
dev->opt[OPT_WHITE_LEVEL].name = SANE_NAME_WHITE_LEVEL;
dev->opt[OPT_WHITE_LEVEL].title = SANE_TITLE_WHITE_LEVEL;
dev->opt[OPT_WHITE_LEVEL].desc = SANE_DESC_WHITE_LEVEL;
dev->opt[OPT_WHITE_LEVEL].type = SANE_TYPE_STRING;
dev->opt[OPT_WHITE_LEVEL].size = max_string_size (go_white_level_list);
dev->opt[OPT_WHITE_LEVEL].constraint_type = SANE_CONSTRAINT_STRING_LIST;
dev->opt[OPT_WHITE_LEVEL].constraint.string_list = go_white_level_list;
dev->val[OPT_WHITE_LEVEL].s = strdup (go_white_level_list[0]);
/* Noise reduction */
dev->opt[OPT_NOISE_REDUCTION].name = "noise-reduction";
dev->opt[OPT_NOISE_REDUCTION].title = SANE_I18N ("Noise reduction");
dev->opt[OPT_NOISE_REDUCTION].desc =
SANE_I18N ("Reduce the isolated dot noise");
dev->opt[OPT_NOISE_REDUCTION].type = SANE_TYPE_STRING;
dev->opt[OPT_NOISE_REDUCTION].size =
max_string_size (go_noise_reduction_list);
dev->opt[OPT_NOISE_REDUCTION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
dev->opt[OPT_NOISE_REDUCTION].constraint.string_list =
go_noise_reduction_list;
dev->val[OPT_NOISE_REDUCTION].s = strdup (go_noise_reduction_list[0]);
/* Image emphasis */
dev->opt[OPT_IMAGE_EMPHASIS].name = "image-emphasis";
dev->opt[OPT_IMAGE_EMPHASIS].title = SANE_I18N ("Image emphasis");
dev->opt[OPT_IMAGE_EMPHASIS].desc = SANE_I18N ("Sets the image emphasis");
dev->opt[OPT_IMAGE_EMPHASIS].type = SANE_TYPE_STRING;
dev->opt[OPT_IMAGE_EMPHASIS].size =
max_string_size (go_image_emphasis_list);
dev->opt[OPT_IMAGE_EMPHASIS].constraint_type = SANE_CONSTRAINT_STRING_LIST;
dev->opt[OPT_IMAGE_EMPHASIS].constraint.string_list =
go_image_emphasis_list;
dev->val[OPT_IMAGE_EMPHASIS].s = strdup (SANE_I18N ("medium"));
/* Gamma */
dev->opt[OPT_GAMMA].name = "gamma";
dev->opt[OPT_GAMMA].title = SANE_I18N ("Gamma");
dev->opt[OPT_GAMMA].desc = SANE_I18N ("Gamma");
dev->opt[OPT_GAMMA].type = SANE_TYPE_STRING;
dev->opt[OPT_GAMMA].size = max_string_size (go_gamma_list);
dev->opt[OPT_GAMMA].constraint_type = SANE_CONSTRAINT_STRING_LIST;
dev->opt[OPT_GAMMA].constraint.string_list = go_gamma_list;
dev->val[OPT_GAMMA].s = strdup (go_gamma_list[0]);
/* Lamp color dropout */
dev->opt[OPT_LAMP].name = "lamp-color";
dev->opt[OPT_LAMP].title = SANE_I18N ("Lamp color");
dev->opt[OPT_LAMP].desc = SANE_I18N ("Sets the lamp color (color dropout)");
dev->opt[OPT_LAMP].type = SANE_TYPE_STRING;
dev->opt[OPT_LAMP].size = max_string_size (go_lamp_list);
dev->opt[OPT_LAMP].constraint_type = SANE_CONSTRAINT_STRING_LIST;
dev->opt[OPT_LAMP].constraint.string_list = go_lamp_list;
dev->val[OPT_LAMP].s = strdup (go_lamp_list[0]);
if (!dev->support_info.support_lamp)
dev->opt[OPT_LAMP].cap |= SANE_CAP_INACTIVE;
/* Inverse image */
dev->opt[OPT_INVERSE].name = SANE_NAME_INVERSE;
dev->opt[OPT_INVERSE].title = SANE_TITLE_INVERSE;
dev->opt[OPT_INVERSE].desc =
SANE_I18N ("Inverse image in B/W or halftone mode");
dev->opt[OPT_INVERSE].type = SANE_TYPE_BOOL;
dev->opt[OPT_INVERSE].unit = SANE_UNIT_NONE;
dev->val[OPT_INVERSE].w = SANE_FALSE;
/* Mirror image (left/right flip) */
dev->opt[OPT_MIRROR].name = SANE_NAME_MIRROR;
dev->opt[OPT_MIRROR].title = SANE_TITLE_MIRROR;
dev->opt[OPT_MIRROR].desc = SANE_I18N ("Mirror image (left/right flip)");
dev->opt[OPT_MIRROR].type = SANE_TYPE_BOOL;
dev->opt[OPT_MIRROR].unit = SANE_UNIT_NONE;
dev->val[OPT_MIRROR].w = SANE_FALSE;
/* JPEG Image Compression */
dev->opt[OPT_JPEG].name = "jpeg";
dev->opt[OPT_JPEG].title = SANE_I18N ("jpeg compression");
dev->opt[OPT_JPEG].desc =
SANE_I18N
("JPEG Image Compression with Q parameter, '0' - no compression");
dev->opt[OPT_JPEG].type = SANE_TYPE_INT;
dev->opt[OPT_JPEG].unit = SANE_UNIT_NONE;
dev->opt[OPT_JPEG].size = sizeof (SANE_Int);
dev->opt[OPT_JPEG].constraint_type = SANE_CONSTRAINT_RANGE;
dev->opt[OPT_JPEG].constraint.range = &(go_jpeg_compression_range);
dev->val[OPT_JPEG].w = 0;
/* Image Rotation */
dev->opt[OPT_ROTATE].name = "rotate";
dev->opt[OPT_ROTATE].title = SANE_I18N ("Rotate image clockwise");
dev->opt[OPT_ROTATE].desc =
SANE_I18N("Request driver to rotate pages by a fixed amount");
dev->opt[OPT_ROTATE].type = SANE_TYPE_INT;
dev->opt[OPT_ROTATE].unit = SANE_UNIT_NONE;
dev->opt[OPT_ROTATE].size = sizeof (SANE_Int);
dev->opt[OPT_ROTATE].constraint_type = SANE_CONSTRAINT_RANGE;
dev->opt[OPT_ROTATE].constraint.range = &(go_rotate_range);
dev->val[OPT_ROTATE].w = 0;
/* Software Deskew */
dev->opt[OPT_SWDESKEW].name = "swdeskew";
dev->opt[OPT_SWDESKEW].title = SANE_I18N ("Software deskew");
dev->opt[OPT_SWDESKEW].desc =
SANE_I18N("Request driver to rotate skewed pages digitally");
dev->opt[OPT_SWDESKEW].type = SANE_TYPE_BOOL;
dev->opt[OPT_SWDESKEW].unit = SANE_UNIT_NONE;
dev->val[OPT_SWDESKEW].w = SANE_FALSE;
/* Software Despeckle */
dev->opt[OPT_SWDESPECK].name = "swdespeck";
dev->opt[OPT_SWDESPECK].title = SANE_I18N ("Software despeckle diameter");
dev->opt[OPT_SWDESPECK].desc =
SANE_I18N("Maximum diameter of lone dots to remove from scan");
dev->opt[OPT_SWDESPECK].type = SANE_TYPE_INT;
dev->opt[OPT_SWDESPECK].unit = SANE_UNIT_NONE;
dev->opt[OPT_SWDESPECK].size = sizeof (SANE_Int);
dev->opt[OPT_SWDESPECK].constraint_type = SANE_CONSTRAINT_RANGE;
dev->opt[OPT_SWDESPECK].constraint.range = &(go_swdespeck_range);
dev->val[OPT_SWDESPECK].w = 0;
/* Software Derotate */
dev->opt[OPT_SWDEROTATE].name = "swderotate";
dev->opt[OPT_SWDEROTATE].title = SANE_I18N ("Software derotate");
dev->opt[OPT_SWDEROTATE].desc =
SANE_I18N("Request driver to detect and correct 90 degree image rotation");
dev->opt[OPT_SWDEROTATE].type = SANE_TYPE_BOOL;
dev->opt[OPT_SWDEROTATE].unit = SANE_UNIT_NONE;
dev->val[OPT_SWDEROTATE].w = SANE_FALSE;
/* Software Autocrop*/
dev->opt[OPT_SWCROP].name = "swcrop";
dev->opt[OPT_SWCROP].title = SANE_I18N ("Software automatic cropping");
dev->opt[OPT_SWCROP].desc =
SANE_I18N("Request driver to remove border from pages digitally");
dev->opt[OPT_SWCROP].type = SANE_TYPE_BOOL;
dev->opt[OPT_SWCROP].unit = SANE_UNIT_NONE;
dev->val[OPT_SWCROP].w = SANE_FALSE;
/* Software blank page skip */
dev->opt[OPT_SWSKIP].name = "swskip";
dev->opt[OPT_SWSKIP].title = SANE_I18N ("Software blank skip percentage");
dev->opt[OPT_SWSKIP].desc
= SANE_I18N("Request driver to discard pages with low numbers of dark pixels");
dev->opt[OPT_SWSKIP].type = SANE_TYPE_FIXED;
dev->opt[OPT_SWSKIP].unit = SANE_UNIT_PERCENT;
dev->opt[OPT_SWSKIP].constraint_type = SANE_CONSTRAINT_RANGE;
dev->opt[OPT_SWSKIP].constraint.range = &(go_swskip_range);
/* Lastly, set the default scan mode. This might change some
* values previously set here. */
sane_control_option (dev, OPT_PAPER_SIZE, SANE_ACTION_SET_VALUE,
(void *) go_paper_list[default_paper_size_idx], NULL);
sane_control_option (dev, OPT_MODE, SANE_ACTION_SET_VALUE,
(void *) go_scan_mode_list[0], NULL);
DBG (DBG_proc, "kv_init_options: exit\n");
dev->option_set = 1;
}
SANE_Status
kv_control_option (PKV_DEV dev, SANE_Int option,
SANE_Action action, void *val, SANE_Int * info)
{
SANE_Status status;
SANE_Word cap;
SANE_String_Const name;
int i;
SANE_Word value;
DBG (DBG_proc, "sane_control_option: enter, option %s, action %s\n",
go_option_name[option], action == SANE_ACTION_GET_VALUE ? "R" : "W");
if (info)
{
*info = 0;
}
if (dev->scanning)
{
return SANE_STATUS_DEVICE_BUSY;
}
if (option < 0 || option >= OPT_NUM_OPTIONS)
{
return SANE_STATUS_UNSUPPORTED;
}
cap = dev->opt[option].cap;
if (!SANE_OPTION_IS_ACTIVE (cap))
{
return SANE_STATUS_UNSUPPORTED;
}
name = dev->opt[option].name;
if (!name)
{
name = "(no name)";
}
if (action == SANE_ACTION_GET_VALUE)
{
switch (option)
{
/* word options */
case OPT_NUM_OPTS:
case OPT_LONGPAPER:
case OPT_LENGTHCTL:
case OPT_DBLFEED:
case OPT_RESOLUTION:
case OPT_TL_Y:
case OPT_BR_Y:
case OPT_TL_X:
case OPT_BR_X:
case OPT_BRIGHTNESS:
case OPT_CONTRAST:
case OPT_DUPLEX:
case OPT_LANDSCAPE:
case OPT_AUTOMATIC_SEPARATION:
case OPT_INVERSE:
case OPT_MIRROR:
case OPT_FEED_TIMEOUT:
case OPT_JPEG:
case OPT_ROTATE:
case OPT_SWDESKEW:
case OPT_SWDESPECK:
case OPT_SWDEROTATE:
case OPT_SWCROP:
case OPT_SWSKIP:
case OPT_FIT_TO_PAGE:
*(SANE_Word *) val = dev->val[option].w;
DBG (DBG_error, "opt value = %d\n", *(SANE_Word *) val);
return SANE_STATUS_GOOD;
/* string options */
case OPT_MODE:
case OPT_FEEDER_MODE:
case OPT_SCAN_SOURCE:
case OPT_MANUALFEED:
case OPT_HALFTONE_PATTERN:
case OPT_PAPER_SIZE:
case OPT_AUTOMATIC_THRESHOLD:
case OPT_WHITE_LEVEL:
case OPT_NOISE_REDUCTION:
case OPT_IMAGE_EMPHASIS:
case OPT_GAMMA:
case OPT_LAMP:
strcpy (val, dev->val[option].s);
DBG (DBG_error, "opt value = %s\n", (char *) val);
return SANE_STATUS_GOOD;
default:
return SANE_STATUS_UNSUPPORTED;
}
}
else if (action == SANE_ACTION_SET_VALUE)
{
if (!SANE_OPTION_IS_SETTABLE (cap))
{
DBG (DBG_error,
"could not set option %s, not settable\n",
go_option_name[option]);
return SANE_STATUS_INVAL;
}
status = sanei_constrain_value (dev->opt + option, val, info);
if (status != SANE_STATUS_GOOD)
{
DBG (DBG_error, "could not set option, invalid value\n");
return status;
}
switch (option)
{
/* Side-effect options */
case OPT_TL_Y:
case OPT_BR_Y:
case OPT_RESOLUTION:
if (info)
{
*info |= SANE_INFO_RELOAD_PARAMS;
}
dev->val[option].w = *(SANE_Word *) val;
if (option == OPT_RESOLUTION)
{
if (round_to_boundry (&(dev->val[option].w),
dev->support_info.
step_resolution, 100, 600))
{
if (info)
{
*info |= SANE_INFO_INEXACT;
}
}
}
else if (option == OPT_TL_Y)
{
if (dev->val[option].w > dev->val[OPT_BR_Y].w)
{
dev->val[option].w = dev->val[OPT_BR_Y].w;
if (info)
{
*info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT;
}
}
}
else
{
if (dev->val[option].w < dev->val[OPT_TL_Y].w)
{
dev->val[option].w = dev->val[OPT_TL_Y].w;
if (info)
{
*info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT;
}
}
}
DBG (DBG_error,
"option %s, input = %d, value = %d\n",
go_option_name[option], (*(SANE_Word *) val),
dev->val[option].w);
return SANE_STATUS_GOOD;
/* The length of X must be rounded (up). */
case OPT_TL_X:
case OPT_BR_X:
{
SANE_Word xr = dev->val[OPT_RESOLUTION].w;
SANE_Word tl_x = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_X].w)) * xr;
SANE_Word br_x = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_X].w)) * xr;
value = mmToIlu (SANE_UNFIX (*(SANE_Word *) val)) * xr; /* XR * W */
if (option == OPT_TL_X)
{
SANE_Word max = KV_PIXEL_MAX * xr - KV_PIXEL_ROUND;
if (br_x < max)
max = br_x;
if (round_to_boundry (&value, KV_PIXEL_ROUND, 0, max))
{
if (info)
{
*info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT;
}
}
}
else
{
if (round_to_boundry
(&value, KV_PIXEL_ROUND, tl_x, KV_PIXEL_MAX * xr))
{
if (info)
{
*info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT;
}
}
}
dev->val[option].w = SANE_FIX (iluToMm ((double) value / xr));
if (info)
{
*info |= SANE_INFO_RELOAD_PARAMS;
}
DBG (DBG_error,
"option %s, input = %d, value = %d\n",
go_option_name[option], (*(SANE_Word *) val),
dev->val[option].w);
return SANE_STATUS_GOOD;
}
case OPT_LANDSCAPE:
dev->val[option].w = *(SANE_Word *) val;
if (info)
{
*info |= SANE_INFO_RELOAD_PARAMS;
}
return SANE_STATUS_GOOD;
/* Side-effect free options */
case OPT_CONTRAST:
case OPT_BRIGHTNESS:
case OPT_DUPLEX:
case OPT_LONGPAPER:
case OPT_LENGTHCTL:
case OPT_DBLFEED:
case OPT_INVERSE:
case OPT_MIRROR:
case OPT_AUTOMATIC_SEPARATION:
case OPT_JPEG:
case OPT_ROTATE:
case OPT_SWDESKEW:
case OPT_SWDESPECK:
case OPT_SWDEROTATE:
case OPT_SWCROP:
case OPT_SWSKIP:
case OPT_FIT_TO_PAGE:
dev->val[option].w = *(SANE_Word *) val;
return SANE_STATUS_GOOD;
case OPT_FEED_TIMEOUT:
dev->val[option].w = *(SANE_Word *) val;
return CMD_set_timeout (dev, *(SANE_Word *) val);
/* String mode */
case OPT_SCAN_SOURCE:
case OPT_WHITE_LEVEL:
case OPT_NOISE_REDUCTION:
case OPT_IMAGE_EMPHASIS:
case OPT_GAMMA:
case OPT_LAMP:
case OPT_HALFTONE_PATTERN:
case OPT_FEEDER_MODE:
if (strcmp (dev->val[option].s, val) == 0)
return SANE_STATUS_GOOD;
free (dev->val[option].s);
dev->val[option].s = (SANE_String) strdup (val);
if (option == OPT_FEEDER_MODE &&
get_string_list_index (go_feeder_mode_list,
dev->val[option].s) == 1)
/* continuous mode */
{
free (dev->val[OPT_SCAN_SOURCE].s);
dev->val[OPT_SCAN_SOURCE].s = strdup (go_scan_source_list[0]);
dev->opt[OPT_LONGPAPER].cap &= ~SANE_CAP_INACTIVE;
if (info)
*info |= SANE_INFO_RELOAD_OPTIONS;
}
else
{
dev->opt[OPT_LONGPAPER].cap |= SANE_CAP_INACTIVE;
if (info)
*info |= SANE_INFO_RELOAD_OPTIONS;
}
if (option == OPT_SCAN_SOURCE &&
get_string_list_index (go_scan_source_list,
dev->val[option].s) == 1)
/* flatbed */
{
free (dev->val[OPT_FEEDER_MODE].s);
dev->val[OPT_FEEDER_MODE].s = strdup (go_feeder_mode_list[0]);
}
return SANE_STATUS_GOOD;
case OPT_MODE:
if (strcmp (dev->val[option].s, val) == 0)
return SANE_STATUS_GOOD;
free (dev->val[OPT_MODE].s);
dev->val[OPT_MODE].s = (SANE_String) strdup (val);
/* Set default options for the scan modes. */
dev->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
dev->opt[OPT_AUTOMATIC_THRESHOLD].cap |= SANE_CAP_INACTIVE;
dev->opt[OPT_AUTOMATIC_SEPARATION].cap |= SANE_CAP_INACTIVE;
dev->opt[OPT_GAMMA].cap |= SANE_CAP_INACTIVE;
dev->opt[OPT_INVERSE].cap |= SANE_CAP_INACTIVE;
dev->opt[OPT_JPEG].cap &= ~SANE_CAP_INACTIVE;
if (strcmp (dev->val[OPT_MODE].s, go_scan_mode_list[0]) == 0)
/* binary */
{
dev->opt[OPT_AUTOMATIC_THRESHOLD].cap &= ~SANE_CAP_INACTIVE;
dev->opt[OPT_INVERSE].cap &= ~SANE_CAP_INACTIVE;
dev->opt[OPT_JPEG].cap |= SANE_CAP_INACTIVE;
}
else if (strcmp (dev->val[OPT_MODE].s, go_scan_mode_list[1]) == 0)
/* halftone */
{
dev->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE;
dev->opt[OPT_AUTOMATIC_SEPARATION].cap &= ~SANE_CAP_INACTIVE;
dev->opt[OPT_GAMMA].cap &= ~SANE_CAP_INACTIVE;
dev->opt[OPT_INVERSE].cap &= ~SANE_CAP_INACTIVE;
dev->opt[OPT_JPEG].cap |= SANE_CAP_INACTIVE;
}
else if (strcmp (dev->val[OPT_MODE].s, go_scan_mode_list[2]) == 0)
/* grayscale */
{
dev->opt[OPT_GAMMA].cap &= ~SANE_CAP_INACTIVE;
}
if (info)
{
*info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
}
return SANE_STATUS_GOOD;
case OPT_MANUALFEED:
if (strcmp (dev->val[option].s, val) == 0)
return SANE_STATUS_GOOD;
free (dev->val[option].s);
dev->val[option].s = (SANE_String) strdup (val);
if (strcmp (dev->val[option].s, go_manual_feed_list[0]) == 0) /* off */
dev->opt[OPT_FEED_TIMEOUT].cap |= SANE_CAP_INACTIVE;
else
dev->opt[OPT_FEED_TIMEOUT].cap &= ~SANE_CAP_INACTIVE;
if (info)
*info |= SANE_INFO_RELOAD_OPTIONS;
return SANE_STATUS_GOOD;
case OPT_PAPER_SIZE:
if (strcmp (dev->val[option].s, val) == 0)
return SANE_STATUS_GOOD;
free (dev->val[OPT_PAPER_SIZE].s);
dev->val[OPT_PAPER_SIZE].s = (SANE_Char *) strdup (val);
i = get_string_list_index (go_paper_list,
dev->val[OPT_PAPER_SIZE].s);
if (i == 0)
{ /*user def */
dev->opt[OPT_TL_X].cap &=
dev->opt[OPT_TL_Y].cap &=
dev->opt[OPT_BR_X].cap &=
dev->opt[OPT_BR_Y].cap &= ~SANE_CAP_INACTIVE;
dev->opt[OPT_LANDSCAPE].cap |= SANE_CAP_INACTIVE;
dev->val[OPT_LANDSCAPE].w = 0;
}
else
{
dev->opt[OPT_TL_X].cap |=
dev->opt[OPT_TL_Y].cap |=
dev->opt[OPT_BR_X].cap |=
dev->opt[OPT_BR_Y].cap |= SANE_CAP_INACTIVE;
if (i == 4 || i == 5 || i == 7)
{ /*A5, A6 or B6 */
dev->opt[OPT_LANDSCAPE].cap &= ~SANE_CAP_INACTIVE;
}
else
{
dev->opt[OPT_LANDSCAPE].cap |= SANE_CAP_INACTIVE;
dev->val[OPT_LANDSCAPE].w = 0;
}
}
if (info)
*info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
return SANE_STATUS_GOOD;
case OPT_AUTOMATIC_THRESHOLD:
if (strcmp (dev->val[option].s, val) == 0)
return SANE_STATUS_GOOD;
free (dev->val[option].s);
dev->val[option].s = (SANE_Char *) strdup (val);
/* If the threshold is not set to none, some option must
* disappear. */
dev->opt[OPT_WHITE_LEVEL].cap |= SANE_CAP_INACTIVE;
dev->opt[OPT_NOISE_REDUCTION].cap |= SANE_CAP_INACTIVE;
dev->opt[OPT_IMAGE_EMPHASIS].cap |= SANE_CAP_INACTIVE;
dev->opt[OPT_AUTOMATIC_SEPARATION].cap |= SANE_CAP_INACTIVE;
dev->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
if (strcmp (val, go_automatic_threshold_list[0]) == 0)
{
dev->opt[OPT_WHITE_LEVEL].cap &= ~SANE_CAP_INACTIVE;
dev->opt[OPT_NOISE_REDUCTION].cap &= ~SANE_CAP_INACTIVE;
dev->opt[OPT_IMAGE_EMPHASIS].cap &= ~SANE_CAP_INACTIVE;
dev->opt[OPT_AUTOMATIC_SEPARATION].cap &= ~SANE_CAP_INACTIVE;
if (strcmp (dev->val[OPT_MODE].s, go_scan_mode_list[1]) == 0)
{
dev->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE;
}
}
if (info)
{
*info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
}
return SANE_STATUS_GOOD;
default:
return SANE_STATUS_INVAL;
}
}
DBG (DBG_proc, "sane_control_option: exit, bad\n");
return SANE_STATUS_UNSUPPORTED;
}
/* Display a buffer in the log. */
void
hexdump (int level, const char *comment, unsigned char *p, int l)
{
int i;
char line[128];
char *ptr;
DBG (level, "%s\n", comment);
ptr = line;
for (i = 0; i < l; i++, p++)
{
if ((i % 16) == 0)
{
if (ptr != line)
{
*ptr = '\0';
DBG (level, "%s\n", line);
ptr = line;
}
sprintf (ptr, "%3.3d:", i);
ptr += 4;
}
sprintf (ptr, " %2.2x", *p);
ptr += 3;
}
*ptr = '\0';
DBG (level, "%s\n", line);
}
/* Set window data */
void
kv_set_window_data (PKV_DEV dev,
KV_SCAN_MODE scan_mode,
int side, unsigned char *windowdata)
{
int paper = go_paper_val[get_string_list_index (go_paper_list,
dev->val[OPT_PAPER_SIZE].
s)];
/* Page side */
windowdata[0] = side;
/* X and Y resolution */
Ito16 (dev->val[OPT_RESOLUTION].w, &windowdata[2]);
Ito16 (dev->val[OPT_RESOLUTION].w, &windowdata[4]);
/* Width and length */
if (paper == 0)
{ /* Non-standard document */
int x_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_X].w));
int y_tl = mmToIlu (SANE_UNFIX (dev->val[OPT_TL_Y].w));
int x_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_X].w));
int y_br = mmToIlu (SANE_UNFIX (dev->val[OPT_BR_Y].w));
int width = x_br - x_tl;
int length = y_br - y_tl;
/* Upper Left (X,Y) */
Ito32 (x_tl, &windowdata[6]);
Ito32 (y_tl, &windowdata[10]);
Ito32 (width, &windowdata[14]);
Ito32 (length, &windowdata[18]);
Ito32 (width, &windowdata[48]); /* device specific */
Ito32 (length, &windowdata[52]); /* device specific */
}
/* Brightness */
windowdata[22] = 255 - GET_OPT_VAL_W (dev, OPT_BRIGHTNESS);
windowdata[23] = windowdata[22]; /* threshold, same as brightness. */
/* Contrast */
windowdata[24] = GET_OPT_VAL_W (dev, OPT_CONTRAST);
/* Image Composition */
windowdata[25] = (unsigned char) scan_mode;
/* Depth */
windowdata[26] = kv_get_depth (scan_mode);
/* Halftone pattern. */
if (scan_mode == SM_DITHER)
{
windowdata[28] = GET_OPT_VAL_L (dev, OPT_HALFTONE_PATTERN,
halftone_pattern);
}
/* Inverse */
if (scan_mode == SM_BINARY || scan_mode == SM_DITHER)
{
windowdata[29] = GET_OPT_VAL_W (dev, OPT_INVERSE);
}
/* Bit ordering */
windowdata[31] = 1;
/*Compression Type */
if (!(dev->opt[OPT_JPEG].cap & SANE_CAP_INACTIVE)
&& GET_OPT_VAL_W (dev, OPT_JPEG))
{
windowdata[32] = 0x81; /*jpeg */
/*Compression Argument */
windowdata[33] = GET_OPT_VAL_W (dev, OPT_JPEG);
}
/* Gamma */
if (scan_mode == SM_DITHER || scan_mode == SM_GRAYSCALE)
{
windowdata[44] = GET_OPT_VAL_L (dev, OPT_GAMMA, gamma);
}
/* Feeder mode */
windowdata[57] = GET_OPT_VAL_L (dev, OPT_FEEDER_MODE, feeder_mode);
/* Stop skew -- disabled */
windowdata[41] = 0;
/* Scan source */
if (GET_OPT_VAL_L (dev, OPT_SCAN_SOURCE, scan_source))
{ /* flatbed */
windowdata[41] |= 0x80;
}
else
{
windowdata[41] &= 0x7f;
}
/* Paper size */
windowdata[47] = paper;
if (paper) /* Standard Document */
windowdata[47] |= 1 << 7;
/* Long paper */
if (GET_OPT_VAL_W (dev, OPT_LONGPAPER))
{
windowdata[47] |= 0x20;
}
/* Length control */
if (GET_OPT_VAL_W (dev, OPT_LENGTHCTL))
{
windowdata[47] |= 0x40;
}
/* Landscape */
if (GET_OPT_VAL_W (dev, OPT_LANDSCAPE))
{
windowdata[47] |= 1 << 4;
}
/* Double feed */
if (GET_OPT_VAL_W (dev, OPT_DBLFEED))
{
windowdata[56] = 0x10;
}
/* Fit to page */
if (GET_OPT_VAL_W (dev, OPT_FIT_TO_PAGE))
{
windowdata[56] |= 1 << 2;
}
/* Manual feed */
windowdata[62] = GET_OPT_VAL_L (dev, OPT_MANUALFEED, manual_feed) << 6;
/* Mirror image */
if (GET_OPT_VAL_W (dev, OPT_MIRROR))
{
windowdata[42] = 0x80;
}
/* Image emphasis */
windowdata[43] = GET_OPT_VAL_L (dev, OPT_IMAGE_EMPHASIS, image_emphasis);
/* White level */
windowdata[60] = GET_OPT_VAL_L (dev, OPT_WHITE_LEVEL, white_level);
if (scan_mode == SM_BINARY || scan_mode == SM_DITHER)
{
/* Noise reduction */
windowdata[61] = GET_OPT_VAL_L (dev, OPT_NOISE_REDUCTION,
noise_reduction);
/* Automatic separation */
if (scan_mode == SM_DITHER && GET_OPT_VAL_W (dev,
OPT_AUTOMATIC_SEPARATION))
{
windowdata[59] = 0x80;
}
}
/* Automatic threshold. Must be last because it may override
* some previous options. */
if (scan_mode == SM_BINARY)
{
windowdata[58] =
GET_OPT_VAL_L (dev, OPT_AUTOMATIC_THRESHOLD, automatic_threshold);
}
if (windowdata[58] != 0)
{
/* Automatic threshold is enabled. */
windowdata[22] = 0; /* brightness. */
windowdata[23] = 0; /* threshold, same as brightness. */
windowdata[24] = 0; /* contrast */
windowdata[27] = windowdata[28] = 0; /* Halftone pattern. */
windowdata[43] = 0; /* Image emphasis */
windowdata[59] = 0; /* Automatic separation */
windowdata[60] = 0; /* White level */
windowdata[61] = 0; /* Noise reduction */
}
/* lamp -- color dropout */
windowdata[45] = GET_OPT_VAL_L (dev, OPT_LAMP, lamp) << 4;
/*Stop Mode: After 1 page */
windowdata[63] = 1;
}