diff --git a/dlls/sane.ds/capability.c b/dlls/sane.ds/capability.c index 17005cc1fd0..4faab5975d9 100644 --- a/dlls/sane.ds/capability.c +++ b/dlls/sane.ds/capability.c @@ -22,6 +22,8 @@ #include "config.h" #include +#include +#include #include "windef.h" #include "winbase.h" @@ -165,7 +167,7 @@ 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_UNITS, ICAP_BITDEPTH, ICAP_COMPRESSION, ICAP_PIXELFLAVOR, - ICAP_XRESOLUTION, ICAP_YRESOLUTION }; + ICAP_XRESOLUTION, ICAP_YRESOLUTION, ICAP_PHYSICALHEIGHT, ICAP_PHYSICALWIDTH }; pCapability->hContainer = GlobalAlloc (0, FIELD_OFFSET( TW_ARRAY, ItemList[sizeof(supported_caps)] )); pCapability->ConType = TWON_ARRAY; @@ -692,6 +694,81 @@ static TW_UINT16 SANE_ICAPResolution (pTW_CAPABILITY pCapability, TW_UINT16 acti return twCC; } +static void convert_double_fix32(double d, TW_FIX32 *fix32) +{ + TW_INT32 value = (TW_INT32) (d * 65536.0 + 0.5); + fix32->Whole = value >> 16; + fix32->Frac = value & 0x0000ffffL; +} + + +#ifdef SONAME_LIBSANE +static BOOL convert_sane_res_to_twain(double sane_res, SANE_Unit unit, TW_FIX32 *twain_res, TW_UINT16 twtype) +{ + double d; + + if (unit != SANE_UNIT_MM) + return FALSE; + + if (twtype != TWUN_INCHES) + return FALSE; + + d = (sane_res / 10.0) / 2.54; + convert_double_fix32((sane_res / 10.0) / 2.54, twain_res); + + return TRUE; +} +#endif + +/* ICAP_PHYSICALHEIGHT, ICAP_PHYSICALWIDTH */ +static TW_UINT16 SANE_ICAPPhysical (pTW_CAPABILITY pCapability, TW_UINT16 action, TW_UINT16 cap) +{ + TW_UINT16 twCC = TWCC_BADCAP; +#ifdef SONAME_LIBSANE + TW_FIX32 res; + char option_name[64]; + SANE_Fixed lower, upper; + SANE_Unit lowerunit, upperunit; + SANE_Status status; + + TRACE("ICAP_PHYSICAL%s\n", cap == ICAP_PHYSICALHEIGHT? "HEIGHT" : "WIDTH"); + + sprintf(option_name, "tl-%c", cap == ICAP_PHYSICALHEIGHT ? 'y' : 'x'); + status = sane_option_probe_scan_area(activeDS.deviceHandle, option_name, NULL, &lowerunit, &lower, NULL, NULL); + if (status != SANE_STATUS_GOOD) + return sane_status_to_twcc(status); + + sprintf(option_name, "br-%c", cap == ICAP_PHYSICALHEIGHT ? 'y' : 'x'); + status = sane_option_probe_scan_area(activeDS.deviceHandle, option_name, NULL, &upperunit, NULL, &upper, NULL); + if (status != SANE_STATUS_GOOD) + return sane_status_to_twcc(status); + + if (upperunit != lowerunit) + return TWCC_BADCAP; + + if (! convert_sane_res_to_twain(SANE_UNFIX(upper) - SANE_UNFIX(lower), upperunit, &res, TWUN_INCHES)) + return TWCC_BADCAP; + + switch (action) + { + case MSG_QUERYSUPPORT: + twCC = set_onevalue(pCapability, TWTY_INT32, + TWQC_GET | TWQC_GETDEFAULT | TWQC_GETCURRENT ); + break; + + case MSG_GET: + case MSG_GETDEFAULT: + + /* .. fall through intentional .. */ + + case MSG_GETCURRENT: + twCC = set_onevalue(pCapability, TWTY_FIX32, res.Whole | (res.Frac << 16)); + break; + } +#endif + return twCC; +} + /* ICAP_PIXELFLAVOR */ static TW_UINT16 SANE_ICAPPixelFlavor (pTW_CAPABILITY pCapability, TW_UINT16 action) { @@ -793,6 +870,15 @@ TW_UINT16 SANE_SaneCapability (pTW_CAPABILITY pCapability, TW_UINT16 action) case ICAP_YRESOLUTION: twCC = SANE_ICAPResolution(pCapability, action, pCapability->Cap); break; + + case ICAP_PHYSICALHEIGHT: + twCC = SANE_ICAPPhysical(pCapability, action, pCapability->Cap); + break; + + case ICAP_PHYSICALWIDTH: + twCC = SANE_ICAPPhysical(pCapability, action, pCapability->Cap); + break; + } /* Twain specifies that you should return a 0 in response to QUERYSUPPORT, diff --git a/dlls/sane.ds/options.c b/dlls/sane.ds/options.c index 98d83dcf489..47938831da8 100644 --- a/dlls/sane.ds/options.c +++ b/dlls/sane.ds/options.c @@ -137,4 +137,30 @@ SANE_Status sane_option_probe_mode(SANE_Handle h, SANE_String_Const **choices, c return SANE_STATUS_NO_MEM; } + +SANE_Status sane_option_probe_scan_area(SANE_Handle h, const char *option_name, SANE_Fixed *val, + SANE_Unit *unit, SANE_Fixed *min, SANE_Fixed *max, SANE_Fixed *quant) +{ + SANE_Status rc; + int optno; + const SANE_Option_Descriptor *opt; + + rc = sane_find_option(h, option_name, &opt, &optno, SANE_TYPE_FIXED); + if (rc != SANE_STATUS_GOOD) + return rc; + + if (unit) + *unit = opt->unit; + if (min) + *min = opt->constraint.range->min; + if (max) + *max = opt->constraint.range->max; + if (quant) + *quant = opt->constraint.range->quant; + + if (val) + rc = psane_control_option(h, optno, SANE_ACTION_GET_VALUE, val, NULL); + + return rc; +} #endif diff --git a/dlls/sane.ds/sane_i.h b/dlls/sane.ds/sane_i.h index 9d0d1233828..18fd8bbfa0a 100644 --- a/dlls/sane.ds/sane_i.h +++ b/dlls/sane.ds/sane_i.h @@ -226,6 +226,8 @@ SANE_Status sane_option_set_int(SANE_Handle h, const char *option_name, SANE_Int SANE_Status sane_option_set_str(SANE_Handle h, const char *option_name, SANE_String 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); SANE_Status sane_option_probe_mode(SANE_Handle h, SANE_String_Const **choices, char *current, int current_size); +SANE_Status sane_option_probe_scan_area(SANE_Handle h, const char *option_name, SANE_Fixed *val, + SANE_Unit *unit, SANE_Fixed *min, SANE_Fixed *max, SANE_Fixed *quant); #endif diff --git a/dlls/twain_32/tests/dsm.c b/dlls/twain_32/tests/dsm.c index 37ebf3b8e76..46d0c5a80ad 100644 --- a/dlls/twain_32/tests/dsm.c +++ b/dlls/twain_32/tests/dsm.c @@ -391,6 +391,88 @@ static void test_resolution(TW_IDENTITY *appid, TW_IDENTITY *source, TW_UINT16 c } } +static void test_physical(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; + + 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 PHYSICALXXX is not type FIX32, is type %d\n", type); + 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) + { + get_onevalue(cap.hContainer, &val, &type); + ok(type == TWTY_FIX32, "GETDEFAULT for PHYSICALXXX is not type FIX32, is type %d\n", type); + 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) + { + get_onevalue(cap.hContainer, &val, &type); + ok(type == TWTY_FIX32, "GET for PHYSICALXXX is not type FIX32, is type %d\n", type); + trace("GET for Physical type 0x%x returns 0x%x\n", captype, val); + GlobalFree(cap.hContainer); + } + } + +} + static void test_single_source(TW_IDENTITY *appid, TW_IDENTITY *source) { @@ -453,10 +535,14 @@ static void test_single_source(TW_IDENTITY *appid, TW_IDENTITY *source) TWQC_GET | TWQC_GETDEFAULT | TWQC_GETCURRENT); todo_wine ok(capabilities[ICAP_PLANARCHUNKY], "ICAP_PLANARCHUNKY not supported\n"); - todo_wine ok(capabilities[ICAP_PHYSICALHEIGHT], "ICAP_PHYSICALHEIGHT not supported\n"); - todo_wine + if (capabilities[ICAP_PHYSICALHEIGHT]) + test_physical(appid, source, ICAP_PHYSICALHEIGHT, + TWQC_GET | TWQC_GETDEFAULT | TWQC_GETCURRENT); ok(capabilities[ICAP_PHYSICALWIDTH], "ICAP_PHYSICALWIDTH not supported\n"); + if (capabilities[ICAP_PHYSICALWIDTH]) + test_physical(appid, source, ICAP_PHYSICALWIDTH, + TWQC_GET | TWQC_GETDEFAULT | TWQC_GETCURRENT); ok(capabilities[ICAP_PIXELFLAVOR], "ICAP_PIXELFLAVOR not supported\n"); if (capabilities[ICAP_PIXELFLAVOR]) test_onevalue_cap(appid, source, ICAP_PIXELFLAVOR, TWTY_UINT16,