diff --git a/dlls/sane.ds/capability.c b/dlls/sane.ds/capability.c index 0446e40d8ca..72574e06772 100644 --- a/dlls/sane.ds/capability.c +++ b/dlls/sane.ds/capability.c @@ -29,6 +29,7 @@ #include "winbase.h" #include "twain.h" #include "sane_i.h" +#include "winnls.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(twain); @@ -168,7 +169,7 @@ static TW_UINT16 TWAIN_GetSupportedCaps(pTW_CAPABILITY pCapability) static const UINT16 supported_caps[] = { CAP_SUPPORTEDCAPS, CAP_XFERCOUNT, CAP_UICONTROLLABLE, CAP_AUTOFEED, CAP_FEEDERENABLED, ICAP_XFERMECH, ICAP_PIXELTYPE, ICAP_UNITS, ICAP_BITDEPTH, ICAP_COMPRESSION, ICAP_PIXELFLAVOR, - ICAP_XRESOLUTION, ICAP_YRESOLUTION, ICAP_PHYSICALHEIGHT, ICAP_PHYSICALWIDTH }; + ICAP_XRESOLUTION, ICAP_YRESOLUTION, ICAP_PHYSICALHEIGHT, ICAP_PHYSICALWIDTH, ICAP_SUPPORTEDSIZES }; pCapability->hContainer = GlobalAlloc (0, FIELD_OFFSET( TW_ARRAY, ItemList[sizeof(supported_caps)] )); pCapability->ConType = TWON_ARRAY; @@ -815,6 +816,229 @@ static TW_UINT16 SANE_ICAPPixelFlavor (pTW_CAPABILITY pCapability, TW_UINT16 act return twCC; } +#ifdef SONAME_LIBSANE +static TW_UINT16 get_width_height(double *width, double *height, BOOL max) +{ + SANE_Status status; + + SANE_Fixed tlx_current, tlx_min, tlx_max; + SANE_Fixed tly_current, tly_min, tly_max; + SANE_Fixed brx_current, brx_min, brx_max; + SANE_Fixed bry_current, bry_min, bry_max; + + status = sane_option_probe_scan_area(activeDS.deviceHandle, "tl-x", &tlx_current, NULL, &tlx_min, &tlx_max, NULL); + if (status != SANE_STATUS_GOOD) + return sane_status_to_twcc(status); + + status = sane_option_probe_scan_area(activeDS.deviceHandle, "tl-y", &tly_current, NULL, &tly_min, &tly_max, NULL); + if (status != SANE_STATUS_GOOD) + return sane_status_to_twcc(status); + + status = sane_option_probe_scan_area(activeDS.deviceHandle, "br-x", &brx_current, NULL, &brx_min, &brx_max, NULL); + if (status != SANE_STATUS_GOOD) + return sane_status_to_twcc(status); + + status = sane_option_probe_scan_area(activeDS.deviceHandle, "br-y", &bry_current, NULL, &bry_min, &bry_max, NULL); + if (status != SANE_STATUS_GOOD) + return sane_status_to_twcc(status); + + if (max) + *width = SANE_UNFIX(brx_max) - SANE_UNFIX(tlx_min); + else + *width = SANE_UNFIX(brx_current) - SANE_UNFIX(tlx_current); + + if (max) + *height = SANE_UNFIX(bry_max) - SANE_UNFIX(tly_min); + else + *height = SANE_UNFIX(bry_current) - SANE_UNFIX(tly_current); + + return(TWCC_SUCCESS); +} + +static TW_UINT16 set_one_coord(const char *name, double coord) +{ + SANE_Status status; + status = sane_option_set_fixed(activeDS.deviceHandle, name, SANE_FIX(coord), NULL); + if (status != SANE_STATUS_GOOD) + return sane_status_to_twcc(status); + return TWCC_SUCCESS; +} + +static TW_UINT16 set_width_height(double width, double height) +{ + TW_UINT16 rc = TWCC_SUCCESS; + rc = set_one_coord("tl-x", 0); + if (rc != TWCC_SUCCESS) + return rc; + rc = set_one_coord("br-x", width); + if (rc != TWCC_SUCCESS) + return rc; + rc = set_one_coord("tl-y", 0); + if (rc != TWCC_SUCCESS) + return rc; + rc = set_one_coord("br-y", height); + + return rc; +} + +typedef struct +{ + TW_UINT32 size; + double x; + double y; +} supported_size_t; + +static const supported_size_t supported_sizes[] = +{ + { TWSS_NONE, 0, 0 }, + { TWSS_A4, 210, 297 }, + { TWSS_JISB5, 182, 257 }, + { TWSS_USLETTER, 215.9, 279.4 }, + { TWSS_USLEGAL, 215.9, 355.6 }, + { TWSS_A5, 148, 210 }, + { TWSS_B4, 250, 353 }, + { TWSS_B6, 125, 176 }, + { TWSS_USLEDGER, 215.9, 431.8 }, + { TWSS_USEXECUTIVE, 184.15, 266.7 }, + { TWSS_A3, 297, 420 }, +}; +#define SUPPORTED_SIZE_COUNT (sizeof(supported_sizes) / sizeof(supported_sizes[0])) + +static TW_UINT16 get_default_paper_size(const supported_size_t *s, int n) +{ + DWORD paper; + int rc; + int defsize = -1; + double width, height; + int i; + rc = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_IPAPERSIZE | LOCALE_RETURN_NUMBER, (void *) &paper, sizeof(paper)); + if (rc > 0) + switch (paper) + { + case 1: + defsize = TWSS_USLETTER; + break; + case 5: + defsize = TWSS_USLEGAL; + break; + case 8: + defsize = TWSS_A3; + break; + case 9: + defsize = TWSS_A4; + break; + } + + if (defsize == -1) + return TWSS_NONE; + + if (get_width_height(&width, &height, TRUE) != TWCC_SUCCESS) + return TWSS_NONE; + + for (i = 0; i < n; i++) + if (s[i].size == defsize) + { + /* Sane's use of integers to store floats is a hair lossy; deal with it */ + if (s[i].x > (width + .01) || s[i].y > (height + 0.01)) + return TWSS_NONE; + else + return s[i].size; + } + + return TWSS_NONE; +} + +static TW_UINT16 get_current_paper_size(const supported_size_t *s, int n) +{ + int i; + double width, height; + double xdelta, ydelta; + + if (get_width_height(&width, &height, FALSE) != TWCC_SUCCESS) + return TWSS_NONE; + + for (i = 0; i < n; i++) + { + /* Sane's use of integers to store floats results + * in a very small error; cope with that */ + xdelta = s[i].x - width; + ydelta = s[i].y - height; + if (xdelta < 0.01 && xdelta > -0.01 && + ydelta < 0.01 && ydelta > -0.01) + return s[i].size; + } + + return TWSS_NONE; +} +#endif + +/* ICAP_SUPPORTEDSIZES */ +static TW_UINT16 SANE_ICAPSupportedSizes (pTW_CAPABILITY pCapability, TW_UINT16 action) +{ + TW_UINT16 twCC = TWCC_BADCAP; +#ifdef SONAME_LIBSANE + + static TW_UINT32 possible_values[SUPPORTED_SIZE_COUNT]; + int i; + TW_UINT32 val; + TW_UINT16 default_size = get_default_paper_size(supported_sizes, SUPPORTED_SIZE_COUNT); + TW_UINT16 current_size = get_current_paper_size(supported_sizes, SUPPORTED_SIZE_COUNT); + + TRACE("ICAP_SUPPORTEDSIZES\n"); + + 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: + for (i = 0; i < sizeof(supported_sizes) / sizeof(supported_sizes[0]); i++) + possible_values[i] = supported_sizes[i].size; + twCC = msg_get_enum(pCapability, possible_values, sizeof(possible_values) / sizeof(possible_values[0]), + TWTY_UINT16, current_size, default_size); + WARN("Partial Stub: our supported size selection is a bit thin.\n"); + break; + + case MSG_SET: + twCC = msg_set(pCapability, &val); + if (twCC == TWCC_SUCCESS) + for (i = 1; i < SUPPORTED_SIZE_COUNT; i++) + if (supported_sizes[i].size == val) + return set_width_height(supported_sizes[i].x, supported_sizes[i].y); + + ERR("Unsupported size %d\n", val); + twCC = TWCC_BADCAP; + break; + + case MSG_GETDEFAULT: + twCC = set_onevalue(pCapability, TWTY_UINT16, default_size); + break; + + case MSG_RESET: + twCC = TWCC_BADCAP; + for (i = 1; i < SUPPORTED_SIZE_COUNT; i++) + if (supported_sizes[i].size == default_size) + { + twCC = set_width_height(supported_sizes[i].x, supported_sizes[i].y); + break; + } + if (twCC != TWCC_SUCCESS) + return twCC; + + /* .. fall through intentional .. */ + + case MSG_GETCURRENT: + twCC = set_onevalue(pCapability, TWTY_UINT16, current_size); + break; + } + +#undef SUPPORTED_SIZE_COUNT +#endif + return twCC; +} + /* CAP_AUTOFEED */ static TW_UINT16 SANE_CAPAutofeed (pTW_CAPABILITY pCapability, TW_UINT16 action) { @@ -1027,6 +1251,10 @@ TW_UINT16 SANE_SaneCapability (pTW_CAPABILITY pCapability, TW_UINT16 action) twCC = SANE_ICAPPhysical(pCapability, action, pCapability->Cap); break; + case ICAP_SUPPORTEDSIZES: + twCC = SANE_ICAPSupportedSizes (pCapability, action); + break; + } /* Twain specifies that you should return a 0 in response to QUERYSUPPORT, @@ -1058,5 +1286,12 @@ TW_UINT16 SANE_SaneSetDefaults (void) if (SANE_SaneCapability(&cap, MSG_RESET) == TWCC_SUCCESS) GlobalFree(cap.hContainer); + memset(&cap, 0, sizeof(cap)); + cap.Cap = ICAP_SUPPORTEDSIZES; + cap.ConType = TWON_DONTCARE16; + + if (SANE_SaneCapability(&cap, MSG_RESET) == TWCC_SUCCESS) + GlobalFree(cap.hContainer); + return TWCC_SUCCESS; } diff --git a/dlls/sane.ds/options.c b/dlls/sane.ds/options.c index 3fccb454474..44cd34efb76 100644 --- a/dlls/sane.ds/options.c +++ b/dlls/sane.ds/options.c @@ -111,6 +111,19 @@ SANE_Status sane_option_set_bool(SANE_Handle h, const char *option_name, SANE_Bo return psane_control_option(h, optno, SANE_ACTION_SET_VALUE, (void *) &val, status); } +SANE_Status sane_option_set_fixed(SANE_Handle h, const char *option_name, SANE_Fixed 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_FIXED); + if (rc != SANE_STATUS_GOOD) + return rc; + + return psane_control_option(h, optno, SANE_ACTION_SET_VALUE, (void *) &val, status); +} + SANE_Status sane_option_get_str(SANE_Handle h, const char *option_name, SANE_String val, size_t len, SANE_Int *status) { SANE_Status rc; diff --git a/dlls/sane.ds/sane_i.h b/dlls/sane.ds/sane_i.h index be4bd8fdc31..72b4b790480 100644 --- a/dlls/sane.ds/sane_i.h +++ b/dlls/sane.ds/sane_i.h @@ -233,6 +233,7 @@ SANE_Status sane_option_probe_scan_area(SANE_Handle h, const char *option_name, SANE_Unit *unit, SANE_Fixed *min, SANE_Fixed *max, SANE_Fixed *quant); SANE_Status sane_option_get_bool(SANE_Handle h, const char *option_name, SANE_Bool *val, SANE_Int *status); SANE_Status sane_option_set_bool(SANE_Handle h, const char *option_name, SANE_Bool val, SANE_Int *status); +SANE_Status sane_option_set_fixed(SANE_Handle h, const char *option_name, SANE_Fixed val, SANE_Int *status); #endif diff --git a/dlls/twain_32/tests/dsm.c b/dlls/twain_32/tests/dsm.c index c1f92f597f2..4218f25fe1b 100644 --- a/dlls/twain_32/tests/dsm.c +++ b/dlls/twain_32/tests/dsm.c @@ -473,6 +473,116 @@ static void test_physical(TW_IDENTITY *appid, TW_IDENTITY *source, TW_UINT16 cap } +static void test_supported_sizes(TW_IDENTITY *appid, TW_IDENTITY *source, TW_INT32 minimum_support) +{ + TW_UINT16 rc; + TW_STATUS status; + TW_CAPABILITY cap; + TW_UINT32 val; + TW_UINT16 type; + TW_INT32 actual_support; + TW_UINT32 orig_value; + TW_UINT32 default_value; + TW_UINT32 new_value = TWSS_NONE; + + + memset(&cap, 0, sizeof(cap)); + cap.Cap = ICAP_SUPPORTEDSIZES; + 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 ICAP_SUPPORTEDSIZES\n", rc, status.ConditionCode); + if (rc != TWRC_SUCCESS) + return; + ok(get_onevalue(cap.hContainer, (TW_UINT32 *) &actual_support, NULL), "Returned cap.hContainer invalid for QuerySupport on ICAP_SUPPORTEDSIZES\n"); + ok((actual_support & minimum_support) == minimum_support, + "Error: minimum support 0x%x for ICAP_SUPPORTEDSIZES, got 0x%x\n", minimum_support, actual_support); + + if (actual_support & TWQC_GETCURRENT) + { + memset(&cap, 0, sizeof(cap)); + cap.Cap = ICAP_SUPPORTEDSIZES; + 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 ICAP_SUPPORTEDSIZES\n", rc, status.ConditionCode); + if (rc == TWRC_SUCCESS) + { + get_onevalue(cap.hContainer, &val, &type); + ok(type == TWTY_UINT16, "GETCURRENT for ICAP_SUPPORTEDSIZES is not type UINT16, is type %d\n", type); + trace("Current size is %d\n", val); + GlobalFree(cap.hContainer); + orig_value = val; + } + } + + if (actual_support & TWQC_GETDEFAULT) + { + memset(&cap, 0, sizeof(cap)); + cap.Cap = ICAP_SUPPORTEDSIZES; + 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 ICAP_SUPPORTEDSIZES\n", rc, status.ConditionCode); + if (rc == TWRC_SUCCESS) + { + get_onevalue(cap.hContainer, &val, &type); + ok(type == TWTY_UINT16, "GETDEFAULT for PHYSICALXXX is not type TWTY_UINT16, is type %d\n", type); + trace("Default size is %d\n", val); + GlobalFree(cap.hContainer); + default_value = val; + } + } + + if (actual_support & TWQC_GET) + { + memset(&cap, 0, sizeof(cap)); + cap.Cap = ICAP_SUPPORTEDSIZES; + 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 ICAP_SUPPORTEDSIZES\n", rc, status.ConditionCode); + check_get(&cap, actual_support, orig_value, default_value, &new_value); + } + + if (actual_support & TWQC_SET) + { + memset(&cap, 0, sizeof(cap)); + cap.Cap = ICAP_SUPPORTEDSIZES; + cap.ConType = TWON_ONEVALUE; + cap.hContainer = alloc_and_set_onevalue(new_value, TWTY_UINT16); + + 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 ICAP_SUPPORTEDSIZES\n", rc, status.ConditionCode); + GlobalFree(cap.hContainer); + + } + + if (actual_support & TWQC_RESET) + { + memset(&cap, 0, sizeof(cap)); + cap.Cap = ICAP_SUPPORTEDSIZES; + 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 ICAP_SUPPORTEDSIZES\n", rc, status.ConditionCode); + if (rc == TWRC_SUCCESS) + GlobalFree(cap.hContainer); + } +} + static void test_single_source(TW_IDENTITY *appid, TW_IDENTITY *source) { @@ -586,6 +696,9 @@ static void test_single_source(TW_IDENTITY *appid, TW_IDENTITY *source) if (capabilities[CAP_FEEDERENABLED]) test_onevalue_cap(appid, source, CAP_FEEDERENABLED, TWTY_BOOL, TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET); + if (capabilities[ICAP_SUPPORTEDSIZES]) + test_supported_sizes(appid, source, + TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET); } }