diff options
author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-10-06 14:00:40 +0200 |
---|---|---|
committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-10-06 14:00:40 +0200 |
commit | 6e9c41a892ed0e0da326e0278b3221ce3f5713b8 (patch) | |
tree | 2e301d871bbeeb44aa57ff9cc070fcf3be484487 /backend/kvs1025_opt.c |
Initial import of sane-backends version 1.0.24-1.2
Diffstat (limited to 'backend/kvs1025_opt.c')
-rw-r--r-- | backend/kvs1025_opt.c | 1581 |
1 files changed, 1581 insertions, 0 deletions
diff --git a/backend/kvs1025_opt.c b/backend/kvs1025_opt.c new file mode 100644 index 0000000..b7581f9 --- /dev/null +++ b/backend/kvs1025_opt.c @@ -0,0 +1,1581 @@ +/* + 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 */ +static const int go_paper_max_width = 216; /* US letter */ + +/* 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 ("linier"), + 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 Continous */ + "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; +} |