From 6ace799f5ac16d431d29de8ec0362951932dcf0a Mon Sep 17 00:00:00 2001 From: Jeremy White Date: Mon, 23 Feb 2009 16:25:26 -0600 Subject: [PATCH] sane.ds: Implement support for ICAP_XRESOLUTION and ICAP_YRESOLUTION. --- dlls/sane.ds/capability.c | 141 +++++++++++++++++++++++++++++++++++++++++++++- dlls/sane.ds/ds_ctrl.c | 8 ++- dlls/sane.ds/options.c | 57 ++++++++++++++++++- dlls/sane.ds/sane_i.h | 8 +++ dlls/twain_32/tests/dsm.c | 129 +++++++++++++++++++++++++++++++++++++++++- 5 files changed, 336 insertions(+), 7 deletions(-) diff --git a/dlls/sane.ds/capability.c b/dlls/sane.ds/capability.c index ad2d7ba086e..1d574e321e2 100644 --- a/dlls/sane.ds/capability.c +++ b/dlls/sane.ds/capability.c @@ -122,11 +122,40 @@ static TW_UINT16 msg_get_enum(pTW_CAPABILITY pCapability, const TW_UINT32 *value return TWCC_SUCCESS; } +#ifdef SONAME_LIBSANE +static TW_UINT16 msg_get_range(pTW_CAPABILITY pCapability, TW_UINT16 type, + TW_UINT32 minval, TW_UINT32 maxval, TW_UINT32 step, TW_UINT32 def, TW_UINT32 current) +{ + TW_RANGE *range = NULL; + + pCapability->ConType = TWON_RANGE; + pCapability->hContainer = 0; + + pCapability->hContainer = GlobalAlloc (0, sizeof(*range)); + if (pCapability->hContainer) + range = GlobalLock(pCapability->hContainer); + + if (! range) + return TWCC_LOWMEMORY; + + range->ItemType = type; + range->MinValue = minval; + range->MaxValue = maxval; + range->StepSize = step; + range->DefaultValue = def; + range->CurrentValue = current; + + GlobalUnlock(pCapability->hContainer); + return TWCC_SUCCESS; +} +#endif + static TW_UINT16 TWAIN_GetSupportedCaps(pTW_CAPABILITY pCapability) { TW_ARRAY *a; static const UINT16 supported_caps[] = { CAP_SUPPORTEDCAPS, CAP_XFERCOUNT, CAP_UICONTROLLABLE, - ICAP_XFERMECH, ICAP_PIXELTYPE, ICAP_COMPRESSION, ICAP_PIXELFLAVOR }; + ICAP_XFERMECH, ICAP_PIXELTYPE, ICAP_COMPRESSION, ICAP_PIXELFLAVOR, + ICAP_XRESOLUTION, ICAP_YRESOLUTION }; pCapability->hContainer = GlobalAlloc (0, FIELD_OFFSET( TW_ARRAY, ItemList[sizeof(supported_caps)] )); pCapability->ConType = TWON_ARRAY; @@ -345,6 +374,108 @@ static TW_UINT16 SANE_ICAPCompression (pTW_CAPABILITY pCapability, TW_UINT16 act return twCC; } +/* ICAP_XRESOLUTION, ICAP_YRESOLUTION */ +static TW_UINT16 SANE_ICAPResolution (pTW_CAPABILITY pCapability, TW_UINT16 action, TW_UINT16 cap) +{ + TW_UINT16 twCC = TWCC_BADCAP; +#ifdef SONAME_LIBSANE + TW_UINT32 val; + SANE_Int current_resolution; + TW_FIX32 *default_res; + const char *best_option_name; + SANE_Int minval, maxval, quantval; + SANE_Status sane_rc; + SANE_Int set_status; + + TRACE("ICAP_%cRESOLUTION\n", cap == ICAP_XRESOLUTION ? 'X' : 'Y'); + + /* Some scanners support 'x-resolution', most seem to just support 'resolution' */ + if (cap == ICAP_XRESOLUTION) + { + best_option_name = "x-resolution"; + default_res = &activeDS.defaultXResolution; + } + else + { + best_option_name = "y-resolution"; + default_res = &activeDS.defaultYResolution; + } + if (sane_option_get_int(activeDS.deviceHandle, best_option_name, ¤t_resolution) != SANE_STATUS_GOOD) + { + best_option_name = "resolution"; + if (sane_option_get_int(activeDS.deviceHandle, best_option_name, ¤t_resolution) != SANE_STATUS_GOOD) + return TWCC_BADCAP; + } + + /* Sane does not support a concept of 'default' resolution, so we have to + * cache the resolution the very first time we load the scanner, and use that + * as the default */ + if (cap == ICAP_XRESOLUTION && ! activeDS.XResolutionSet) + { + default_res->Whole = current_resolution; + default_res->Frac = 0; + activeDS.XResolutionSet = TRUE; + } + + if (cap == ICAP_YRESOLUTION && ! activeDS.YResolutionSet) + { + default_res->Whole = current_resolution; + default_res->Frac = 0; + activeDS.YResolutionSet = TRUE; + } + + switch (action) + { + case MSG_QUERYSUPPORT: + twCC = set_onevalue(pCapability, TWTY_INT32, + TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET ); + break; + + case MSG_GET: + sane_rc = sane_option_probe_resolution(activeDS.deviceHandle, best_option_name, &minval, &maxval, &quantval); + if (sane_rc != SANE_STATUS_GOOD) + twCC = TWCC_BADCAP; + else + twCC = msg_get_range(pCapability, TWTY_FIX32, + minval, maxval, quantval == 0 ? 1 : quantval, default_res->Whole, current_resolution); + break; + + case MSG_SET: + twCC = msg_set(pCapability, &val); + if (twCC == TWCC_SUCCESS) + { + TW_FIX32 f32; + memcpy(&f32, &val, sizeof(f32)); + sane_rc = sane_option_set_int(activeDS.deviceHandle, best_option_name, f32.Whole, &set_status); + if (sane_rc != SANE_STATUS_GOOD) + { + FIXME("Status of %d not expected or handled\n", sane_rc); + twCC = TWCC_BADCAP; + } + else if (set_status == SANE_INFO_INEXACT) + twCC = TWCC_CHECKSTATUS; + } + break; + + case MSG_GETDEFAULT: + twCC = set_onevalue(pCapability, TWTY_FIX32, default_res->Whole); + break; + + case MSG_RESET: + sane_rc = sane_option_set_int(activeDS.deviceHandle, best_option_name, default_res->Whole, NULL); + if (sane_rc != SANE_STATUS_GOOD) + return TWCC_BADCAP; + + /* .. fall through intentional .. */ + + case MSG_GETCURRENT: + twCC = set_onevalue(pCapability, TWTY_FIX32, current_resolution); + break; + } +#endif + return twCC; +} + /* ICAP_PIXELFLAVOR */ static TW_UINT16 SANE_ICAPPixelFlavor (pTW_CAPABILITY pCapability, TW_UINT16 action) { @@ -427,6 +558,14 @@ TW_UINT16 SANE_SaneCapability (pTW_CAPABILITY pCapability, TW_UINT16 action) case ICAP_COMPRESSION: twCC = SANE_ICAPCompression(pCapability, action); break; + + case ICAP_XRESOLUTION: + twCC = SANE_ICAPResolution(pCapability, action, pCapability->Cap); + break; + + case ICAP_YRESOLUTION: + twCC = SANE_ICAPResolution(pCapability, action, pCapability->Cap); + break; } /* Twain specifies that you should return a 0 in response to QUERYSUPPORT, diff --git a/dlls/sane.ds/ds_ctrl.c b/dlls/sane.ds/ds_ctrl.c index 69ec633a13c..f204a71d22d 100644 --- a/dlls/sane.ds/ds_ctrl.c +++ b/dlls/sane.ds/ds_ctrl.c @@ -162,7 +162,13 @@ TW_UINT16 SANE_CapabilitySet (pTW_IDENTITY pOrigin, else { twCC = SANE_SaneCapability (pCapability, MSG_SET); - twRC = (twCC == TWCC_SUCCESS)?TWRC_SUCCESS:TWRC_FAILURE; + if (twCC == TWCC_CHECKSTATUS) + { + twCC = TWCC_SUCCESS; + twRC = TWRC_CHECKSTATUS; + } + else + twRC = (twCC == TWCC_SUCCESS)?TWRC_SUCCESS:TWRC_FAILURE; activeDS.twCC = twCC; } return twRC; diff --git a/dlls/sane.ds/options.c b/dlls/sane.ds/options.c index 0ac5c454600..99247465c4f 100644 --- a/dlls/sane.ds/options.c +++ b/dlls/sane.ds/options.c @@ -26,7 +26,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(twain); #ifdef SONAME_LIBSANE -SANE_Status sane_option_get_int(SANE_Handle h, const char *option_name, SANE_Int *val) +static SANE_Status sane_find_option(SANE_Handle h, const char *option_name, + const SANE_Option_Descriptor **opt_p, int *optno, SANE_Value_Type type) { SANE_Status rc; SANE_Int optcount; @@ -41,9 +42,59 @@ SANE_Status sane_option_get_int(SANE_Handle h, const char *option_name, SANE_Int { opt = psane_get_option_descriptor(h, i); if (opt && (opt->name && strcmp(opt->name, option_name) == 0) && - opt->type == SANE_TYPE_INT ) - return psane_control_option(h, i, SANE_ACTION_GET_VALUE, val, NULL); + opt->type == type) + { + *opt_p = opt; + *optno = i; + return SANE_STATUS_GOOD; + } } return SANE_STATUS_EOF; } + +SANE_Status sane_option_get_int(SANE_Handle h, const char *option_name, SANE_Int *val) +{ + SANE_Status rc; + int optno; + const SANE_Option_Descriptor *opt; + + rc = sane_find_option(h, option_name, &opt, &optno, SANE_TYPE_INT); + if (rc != SANE_STATUS_GOOD) + return rc; + + return psane_control_option(h, optno, SANE_ACTION_GET_VALUE, val, NULL); +} + +SANE_Status sane_option_set_int(SANE_Handle h, const char *option_name, SANE_Int val, SANE_Int *status) +{ + SANE_Status rc; + int optno; + const SANE_Option_Descriptor *opt; + + rc = sane_find_option(h, option_name, &opt, &optno, SANE_TYPE_INT); + if (rc != SANE_STATUS_GOOD) + return rc; + + return psane_control_option(h, optno, SANE_ACTION_SET_VALUE, (void *) &val, status); +} + +SANE_Status sane_option_probe_resolution(SANE_Handle h, const char *option_name, SANE_Int *minval, SANE_Int *maxval, SANE_Int *quant) +{ + SANE_Status rc; + int optno; + const SANE_Option_Descriptor *opt; + + rc = sane_find_option(h, option_name, &opt, &optno, SANE_TYPE_INT); + if (rc != SANE_STATUS_GOOD) + return rc; + + if (opt->constraint_type != SANE_CONSTRAINT_RANGE) + return SANE_STATUS_UNSUPPORTED; + + *minval = opt->constraint.range->min; + *maxval = opt->constraint.range->max; + *quant = opt->constraint.range->quant; + + return rc; +} #endif diff --git a/dlls/sane.ds/sane_i.h b/dlls/sane.ds/sane_i.h index 7e2f1177005..79e2cce00d0 100644 --- a/dlls/sane.ds/sane_i.h +++ b/dlls/sane.ds/sane_i.h @@ -53,6 +53,8 @@ MAKE_FUNCPTR(sane_strstatus) extern HINSTANCE SANE_instance; +#define TWCC_CHECKSTATUS (TWCC_CUSTOMBASE + 1) + /* internal information about an active data source */ struct tagActiveDS { @@ -73,6 +75,10 @@ struct tagActiveDS /* Capabilities */ TW_UINT16 capXferMech; /* ICAP_XFERMECH */ TW_UINT16 capPixelType; /* ICAP_PIXELTYPE */ + BOOL XResolutionSet; + TW_FIX32 defaultXResolution; + BOOL YResolutionSet; + TW_FIX32 defaultYResolution; } activeDS; /* Helper functions */ @@ -214,6 +220,8 @@ HWND ScanningDialogBox(HWND dialog, LONG progress); /* Option functions */ #ifdef SONAME_LIBSANE SANE_Status sane_option_get_int(SANE_Handle h, const char *option_name, SANE_Int *val); +SANE_Status sane_option_set_int(SANE_Handle h, const char *option_name, SANE_Int val, SANE_Int *status); +SANE_Status sane_option_probe_resolution(SANE_Handle h, const char *option_name, SANE_Int *minval, SANE_Int *maxval, SANE_Int *quant); #endif diff --git a/dlls/twain_32/tests/dsm.c b/dlls/twain_32/tests/dsm.c index 1c38824ebbc..54641c14ac3 100644 --- a/dlls/twain_32/tests/dsm.c +++ b/dlls/twain_32/tests/dsm.c @@ -267,6 +267,127 @@ static void test_onevalue_cap(TW_IDENTITY *appid, TW_IDENTITY *source, TW_UINT16 } } +static void test_resolution(TW_IDENTITY *appid, TW_IDENTITY *source, TW_UINT16 captype, TW_INT32 minimum_support) +{ + TW_UINT16 rc; + TW_STATUS status; + TW_CAPABILITY cap; + TW_UINT32 val; + TW_UINT16 type; + TW_INT32 actual_support; + TW_FIX32 orig_value = { 0, 0 }; + TW_UINT32 new_value = 0; + TW_FIX32 default_value = { 0, 0 }; + + memset(&cap, 0, sizeof(cap)); + cap.Cap = captype; + cap.ConType = TWON_DONTCARE16; + + rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_QUERYSUPPORT, &cap); + get_condition_code(appid, source, &status); + ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS, + "Error [rc %d|cc %d] doing MSG_QUERYSUPPORT for type 0x%x\n", rc, status.ConditionCode, captype); + if (rc != TWRC_SUCCESS) + return; + ok(get_onevalue(cap.hContainer, (TW_UINT32 *) &actual_support, NULL), "Returned cap.hContainer invalid for QuerySupport on type 0x%x\n", captype); + ok((actual_support & minimum_support) == minimum_support, + "Error: minimum support 0x%x for type 0x%x, got 0x%x\n", minimum_support, + captype, actual_support); + + + if (actual_support & TWQC_GETCURRENT) + { + memset(&cap, 0, sizeof(cap)); + cap.Cap = captype; + cap.ConType = TWON_DONTCARE16; + + rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_GETCURRENT, &cap); + get_condition_code(appid, source, &status); + ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS, + "Error [rc %d|cc %d] doing MSG_GETCURRENT for type 0x%x\n", rc, status.ConditionCode, captype); + if (rc == TWRC_SUCCESS) + { + get_onevalue(cap.hContainer, &val, &type); + ok(type == TWTY_FIX32, "GETCURRENT for RESOLUTION is not type FIX32, is type %d\n", type); + memcpy(&orig_value, &val, sizeof(orig_value)); + GlobalFree(cap.hContainer); + } + } + + if (actual_support & TWQC_GETDEFAULT) + { + memset(&cap, 0, sizeof(cap)); + cap.Cap = captype; + cap.ConType = TWON_DONTCARE16; + + rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_GETDEFAULT, &cap); + get_condition_code(appid, source, &status); + ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS, + "Error [rc %d|cc %d] doing MSG_GETDEFAULT for type 0x%x\n", rc, status.ConditionCode, captype); + if (rc == TWRC_SUCCESS) + { + ok(type == TWTY_FIX32, "GETDEFAULT for RESOLUTION is not type FIX32, is type %d\n", type); + memcpy(&default_value, &val, sizeof(default_value)); + GlobalFree(cap.hContainer); + } + } + + if (actual_support & TWQC_GET) + { + memset(&cap, 0, sizeof(cap)); + cap.Cap = captype; + cap.ConType = TWON_DONTCARE16; + + rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_GET, &cap); + get_condition_code(appid, source, &status); + ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS, + "Error [rc %d|cc %d] doing MSG_GET for type 0x%x\n", rc, status.ConditionCode, captype); + if (rc == TWRC_SUCCESS) + { + TW_RANGE *range; + ok(cap.ConType == TWON_RANGE, "MSG_GET for ICAP_[XY]RESOLUTION did not return TWON_RANGE, but %d\n", cap.ConType); + range = GlobalLock(cap.hContainer); + trace("MSG_GET of 0x%x returned [ItemType %d|MinValue %d|MaxValue %d|StepSize %d|DefaultValue %d|CurrentValue %d]:\n", + cap.Cap, range->ItemType, range->MinValue, range->MaxValue, range->StepSize, + range->DefaultValue, range->CurrentValue); + for (new_value = range->MinValue; new_value < range->MaxValue; new_value += range->StepSize) + if (new_value != range->CurrentValue) + break; + GlobalUnlock(cap.hContainer); + GlobalFree(cap.hContainer); + } + } + + if (actual_support & TWQC_SET) + { + memset(&cap, 0, sizeof(cap)); + cap.Cap = captype; + cap.ConType = TWON_ONEVALUE; + cap.hContainer = alloc_and_set_onevalue(new_value, TWTY_FIX32); + + rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_SET, &cap); + get_condition_code(appid, source, &status); + ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS, + "Error [rc %d|cc %d] doing MSG_SET for type 0x%x\n", rc, status.ConditionCode, captype); + GlobalFree(cap.hContainer); + + } + + if (actual_support & TWQC_RESET) + { + memset(&cap, 0, sizeof(cap)); + cap.Cap = captype; + cap.ConType = TWON_DONTCARE16; + + rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_RESET, &cap); + get_condition_code(appid, source, &status); + ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS, + "Error [rc %d|cc %d] doing MSG_RESET for type 0x%x\n", rc, status.ConditionCode, captype); + if (rc == TWRC_SUCCESS) + GlobalFree(cap.hContainer); + } +} + static void test_single_source(TW_IDENTITY *appid, TW_IDENTITY *source) { @@ -356,10 +477,14 @@ static void test_single_source(TW_IDENTITY *appid, TW_IDENTITY *source) if (capabilities[ICAP_XFERMECH]) test_onevalue_cap(appid, source, ICAP_XFERMECH, TWTY_UINT16, TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET); - todo_wine ok(capabilities[ICAP_XRESOLUTION], "ICAP_XRESOLUTION not supported\n"); - todo_wine + if (capabilities[ICAP_XRESOLUTION]) + test_resolution(appid, source, ICAP_XRESOLUTION, + TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET); ok(capabilities[ICAP_YRESOLUTION], "ICAP_YRESOLUTION not supported\n"); + if (capabilities[ICAP_YRESOLUTION]) + test_resolution(appid, source, ICAP_YRESOLUTION, + TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET); } } -- 2.11.4.GIT