[sane.ds try2] Implement support for ICAP_XRESOLUTION and ICAP_YRESOLUTION.

Jeremy White jwhite at codeweavers.com
Mon Feb 23 16:25:26 CST 2009


---
 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 ad2d7ba..1d574e3 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, &current_resolution) != SANE_STATUS_GOOD)
+    {
+        best_option_name = "resolution";
+        if (sane_option_get_int(activeDS.deviceHandle, best_option_name, &current_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 69ec633..f204a71 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 0ac5c45..9924746 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 7e2f117..79e2cce 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 1c38824..09b432a 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);
     }
 }
 



More information about the wine-patches mailing list