sane.ds: Added support for ICAP_UNITS.
[wine] / dlls / sane.ds / capability.c
1 /*
2  * Copyright 2000 Corel Corporation
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #define NONAMELESSUNION
20 #define NONAMELESSSTRUCT
21
22 #include "config.h"
23
24 #include <stdarg.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "twain.h"
29 #include "sane_i.h"
30 #include "wine/debug.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(twain);
33
34 static TW_UINT16 get_onevalue(pTW_CAPABILITY pCapability, TW_UINT16 *type, TW_UINT32 *value)
35 {
36     if (pCapability->hContainer)
37     {
38         pTW_ONEVALUE pVal = GlobalLock (pCapability->hContainer);
39         if (pVal)
40         {
41             *value = pVal->Item;
42             if (type)
43                 *type = pVal->ItemType;
44             GlobalUnlock (pCapability->hContainer);
45             return TWCC_SUCCESS;
46         }
47     }
48     return TWCC_BUMMER;
49 }
50
51
52 static TW_UINT16 set_onevalue(pTW_CAPABILITY pCapability, TW_UINT16 type, TW_UINT32 value)
53 {
54     pCapability->hContainer = GlobalAlloc (0, sizeof(TW_ONEVALUE));
55
56     if (pCapability->hContainer)
57     {
58         pTW_ONEVALUE pVal = GlobalLock (pCapability->hContainer);
59         if (pVal)
60         {
61             pCapability->ConType = TWON_ONEVALUE;
62             pVal->ItemType = type;
63             pVal->Item = value;
64             GlobalUnlock (pCapability->hContainer);
65             return TWCC_SUCCESS;
66         }
67     }
68    return TWCC_LOWMEMORY;
69 }
70
71 static TW_UINT16 msg_set(pTW_CAPABILITY pCapability, TW_UINT32 *val)
72 {
73     if (pCapability->ConType == TWON_ONEVALUE)
74         return get_onevalue(pCapability, NULL, val);
75
76     FIXME("Partial Stub:  MSG_SET only supports TW_ONEVALUE\n");
77     return TWCC_BADCAP;
78 }
79
80
81 static TW_UINT16 msg_get_enum(pTW_CAPABILITY pCapability, const TW_UINT32 *values, int value_count,
82                               TW_UINT16 type, TW_UINT32 current, TW_UINT32 default_value)
83 {
84     TW_ENUMERATION *enumv = NULL;
85     TW_UINT32 *p32;
86     TW_UINT16 *p16;
87     int i;
88
89     pCapability->ConType = TWON_ENUMERATION;
90     pCapability->hContainer = 0;
91
92     if (type == TWTY_INT16 || type == TWTY_UINT16)
93         pCapability->hContainer = GlobalAlloc (0, FIELD_OFFSET( TW_ENUMERATION, ItemList[value_count * sizeof(TW_UINT16)]));
94
95     if (type == TWTY_INT32 || type == TWTY_UINT32)
96         pCapability->hContainer = GlobalAlloc (0, FIELD_OFFSET( TW_ENUMERATION, ItemList[value_count * sizeof(TW_UINT32)]));
97
98     if (pCapability->hContainer)
99         enumv = GlobalLock(pCapability->hContainer);
100
101     if (! enumv)
102         return TWCC_LOWMEMORY;
103
104     enumv->ItemType = type;
105     enumv->NumItems = value_count;
106
107     p16 = (TW_UINT16 *) enumv->ItemList;
108     p32 = (TW_UINT32 *) enumv->ItemList;
109     for (i = 0; i < value_count; i++)
110     {
111         if (values[i] == current)
112             enumv->CurrentIndex = i;
113         if (values[i] == default_value)
114             enumv->DefaultIndex = i;
115         if (type == TWTY_INT16 || type == TWTY_UINT16)
116             p16[i] = values[i];
117         if (type == TWTY_INT32 || type == TWTY_UINT32)
118             p32[i] = values[i];
119     }
120
121     GlobalUnlock(pCapability->hContainer);
122     return TWCC_SUCCESS;
123 }
124
125 #ifdef SONAME_LIBSANE
126 static TW_UINT16 msg_get_range(pTW_CAPABILITY pCapability, TW_UINT16 type,
127             TW_UINT32 minval, TW_UINT32 maxval, TW_UINT32 step, TW_UINT32 def,  TW_UINT32 current)
128 {
129     TW_RANGE *range = NULL;
130
131     pCapability->ConType = TWON_RANGE;
132     pCapability->hContainer = 0;
133
134     pCapability->hContainer = GlobalAlloc (0, sizeof(*range));
135     if (pCapability->hContainer)
136         range = GlobalLock(pCapability->hContainer);
137
138     if (! range)
139         return TWCC_LOWMEMORY;
140
141     range->ItemType = type;
142     range->MinValue = minval;
143     range->MaxValue = maxval;
144     range->StepSize = step;
145     range->DefaultValue = def;
146     range->CurrentValue = current;
147
148     GlobalUnlock(pCapability->hContainer);
149     return TWCC_SUCCESS;
150 }
151 #endif
152
153 static TW_UINT16 TWAIN_GetSupportedCaps(pTW_CAPABILITY pCapability)
154 {
155     TW_ARRAY *a;
156     static const UINT16 supported_caps[] = { CAP_SUPPORTEDCAPS, CAP_XFERCOUNT, CAP_UICONTROLLABLE,
157                     ICAP_XFERMECH, ICAP_PIXELTYPE, ICAP_UNITS, ICAP_BITDEPTH, ICAP_COMPRESSION, ICAP_PIXELFLAVOR,
158                     ICAP_XRESOLUTION, ICAP_YRESOLUTION };
159
160     pCapability->hContainer = GlobalAlloc (0, FIELD_OFFSET( TW_ARRAY, ItemList[sizeof(supported_caps)] ));
161     pCapability->ConType = TWON_ARRAY;
162
163     if (pCapability->hContainer)
164     {
165         UINT16 *u;
166         int i;
167         a = GlobalLock (pCapability->hContainer);
168         a->ItemType = TWTY_UINT16;
169         a->NumItems = sizeof(supported_caps) / sizeof(supported_caps[0]);
170         u = (UINT16 *) a->ItemList;
171         for (i = 0; i < a->NumItems; i++)
172             u[i] = supported_caps[i];
173         GlobalUnlock (pCapability->hContainer);
174         return TWCC_SUCCESS;
175     }
176     else
177         return TWCC_LOWMEMORY;
178 }
179
180
181 /* ICAP_XFERMECH */
182 static TW_UINT16 SANE_ICAPXferMech (pTW_CAPABILITY pCapability, TW_UINT16 action)
183 {
184     static const TW_UINT32 possible_values[] = { TWSX_NATIVE, TWSX_MEMORY };
185     TW_UINT32 val;
186     TW_UINT16 twCC = TWCC_BADCAP;
187
188     TRACE("ICAP_XFERMECH\n");
189
190     switch (action)
191     {
192         case MSG_QUERYSUPPORT:
193             twCC = set_onevalue(pCapability, TWTY_INT32,
194                     TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
195             break;
196
197         case MSG_GET:
198             twCC = msg_get_enum(pCapability, possible_values, sizeof(possible_values) / sizeof(possible_values[0]),
199                     TWTY_UINT16, activeDS.capXferMech, TWSX_NATIVE);
200             break;
201
202         case MSG_SET:
203             twCC = msg_set(pCapability, &val);
204             if (twCC == TWCC_SUCCESS)
205             {
206                activeDS.capXferMech = (TW_UINT16) val;
207                FIXME("Partial Stub:  XFERMECH set to %d, but ignored\n", val);
208             }
209             break;
210
211         case MSG_GETDEFAULT:
212             twCC = set_onevalue(pCapability, TWTY_UINT16, TWSX_NATIVE);
213             break;
214
215         case MSG_RESET:
216             activeDS.capXferMech = TWSX_NATIVE;
217             /* .. fall through intentional .. */
218
219         case MSG_GETCURRENT:
220             twCC = set_onevalue(pCapability, TWTY_UINT16, activeDS.capXferMech);
221             FIXME("Partial Stub:  XFERMECH of %d not actually used\n", activeDS.capXferMech);
222             break;
223     }
224     return twCC;
225 }
226
227
228 /* CAP_XFERCOUNT */
229 static TW_UINT16 SANE_CAPXferCount (pTW_CAPABILITY pCapability, TW_UINT16 action)
230 {
231     TW_UINT32 val;
232     TW_UINT16 twCC = TWCC_BADCAP;
233
234     TRACE("CAP_XFERCOUNT\n");
235
236     switch (action)
237     {
238         case MSG_QUERYSUPPORT:
239             twCC = set_onevalue(pCapability, TWTY_INT32,
240                     TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
241             break;
242
243         case MSG_GET:
244             twCC = set_onevalue(pCapability, TWTY_INT16, -1);
245             FIXME("Partial Stub:  Reporting only support for transfer all\n");
246             break;
247
248         case MSG_SET:
249             twCC = msg_set(pCapability, &val);
250             if (twCC == TWCC_SUCCESS)
251                FIXME("Partial Stub:  XFERCOUNT set to %d, but ignored\n", val);
252             break;
253
254         case MSG_GETDEFAULT:
255             twCC = set_onevalue(pCapability, TWTY_INT16, -1);
256             break;
257
258         case MSG_RESET:
259             /* .. fall through intentional .. */
260
261         case MSG_GETCURRENT:
262             twCC = set_onevalue(pCapability, TWTY_INT16, -1);
263             break;
264     }
265     return twCC;
266 }
267
268 /* ICAP_PIXELTYPE */
269 static TW_UINT16 SANE_ICAPPixelType (pTW_CAPABILITY pCapability, TW_UINT16 action)
270 {
271     static const TW_UINT32 possible_values[] = { TWPT_BW, TWPT_GRAY, TWPT_RGB };
272     TW_UINT32 val;
273     TW_UINT16 twCC = TWCC_BADCAP;
274
275     TRACE("ICAP_PIXELTYPE\n");
276
277     switch (action)
278     {
279         case MSG_QUERYSUPPORT:
280             twCC = set_onevalue(pCapability, TWTY_INT32,
281                     TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
282             break;
283
284         case MSG_GET:
285             twCC = msg_get_enum(pCapability, possible_values, sizeof(possible_values) / sizeof(possible_values[0]),
286                     TWTY_UINT16, activeDS.capXferMech, TWPT_BW);
287             break;
288
289         case MSG_SET:
290             twCC = msg_set(pCapability, &val);
291             if (twCC == TWCC_SUCCESS)
292             {
293                activeDS.capPixelType = (TW_UINT16) val;
294                FIXME("Partial Stub:  PIXELTYPE set to %d, but ignored\n", val);
295             }
296             break;
297
298         case MSG_GETDEFAULT:
299             twCC = set_onevalue(pCapability, TWTY_UINT16, TWPT_BW);
300             break;
301
302         case MSG_RESET:
303             activeDS.capPixelType = TWPT_BW;
304             /* .. fall through intentional .. */
305
306         case MSG_GETCURRENT:
307             twCC = set_onevalue(pCapability, TWTY_UINT16, activeDS.capPixelType);
308             break;
309     }
310
311     return twCC;
312 }
313
314 /* ICAP_UNITS */
315 static TW_UINT16 SANE_ICAPUnits (pTW_CAPABILITY pCapability, TW_UINT16 action)
316 {
317     TW_UINT32 val;
318     TW_UINT16 twCC = TWCC_BADCAP;
319
320     TRACE("ICAP_UNITS\n");
321
322     switch (action)
323     {
324         case MSG_QUERYSUPPORT:
325             twCC = set_onevalue(pCapability, TWTY_INT32,
326                     TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
327             break;
328
329         case MSG_GET:
330             twCC = set_onevalue(pCapability, TWTY_UINT16, TWUN_INCHES);
331             break;
332
333         case MSG_SET:
334             twCC = msg_set(pCapability, &val);
335             if (twCC == TWCC_SUCCESS)
336             {
337                 if  (val != TWUN_INCHES)
338                 {
339                     ERR("Sane supports only SANE_UNIT_DPI\n");
340                     twCC = TWCC_BADVALUE;
341                 }
342             }
343             break;
344
345         case MSG_GETDEFAULT:
346         case MSG_RESET:
347             /* .. fall through intentional .. */
348
349         case MSG_GETCURRENT:
350             twCC = set_onevalue(pCapability, TWTY_UINT16, TWUN_INCHES);
351             break;
352     }
353
354     return twCC;
355 }
356
357 /* ICAP_BITDEPTH */
358 static TW_UINT16 SANE_ICAPBitDepth(pTW_CAPABILITY pCapability, TW_UINT16 action)
359 {
360     TW_UINT16 twCC = TWCC_BADCAP;
361 #ifdef SONAME_LIBSANE
362     TW_UINT32 possible_values[1];
363
364     TRACE("ICAP_BITDEPTH\n");
365
366     possible_values[0] = activeDS.sane_param.depth;
367
368     switch (action)
369     {
370         case MSG_QUERYSUPPORT:
371             twCC = set_onevalue(pCapability, TWTY_INT32,
372                     TWQC_GET | TWQC_GETDEFAULT | TWQC_GETCURRENT  );
373             break;
374
375         case MSG_GET:
376             twCC = msg_get_enum(pCapability, possible_values, sizeof(possible_values) / sizeof(possible_values[0]),
377                     TWTY_UINT16, activeDS.sane_param.depth, activeDS.sane_param.depth);
378             break;
379
380         case MSG_GETDEFAULT:
381             /* .. Fall through intentional .. */
382
383         case MSG_GETCURRENT:
384             twCC = set_onevalue(pCapability, TWTY_UINT16, activeDS.sane_param.depth);
385             break;
386     }
387 #endif
388     return twCC;
389 }
390
391 /* CAP_UICONTROLLABLE */
392 static TW_UINT16 SANE_CAPUiControllable(pTW_CAPABILITY pCapability, TW_UINT16 action)
393 {
394     TW_UINT16 twCC = TWCC_BADCAP;
395
396     TRACE("CAP_UICONTROLLABLE\n");
397
398     switch (action)
399     {
400         case MSG_QUERYSUPPORT:
401             twCC = set_onevalue(pCapability, TWTY_INT32, TWQC_GET);
402             break;
403
404         case MSG_GET:
405             twCC = set_onevalue(pCapability, TWTY_BOOL, TRUE);
406             break;
407
408     }
409     return twCC;
410 }
411
412 /* ICAP_COMPRESSION */
413 static TW_UINT16 SANE_ICAPCompression (pTW_CAPABILITY pCapability, TW_UINT16 action)
414 {
415     static const TW_UINT32 possible_values[] = { TWCP_NONE };
416     TW_UINT32 val;
417     TW_UINT16 twCC = TWCC_BADCAP;
418
419     TRACE("ICAP_COMPRESSION\n");
420
421     switch (action)
422     {
423         case MSG_QUERYSUPPORT:
424             twCC = set_onevalue(pCapability, TWTY_INT32,
425                     TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
426             break;
427
428         case MSG_GET:
429             twCC = msg_get_enum(pCapability, possible_values, sizeof(possible_values) / sizeof(possible_values[0]),
430                     TWTY_UINT16, TWCP_NONE, TWCP_NONE);
431             FIXME("Partial stub:  We don't attempt to support compression\n");
432             break;
433
434         case MSG_SET:
435             twCC = msg_set(pCapability, &val);
436             if (twCC == TWCC_SUCCESS)
437                FIXME("Partial Stub:  COMPRESSION set to %d, but ignored\n", val);
438             break;
439
440         case MSG_GETDEFAULT:
441             twCC = set_onevalue(pCapability, TWTY_UINT16, TWCP_NONE);
442             break;
443
444         case MSG_RESET:
445             /* .. fall through intentional .. */
446
447         case MSG_GETCURRENT:
448             twCC = set_onevalue(pCapability, TWTY_UINT16, TWCP_NONE);
449             break;
450     }
451     return twCC;
452 }
453
454 /* ICAP_XRESOLUTION, ICAP_YRESOLUTION  */
455 static TW_UINT16 SANE_ICAPResolution (pTW_CAPABILITY pCapability, TW_UINT16 action,  TW_UINT16 cap)
456 {
457     TW_UINT16 twCC = TWCC_BADCAP;
458 #ifdef SONAME_LIBSANE
459     TW_UINT32 val;
460     SANE_Int current_resolution;
461     TW_FIX32 *default_res;
462     const char *best_option_name;
463     SANE_Int minval, maxval, quantval;
464     SANE_Status sane_rc;
465     SANE_Int set_status;
466
467     TRACE("ICAP_%cRESOLUTION\n", cap == ICAP_XRESOLUTION ? 'X' : 'Y');
468
469     /* Some scanners support 'x-resolution', most seem to just support 'resolution' */
470     if (cap == ICAP_XRESOLUTION)
471     {
472         best_option_name = "x-resolution";
473         default_res = &activeDS.defaultXResolution;
474     }
475     else
476     {
477         best_option_name = "y-resolution";
478         default_res = &activeDS.defaultYResolution;
479     }
480     if (sane_option_get_int(activeDS.deviceHandle, best_option_name, &current_resolution) != SANE_STATUS_GOOD)
481     {
482         best_option_name = "resolution";
483         if (sane_option_get_int(activeDS.deviceHandle, best_option_name, &current_resolution) != SANE_STATUS_GOOD)
484             return TWCC_BADCAP;
485     }
486
487     /* Sane does not support a concept of 'default' resolution, so we have to
488      *   cache the resolution the very first time we load the scanner, and use that
489      *   as the default */
490     if (cap == ICAP_XRESOLUTION && ! activeDS.XResolutionSet)
491     {
492         default_res->Whole = current_resolution;
493         default_res->Frac = 0;
494         activeDS.XResolutionSet = TRUE;
495     }
496
497     if (cap == ICAP_YRESOLUTION && ! activeDS.YResolutionSet)
498     {
499         default_res->Whole = current_resolution;
500         default_res->Frac = 0;
501         activeDS.YResolutionSet = TRUE;
502     }
503
504     switch (action)
505     {
506         case MSG_QUERYSUPPORT:
507             twCC = set_onevalue(pCapability, TWTY_INT32,
508                     TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
509             break;
510
511         case MSG_GET:
512             sane_rc = sane_option_probe_resolution(activeDS.deviceHandle, best_option_name, &minval, &maxval, &quantval);
513             if (sane_rc != SANE_STATUS_GOOD)
514                 twCC = TWCC_BADCAP;
515             else
516                 twCC = msg_get_range(pCapability, TWTY_FIX32,
517                                 minval, maxval, quantval == 0 ? 1 : quantval, default_res->Whole, current_resolution);
518             break;
519
520         case MSG_SET:
521             twCC = msg_set(pCapability, &val);
522             if (twCC == TWCC_SUCCESS)
523             {
524                 TW_FIX32 f32;
525                 memcpy(&f32, &val, sizeof(f32));
526                 sane_rc = sane_option_set_int(activeDS.deviceHandle, best_option_name, f32.Whole, &set_status);
527                 if (sane_rc != SANE_STATUS_GOOD)
528                 {
529                     FIXME("Status of %d not expected or handled\n", sane_rc);
530                     twCC = TWCC_BADCAP;
531                 }
532                 else if (set_status == SANE_INFO_INEXACT)
533                     twCC = TWCC_CHECKSTATUS;
534             }
535             break;
536
537         case MSG_GETDEFAULT:
538             twCC = set_onevalue(pCapability, TWTY_FIX32, default_res->Whole);
539             break;
540
541         case MSG_RESET:
542             sane_rc = sane_option_set_int(activeDS.deviceHandle, best_option_name, default_res->Whole, NULL);
543             if (sane_rc != SANE_STATUS_GOOD)
544                 return TWCC_BADCAP;
545
546             /* .. fall through intentional .. */
547
548         case MSG_GETCURRENT:
549             twCC = set_onevalue(pCapability, TWTY_FIX32, current_resolution);
550             break;
551     }
552 #endif
553     return twCC;
554 }
555
556 /* ICAP_PIXELFLAVOR */
557 static TW_UINT16 SANE_ICAPPixelFlavor (pTW_CAPABILITY pCapability, TW_UINT16 action)
558 {
559     static const TW_UINT32 possible_values[] = { TWPF_CHOCOLATE, TWPF_VANILLA };
560     TW_UINT32 val;
561     TW_UINT16 twCC = TWCC_BADCAP;
562
563     TRACE("ICAP_PIXELFLAVOR\n");
564
565     switch (action)
566     {
567         case MSG_QUERYSUPPORT:
568             twCC = set_onevalue(pCapability, TWTY_INT32,
569                     TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
570             break;
571
572         case MSG_GET:
573             twCC = msg_get_enum(pCapability, possible_values, sizeof(possible_values) / sizeof(possible_values[0]),
574                     TWTY_UINT16, TWPF_CHOCOLATE, TWPF_CHOCOLATE);
575             break;
576
577         case MSG_SET:
578             twCC = msg_set(pCapability, &val);
579             if (twCC == TWCC_SUCCESS)
580             {
581                FIXME("Stub:  PIXELFLAVOR set to %d, but ignored\n", val);
582             }
583             break;
584
585         case MSG_GETDEFAULT:
586             twCC = set_onevalue(pCapability, TWTY_UINT16, TWPF_CHOCOLATE);
587             break;
588
589         case MSG_RESET:
590             /* .. fall through intentional .. */
591
592         case MSG_GETCURRENT:
593             twCC = set_onevalue(pCapability, TWTY_UINT16, TWPF_CHOCOLATE);
594             break;
595     }
596     return twCC;
597 }
598
599
600 TW_UINT16 SANE_SaneCapability (pTW_CAPABILITY pCapability, TW_UINT16 action)
601 {
602     TW_UINT16 twCC = TWCC_CAPUNSUPPORTED;
603
604     TRACE("capability=%d action=%d\n", pCapability->Cap, action);
605
606     switch (pCapability->Cap)
607     {
608         case CAP_SUPPORTEDCAPS:
609             if (action == MSG_GET)
610                 twCC = TWAIN_GetSupportedCaps(pCapability);
611             else
612                 twCC = TWCC_BADVALUE;
613             break;
614
615         case CAP_XFERCOUNT:
616             twCC = SANE_CAPXferCount (pCapability, action);
617             break;
618
619         case CAP_UICONTROLLABLE:
620             twCC = SANE_CAPUiControllable (pCapability, action);
621             break;
622
623         case ICAP_PIXELTYPE:
624             twCC = SANE_ICAPPixelType (pCapability, action);
625             break;
626
627         case ICAP_UNITS:
628             twCC = SANE_ICAPUnits (pCapability, action);
629             break;
630
631         case ICAP_BITDEPTH:
632             twCC = SANE_ICAPBitDepth(pCapability, action);
633             break;
634
635         case ICAP_XFERMECH:
636             twCC = SANE_ICAPXferMech (pCapability, action);
637             break;
638
639         case ICAP_PIXELFLAVOR:
640             twCC = SANE_ICAPPixelFlavor (pCapability, action);
641             break;
642
643         case ICAP_COMPRESSION:
644             twCC = SANE_ICAPCompression(pCapability, action);
645             break;
646
647         case ICAP_XRESOLUTION:
648             twCC = SANE_ICAPResolution(pCapability, action, pCapability->Cap);
649             break;
650
651         case ICAP_YRESOLUTION:
652             twCC = SANE_ICAPResolution(pCapability, action, pCapability->Cap);
653             break;
654     }
655
656     /* Twain specifies that you should return a 0 in response to QUERYSUPPORT,
657      *   even if you don't formally support the capability */
658     if (twCC == TWCC_CAPUNSUPPORTED && action == MSG_QUERYSUPPORT)
659         twCC = set_onevalue(pCapability, 0, TWTY_INT32);
660
661     if (twCC == TWCC_CAPUNSUPPORTED)
662         TRACE("capability 0x%x/action=%d being reported as unsupported\n", pCapability->Cap, action);
663
664     return twCC;
665 }