strmbase: Add support for rendering algorithms to quality control.
[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 #include <stdio.h>
26 #include <math.h>
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "twain.h"
31 #include "sane_i.h"
32 #include "winnls.h"
33 #include "wine/debug.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(twain);
36
37 #ifndef SANE_VALUE_SCAN_MODE_COLOR
38 #define SANE_VALUE_SCAN_MODE_COLOR              SANE_I18N("Color")
39 #endif
40 #ifndef SANE_VALUE_SCAN_MODE_GRAY
41 #define SANE_VALUE_SCAN_MODE_GRAY               SANE_I18N("Gray")
42 #endif
43 #ifndef SANE_VALUE_SCAN_MODE_LINEART
44 #define SANE_VALUE_SCAN_MODE_LINEART            SANE_I18N("Lineart")
45 #endif
46
47 static TW_UINT16 get_onevalue(pTW_CAPABILITY pCapability, TW_UINT16 *type, TW_UINT32 *value)
48 {
49     if (pCapability->hContainer)
50     {
51         pTW_ONEVALUE pVal = GlobalLock (pCapability->hContainer);
52         if (pVal)
53         {
54             *value = pVal->Item;
55             if (type)
56                 *type = pVal->ItemType;
57             GlobalUnlock (pCapability->hContainer);
58             return TWCC_SUCCESS;
59         }
60     }
61     return TWCC_BUMMER;
62 }
63
64
65 static TW_UINT16 set_onevalue(pTW_CAPABILITY pCapability, TW_UINT16 type, TW_UINT32 value)
66 {
67     pCapability->hContainer = GlobalAlloc (0, sizeof(TW_ONEVALUE));
68
69     if (pCapability->hContainer)
70     {
71         pTW_ONEVALUE pVal = GlobalLock (pCapability->hContainer);
72         if (pVal)
73         {
74             pCapability->ConType = TWON_ONEVALUE;
75             pVal->ItemType = type;
76             pVal->Item = value;
77             GlobalUnlock (pCapability->hContainer);
78             return TWCC_SUCCESS;
79         }
80     }
81    return TWCC_LOWMEMORY;
82 }
83
84 static TW_UINT16 msg_set(pTW_CAPABILITY pCapability, TW_UINT32 *val)
85 {
86     if (pCapability->ConType == TWON_ONEVALUE)
87         return get_onevalue(pCapability, NULL, val);
88
89     FIXME("Partial Stub:  MSG_SET only supports TW_ONEVALUE\n");
90     return TWCC_BADCAP;
91 }
92
93
94 static TW_UINT16 msg_get_enum(pTW_CAPABILITY pCapability, const TW_UINT32 *values, int value_count,
95                               TW_UINT16 type, TW_UINT32 current, TW_UINT32 default_value)
96 {
97     TW_ENUMERATION *enumv = NULL;
98     TW_UINT32 *p32;
99     TW_UINT16 *p16;
100     int i;
101
102     pCapability->ConType = TWON_ENUMERATION;
103     pCapability->hContainer = 0;
104
105     if (type == TWTY_INT16 || type == TWTY_UINT16)
106         pCapability->hContainer = GlobalAlloc (0, FIELD_OFFSET( TW_ENUMERATION, ItemList[value_count * sizeof(TW_UINT16)]));
107
108     if (type == TWTY_INT32 || type == TWTY_UINT32)
109         pCapability->hContainer = GlobalAlloc (0, FIELD_OFFSET( TW_ENUMERATION, ItemList[value_count * sizeof(TW_UINT32)]));
110
111     if (pCapability->hContainer)
112         enumv = GlobalLock(pCapability->hContainer);
113
114     if (! enumv)
115         return TWCC_LOWMEMORY;
116
117     enumv->ItemType = type;
118     enumv->NumItems = value_count;
119
120     p16 = (TW_UINT16 *) enumv->ItemList;
121     p32 = (TW_UINT32 *) enumv->ItemList;
122     for (i = 0; i < value_count; i++)
123     {
124         if (values[i] == current)
125             enumv->CurrentIndex = i;
126         if (values[i] == default_value)
127             enumv->DefaultIndex = i;
128         if (type == TWTY_INT16 || type == TWTY_UINT16)
129             p16[i] = values[i];
130         if (type == TWTY_INT32 || type == TWTY_UINT32)
131             p32[i] = values[i];
132     }
133
134     GlobalUnlock(pCapability->hContainer);
135     return TWCC_SUCCESS;
136 }
137
138 #ifdef SONAME_LIBSANE
139 static TW_UINT16 msg_get_range(pTW_CAPABILITY pCapability, TW_UINT16 type,
140             TW_UINT32 minval, TW_UINT32 maxval, TW_UINT32 step, TW_UINT32 def,  TW_UINT32 current)
141 {
142     TW_RANGE *range = NULL;
143
144     pCapability->ConType = TWON_RANGE;
145     pCapability->hContainer = 0;
146
147     pCapability->hContainer = GlobalAlloc (0, sizeof(*range));
148     if (pCapability->hContainer)
149         range = GlobalLock(pCapability->hContainer);
150
151     if (! range)
152         return TWCC_LOWMEMORY;
153
154     range->ItemType = type;
155     range->MinValue = minval;
156     range->MaxValue = maxval;
157     range->StepSize = step;
158     range->DefaultValue = def;
159     range->CurrentValue = current;
160
161     GlobalUnlock(pCapability->hContainer);
162     return TWCC_SUCCESS;
163 }
164 #endif
165
166 static TW_UINT16 TWAIN_GetSupportedCaps(pTW_CAPABILITY pCapability)
167 {
168     TW_ARRAY *a;
169     static const UINT16 supported_caps[] = { CAP_SUPPORTEDCAPS, CAP_XFERCOUNT, CAP_UICONTROLLABLE,
170                     CAP_AUTOFEED, CAP_FEEDERENABLED,
171                     ICAP_XFERMECH, ICAP_PIXELTYPE, ICAP_UNITS, ICAP_BITDEPTH, ICAP_COMPRESSION, ICAP_PIXELFLAVOR,
172                     ICAP_XRESOLUTION, ICAP_YRESOLUTION, ICAP_PHYSICALHEIGHT, ICAP_PHYSICALWIDTH, ICAP_SUPPORTEDSIZES };
173
174     pCapability->hContainer = GlobalAlloc (0, FIELD_OFFSET( TW_ARRAY, ItemList[sizeof(supported_caps)] ));
175     pCapability->ConType = TWON_ARRAY;
176
177     if (pCapability->hContainer)
178     {
179         UINT16 *u;
180         int i;
181         a = GlobalLock (pCapability->hContainer);
182         a->ItemType = TWTY_UINT16;
183         a->NumItems = sizeof(supported_caps) / sizeof(supported_caps[0]);
184         u = (UINT16 *) a->ItemList;
185         for (i = 0; i < a->NumItems; i++)
186             u[i] = supported_caps[i];
187         GlobalUnlock (pCapability->hContainer);
188         return TWCC_SUCCESS;
189     }
190     else
191         return TWCC_LOWMEMORY;
192 }
193
194
195 /* ICAP_XFERMECH */
196 static TW_UINT16 SANE_ICAPXferMech (pTW_CAPABILITY pCapability, TW_UINT16 action)
197 {
198     static const TW_UINT32 possible_values[] = { TWSX_NATIVE, TWSX_MEMORY };
199     TW_UINT32 val;
200     TW_UINT16 twCC = TWCC_BADCAP;
201
202     TRACE("ICAP_XFERMECH\n");
203
204     switch (action)
205     {
206         case MSG_QUERYSUPPORT:
207             twCC = set_onevalue(pCapability, TWTY_INT32,
208                     TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
209             break;
210
211         case MSG_GET:
212             twCC = msg_get_enum(pCapability, possible_values, sizeof(possible_values) / sizeof(possible_values[0]),
213                     TWTY_UINT16, activeDS.capXferMech, TWSX_NATIVE);
214             break;
215
216         case MSG_SET:
217             twCC = msg_set(pCapability, &val);
218             if (twCC == TWCC_SUCCESS)
219             {
220                activeDS.capXferMech = (TW_UINT16) val;
221                FIXME("Partial Stub:  XFERMECH set to %d, but ignored\n", val);
222             }
223             break;
224
225         case MSG_GETDEFAULT:
226             twCC = set_onevalue(pCapability, TWTY_UINT16, TWSX_NATIVE);
227             break;
228
229         case MSG_RESET:
230             activeDS.capXferMech = TWSX_NATIVE;
231             /* .. fall through intentional .. */
232
233         case MSG_GETCURRENT:
234             twCC = set_onevalue(pCapability, TWTY_UINT16, activeDS.capXferMech);
235             FIXME("Partial Stub:  XFERMECH of %d not actually used\n", activeDS.capXferMech);
236             break;
237     }
238     return twCC;
239 }
240
241
242 /* CAP_XFERCOUNT */
243 static TW_UINT16 SANE_CAPXferCount (pTW_CAPABILITY pCapability, TW_UINT16 action)
244 {
245     TW_UINT32 val;
246     TW_UINT16 twCC = TWCC_BADCAP;
247
248     TRACE("CAP_XFERCOUNT\n");
249
250     switch (action)
251     {
252         case MSG_QUERYSUPPORT:
253             twCC = set_onevalue(pCapability, TWTY_INT32,
254                     TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
255             break;
256
257         case MSG_GET:
258             twCC = set_onevalue(pCapability, TWTY_INT16, -1);
259             FIXME("Partial Stub:  Reporting only support for transfer all\n");
260             break;
261
262         case MSG_SET:
263             twCC = msg_set(pCapability, &val);
264             if (twCC == TWCC_SUCCESS)
265                FIXME("Partial Stub:  XFERCOUNT set to %d, but ignored\n", val);
266             break;
267
268         case MSG_GETDEFAULT:
269             twCC = set_onevalue(pCapability, TWTY_INT16, -1);
270             break;
271
272         case MSG_RESET:
273             /* .. fall through intentional .. */
274
275         case MSG_GETCURRENT:
276             twCC = set_onevalue(pCapability, TWTY_INT16, -1);
277             break;
278     }
279     return twCC;
280 }
281
282 #ifdef SONAME_LIBSANE
283 static BOOL pixeltype_to_sane_mode(TW_UINT16 pixeltype, SANE_String mode, int len)
284 {
285     SANE_String_Const m = NULL;
286     switch (pixeltype)
287     {
288         case TWPT_GRAY:
289             m = SANE_VALUE_SCAN_MODE_GRAY;
290             break;
291         case TWPT_RGB:
292             m = SANE_VALUE_SCAN_MODE_COLOR;
293             break;
294         case TWPT_BW:
295             m = SANE_VALUE_SCAN_MODE_LINEART;
296             break;
297     }
298     if (! m)
299         return FALSE;
300     if (strlen(m) >= len)
301         return FALSE;
302     strcpy(mode, m);
303     return TRUE;
304 }
305 static BOOL sane_mode_to_pixeltype(SANE_String_Const mode, TW_UINT16 *pixeltype)
306 {
307     if (strcmp(mode, SANE_VALUE_SCAN_MODE_LINEART) == 0)
308         *pixeltype = TWPT_BW;
309     else if (memcmp(mode, SANE_VALUE_SCAN_MODE_GRAY, strlen(SANE_VALUE_SCAN_MODE_GRAY)) == 0)
310         *pixeltype = TWPT_GRAY;
311     else if (strcmp(mode, SANE_VALUE_SCAN_MODE_COLOR) == 0)
312         *pixeltype = TWPT_RGB;
313     else
314         return FALSE;
315
316     return TRUE;
317 }
318 #endif
319
320 /* ICAP_PIXELTYPE */
321 static TW_UINT16 SANE_ICAPPixelType (pTW_CAPABILITY pCapability, TW_UINT16 action)
322 {
323     TW_UINT16 twCC = TWCC_BADCAP;
324 #ifdef SONAME_LIBSANE
325     TW_UINT32 possible_values[3];
326     int possible_value_count;
327     TW_UINT32 val;
328     SANE_Status rc;
329     SANE_Int status;
330     SANE_String_Const *choices;
331     char current_mode[64];
332     TW_UINT16 current_pixeltype = TWPT_BW;
333     SANE_Char mode[64];
334
335     TRACE("ICAP_PIXELTYPE\n");
336
337     rc = sane_option_probe_mode(activeDS.deviceHandle, &choices, current_mode, sizeof(current_mode));
338     if (rc != SANE_STATUS_GOOD)
339     {
340         ERR("Unable to retrieve mode from sane, ICAP_PIXELTYPE unsupported\n");
341         return twCC;
342     }
343
344     sane_mode_to_pixeltype(current_mode, &current_pixeltype);
345
346     /* Sane does not support a concept of a default mode, so we simply cache
347      *   the first mode we find */
348     if (! activeDS.PixelTypeSet)
349     {
350         activeDS.PixelTypeSet = TRUE;
351         activeDS.defaultPixelType = current_pixeltype;
352     }
353
354     switch (action)
355     {
356         case MSG_QUERYSUPPORT:
357             twCC = set_onevalue(pCapability, TWTY_INT32,
358                     TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
359             break;
360
361         case MSG_GET:
362             for (possible_value_count = 0; choices && *choices && possible_value_count < 3; choices++)
363             {
364                 TW_UINT16 pix;
365                 if (sane_mode_to_pixeltype(*choices, &pix))
366                     possible_values[possible_value_count++] = pix;
367             }
368             twCC = msg_get_enum(pCapability, possible_values, possible_value_count,
369                     TWTY_UINT16, current_pixeltype, activeDS.defaultPixelType);
370             break;
371
372         case MSG_SET:
373             twCC = msg_set(pCapability, &val);
374             if (twCC == TWCC_SUCCESS)
375             {
376                 TRACE("Setting pixeltype to %d\n", val);
377                 if (! pixeltype_to_sane_mode(val, mode, sizeof(mode)))
378                     return TWCC_BADVALUE;
379
380                 status = 0;
381                 rc = sane_option_set_str(activeDS.deviceHandle, "mode", mode, &status);
382                 /* Some SANE devices use 'Grayscale' instead of the standard 'Gray' */
383                 if (rc == SANE_STATUS_INVAL && strcmp(mode, SANE_VALUE_SCAN_MODE_GRAY) == 0)
384                 {
385                     strcpy(mode, "Grayscale");
386                     rc = sane_option_set_str(activeDS.deviceHandle, "mode", mode, &status);
387                 }
388                 if (rc != SANE_STATUS_GOOD)
389                     return sane_status_to_twcc(rc);
390                 if (status & SANE_INFO_RELOAD_PARAMS)
391                     psane_get_parameters (activeDS.deviceHandle, &activeDS.sane_param);
392             }
393             break;
394
395         case MSG_GETDEFAULT:
396             twCC = set_onevalue(pCapability, TWTY_UINT16, activeDS.defaultPixelType);
397             break;
398
399         case MSG_RESET:
400             current_pixeltype = activeDS.defaultPixelType;
401             if (! pixeltype_to_sane_mode(current_pixeltype, mode, sizeof(mode)))
402                 return TWCC_BADVALUE;
403
404             status = 0;
405             rc = sane_option_set_str(activeDS.deviceHandle, "mode", mode, &status);
406             /* Some SANE devices use 'Grayscale' instead of the standard 'Gray' */
407             if (rc == SANE_STATUS_INVAL && strcmp(mode, SANE_VALUE_SCAN_MODE_GRAY) == 0)
408             {
409                 strcpy(mode, "Grayscale");
410                 rc = sane_option_set_str(activeDS.deviceHandle, "mode", mode, &status);
411             }
412             if (rc != SANE_STATUS_GOOD)
413                 return sane_status_to_twcc(rc);
414             if (status & SANE_INFO_RELOAD_PARAMS)
415                 psane_get_parameters (activeDS.deviceHandle, &activeDS.sane_param);
416
417             /* .. fall through intentional .. */
418
419         case MSG_GETCURRENT:
420             twCC = set_onevalue(pCapability, TWTY_UINT16, current_pixeltype);
421             TRACE("Returning current pixeltype of %d\n", current_pixeltype);
422             break;
423     }
424
425 #endif
426     return twCC;
427 }
428
429 /* ICAP_UNITS */
430 static TW_UINT16 SANE_ICAPUnits (pTW_CAPABILITY pCapability, TW_UINT16 action)
431 {
432     TW_UINT32 val;
433     TW_UINT16 twCC = TWCC_BADCAP;
434
435     TRACE("ICAP_UNITS\n");
436
437     switch (action)
438     {
439         case MSG_QUERYSUPPORT:
440             twCC = set_onevalue(pCapability, TWTY_INT32,
441                     TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
442             break;
443
444         case MSG_GET:
445             twCC = set_onevalue(pCapability, TWTY_UINT16, TWUN_INCHES);
446             break;
447
448         case MSG_SET:
449             twCC = msg_set(pCapability, &val);
450             if (twCC == TWCC_SUCCESS)
451             {
452                 if  (val != TWUN_INCHES)
453                 {
454                     ERR("Sane supports only SANE_UNIT_DPI\n");
455                     twCC = TWCC_BADVALUE;
456                 }
457             }
458             break;
459
460         case MSG_GETDEFAULT:
461         case MSG_RESET:
462             /* .. fall through intentional .. */
463
464         case MSG_GETCURRENT:
465             twCC = set_onevalue(pCapability, TWTY_UINT16, TWUN_INCHES);
466             break;
467     }
468
469     return twCC;
470 }
471
472 /* ICAP_BITDEPTH */
473 static TW_UINT16 SANE_ICAPBitDepth(pTW_CAPABILITY pCapability, TW_UINT16 action)
474 {
475     TW_UINT16 twCC = TWCC_BADCAP;
476 #ifdef SONAME_LIBSANE
477     TW_UINT32 possible_values[1];
478
479     TRACE("ICAP_BITDEPTH\n");
480
481     possible_values[0] = activeDS.sane_param.depth;
482
483     switch (action)
484     {
485         case MSG_QUERYSUPPORT:
486             twCC = set_onevalue(pCapability, TWTY_INT32,
487                     TWQC_GET | TWQC_GETDEFAULT | TWQC_GETCURRENT  );
488             break;
489
490         case MSG_GET:
491             twCC = msg_get_enum(pCapability, possible_values, sizeof(possible_values) / sizeof(possible_values[0]),
492                     TWTY_UINT16, activeDS.sane_param.depth, activeDS.sane_param.depth);
493             break;
494
495         case MSG_GETDEFAULT:
496             /* .. Fall through intentional .. */
497
498         case MSG_GETCURRENT:
499             TRACE("Returning current bitdepth of %d\n", activeDS.sane_param.depth);
500             twCC = set_onevalue(pCapability, TWTY_UINT16, activeDS.sane_param.depth);
501             break;
502     }
503 #endif
504     return twCC;
505 }
506
507 /* CAP_UICONTROLLABLE */
508 static TW_UINT16 SANE_CAPUiControllable(pTW_CAPABILITY pCapability, TW_UINT16 action)
509 {
510     TW_UINT16 twCC = TWCC_BADCAP;
511
512     TRACE("CAP_UICONTROLLABLE\n");
513
514     switch (action)
515     {
516         case MSG_QUERYSUPPORT:
517             twCC = set_onevalue(pCapability, TWTY_INT32, TWQC_GET);
518             break;
519
520         case MSG_GET:
521             twCC = set_onevalue(pCapability, TWTY_BOOL, TRUE);
522             break;
523
524     }
525     return twCC;
526 }
527
528 /* ICAP_COMPRESSION */
529 static TW_UINT16 SANE_ICAPCompression (pTW_CAPABILITY pCapability, TW_UINT16 action)
530 {
531     static const TW_UINT32 possible_values[] = { TWCP_NONE };
532     TW_UINT32 val;
533     TW_UINT16 twCC = TWCC_BADCAP;
534
535     TRACE("ICAP_COMPRESSION\n");
536
537     switch (action)
538     {
539         case MSG_QUERYSUPPORT:
540             twCC = set_onevalue(pCapability, TWTY_INT32,
541                     TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
542             break;
543
544         case MSG_GET:
545             twCC = msg_get_enum(pCapability, possible_values, sizeof(possible_values) / sizeof(possible_values[0]),
546                     TWTY_UINT16, TWCP_NONE, TWCP_NONE);
547             FIXME("Partial stub:  We don't attempt to support compression\n");
548             break;
549
550         case MSG_SET:
551             twCC = msg_set(pCapability, &val);
552             if (twCC == TWCC_SUCCESS)
553                FIXME("Partial Stub:  COMPRESSION set to %d, but ignored\n", val);
554             break;
555
556         case MSG_GETDEFAULT:
557             twCC = set_onevalue(pCapability, TWTY_UINT16, TWCP_NONE);
558             break;
559
560         case MSG_RESET:
561             /* .. fall through intentional .. */
562
563         case MSG_GETCURRENT:
564             twCC = set_onevalue(pCapability, TWTY_UINT16, TWCP_NONE);
565             break;
566     }
567     return twCC;
568 }
569
570 /* ICAP_XRESOLUTION, ICAP_YRESOLUTION  */
571 static TW_UINT16 SANE_ICAPResolution (pTW_CAPABILITY pCapability, TW_UINT16 action,  TW_UINT16 cap)
572 {
573     TW_UINT16 twCC = TWCC_BADCAP;
574 #ifdef SONAME_LIBSANE
575     TW_UINT32 val;
576     SANE_Int current_resolution;
577     TW_FIX32 *default_res;
578     const char *best_option_name;
579     SANE_Int minval, maxval, quantval;
580     SANE_Status sane_rc;
581     SANE_Int set_status;
582
583     TRACE("ICAP_%cRESOLUTION\n", cap == ICAP_XRESOLUTION ? 'X' : 'Y');
584
585     /* Some scanners support 'x-resolution', most seem to just support 'resolution' */
586     if (cap == ICAP_XRESOLUTION)
587     {
588         best_option_name = "x-resolution";
589         default_res = &activeDS.defaultXResolution;
590     }
591     else
592     {
593         best_option_name = "y-resolution";
594         default_res = &activeDS.defaultYResolution;
595     }
596     if (sane_option_get_int(activeDS.deviceHandle, best_option_name, &current_resolution) != SANE_STATUS_GOOD)
597     {
598         best_option_name = "resolution";
599         if (sane_option_get_int(activeDS.deviceHandle, best_option_name, &current_resolution) != SANE_STATUS_GOOD)
600             return TWCC_BADCAP;
601     }
602
603     /* Sane does not support a concept of 'default' resolution, so we have to
604      *   cache the resolution the very first time we load the scanner, and use that
605      *   as the default */
606     if (cap == ICAP_XRESOLUTION && ! activeDS.XResolutionSet)
607     {
608         default_res->Whole = current_resolution;
609         default_res->Frac = 0;
610         activeDS.XResolutionSet = TRUE;
611     }
612
613     if (cap == ICAP_YRESOLUTION && ! activeDS.YResolutionSet)
614     {
615         default_res->Whole = current_resolution;
616         default_res->Frac = 0;
617         activeDS.YResolutionSet = TRUE;
618     }
619
620     switch (action)
621     {
622         case MSG_QUERYSUPPORT:
623             twCC = set_onevalue(pCapability, TWTY_INT32,
624                     TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
625             break;
626
627         case MSG_GET:
628             sane_rc = sane_option_probe_resolution(activeDS.deviceHandle, best_option_name, &minval, &maxval, &quantval);
629             if (sane_rc != SANE_STATUS_GOOD)
630                 twCC = TWCC_BADCAP;
631             else
632                 twCC = msg_get_range(pCapability, TWTY_FIX32,
633                                 minval, maxval, quantval == 0 ? 1 : quantval, default_res->Whole, current_resolution);
634             break;
635
636         case MSG_SET:
637             twCC = msg_set(pCapability, &val);
638             if (twCC == TWCC_SUCCESS)
639             {
640                 TW_FIX32 f32;
641                 memcpy(&f32, &val, sizeof(f32));
642                 sane_rc = sane_option_set_int(activeDS.deviceHandle, best_option_name, f32.Whole, &set_status);
643                 if (sane_rc != SANE_STATUS_GOOD)
644                 {
645                     FIXME("Status of %d not expected or handled\n", sane_rc);
646                     twCC = TWCC_BADCAP;
647                 }
648                 else if (set_status == SANE_INFO_INEXACT)
649                     twCC = TWCC_CHECKSTATUS;
650             }
651             break;
652
653         case MSG_GETDEFAULT:
654             twCC = set_onevalue(pCapability, TWTY_FIX32, default_res->Whole);
655             break;
656
657         case MSG_RESET:
658             sane_rc = sane_option_set_int(activeDS.deviceHandle, best_option_name, default_res->Whole, NULL);
659             if (sane_rc != SANE_STATUS_GOOD)
660                 return TWCC_BADCAP;
661
662             /* .. fall through intentional .. */
663
664         case MSG_GETCURRENT:
665             twCC = set_onevalue(pCapability, TWTY_FIX32, current_resolution);
666             break;
667     }
668 #endif
669     return twCC;
670 }
671
672 /* ICAP_PHYSICALHEIGHT, ICAP_PHYSICALWIDTH */
673 static TW_UINT16 SANE_ICAPPhysical (pTW_CAPABILITY pCapability, TW_UINT16 action,  TW_UINT16 cap)
674 {
675     TW_UINT16 twCC = TWCC_BADCAP;
676 #ifdef SONAME_LIBSANE
677     TW_FIX32 res;
678     char option_name[64];
679     SANE_Fixed lower, upper;
680     SANE_Unit lowerunit, upperunit;
681     SANE_Status status;
682
683     TRACE("ICAP_PHYSICAL%s\n", cap == ICAP_PHYSICALHEIGHT? "HEIGHT" : "WIDTH");
684
685     sprintf(option_name, "tl-%c", cap == ICAP_PHYSICALHEIGHT ? 'y' : 'x');
686     status = sane_option_probe_scan_area(activeDS.deviceHandle, option_name, NULL, &lowerunit, &lower, NULL, NULL);
687     if (status != SANE_STATUS_GOOD)
688         return sane_status_to_twcc(status);
689
690     sprintf(option_name, "br-%c", cap == ICAP_PHYSICALHEIGHT ? 'y' : 'x');
691     status = sane_option_probe_scan_area(activeDS.deviceHandle, option_name, NULL, &upperunit, NULL, &upper, NULL);
692     if (status != SANE_STATUS_GOOD)
693         return sane_status_to_twcc(status);
694
695     if (upperunit != lowerunit)
696         return TWCC_BADCAP;
697
698     if (! convert_sane_res_to_twain(SANE_UNFIX(upper) - SANE_UNFIX(lower), upperunit, &res, TWUN_INCHES))
699         return TWCC_BADCAP;
700
701     switch (action)
702     {
703         case MSG_QUERYSUPPORT:
704             twCC = set_onevalue(pCapability, TWTY_INT32,
705                     TWQC_GET | TWQC_GETDEFAULT | TWQC_GETCURRENT );
706             break;
707
708         case MSG_GET:
709         case MSG_GETDEFAULT:
710
711             /* .. fall through intentional .. */
712
713         case MSG_GETCURRENT:
714             twCC = set_onevalue(pCapability, TWTY_FIX32, res.Whole | (res.Frac << 16));
715             break;
716     }
717 #endif
718     return twCC;
719 }
720
721 /* ICAP_PIXELFLAVOR */
722 static TW_UINT16 SANE_ICAPPixelFlavor (pTW_CAPABILITY pCapability, TW_UINT16 action)
723 {
724     TW_UINT16 twCC = TWCC_BADCAP;
725 #ifdef SONAME_LIBSANE
726     static const TW_UINT32 possible_values[] = { TWPF_CHOCOLATE, TWPF_VANILLA };
727     TW_UINT32 val;
728     TW_UINT32 flavor = activeDS.sane_param.depth == 1 ? TWPF_VANILLA : TWPF_CHOCOLATE;
729
730     TRACE("ICAP_PIXELFLAVOR\n");
731
732     switch (action)
733     {
734         case MSG_QUERYSUPPORT:
735             twCC = set_onevalue(pCapability, TWTY_INT32,
736                     TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
737             break;
738
739         case MSG_GET:
740             twCC = msg_get_enum(pCapability, possible_values, sizeof(possible_values) / sizeof(possible_values[0]),
741                     TWTY_UINT16, flavor, flavor);
742             break;
743
744         case MSG_SET:
745             twCC = msg_set(pCapability, &val);
746             if (twCC == TWCC_SUCCESS)
747             {
748                FIXME("Stub:  PIXELFLAVOR set to %d, but ignored\n", val);
749             }
750             break;
751
752         case MSG_GETDEFAULT:
753             twCC = set_onevalue(pCapability, TWTY_UINT16, flavor);
754             break;
755
756         case MSG_RESET:
757             /* .. fall through intentional .. */
758
759         case MSG_GETCURRENT:
760             twCC = set_onevalue(pCapability, TWTY_UINT16, flavor);
761             break;
762     }
763 #endif
764     return twCC;
765 }
766
767 #ifdef SONAME_LIBSANE
768 static TW_UINT16 get_width_height(double *width, double *height, BOOL max)
769 {
770     SANE_Status status;
771
772     SANE_Fixed tlx_current, tlx_min, tlx_max;
773     SANE_Fixed tly_current, tly_min, tly_max;
774     SANE_Fixed brx_current, brx_min, brx_max;
775     SANE_Fixed bry_current, bry_min, bry_max;
776
777     status = sane_option_probe_scan_area(activeDS.deviceHandle, "tl-x", &tlx_current, NULL, &tlx_min, &tlx_max, NULL);
778     if (status != SANE_STATUS_GOOD)
779         return sane_status_to_twcc(status);
780
781     status = sane_option_probe_scan_area(activeDS.deviceHandle, "tl-y", &tly_current, NULL, &tly_min, &tly_max, NULL);
782     if (status != SANE_STATUS_GOOD)
783         return sane_status_to_twcc(status);
784
785     status = sane_option_probe_scan_area(activeDS.deviceHandle, "br-x", &brx_current, NULL, &brx_min, &brx_max, NULL);
786     if (status != SANE_STATUS_GOOD)
787         return sane_status_to_twcc(status);
788
789     status = sane_option_probe_scan_area(activeDS.deviceHandle, "br-y", &bry_current, NULL, &bry_min, &bry_max, NULL);
790     if (status != SANE_STATUS_GOOD)
791         return sane_status_to_twcc(status);
792
793     if (max)
794         *width = SANE_UNFIX(brx_max) - SANE_UNFIX(tlx_min);
795     else
796         *width = SANE_UNFIX(brx_current) - SANE_UNFIX(tlx_current);
797
798     if (max)
799         *height = SANE_UNFIX(bry_max) - SANE_UNFIX(tly_min);
800     else
801         *height = SANE_UNFIX(bry_current) - SANE_UNFIX(tly_current);
802
803     return(TWCC_SUCCESS);
804 }
805
806 static TW_UINT16 set_one_coord(const char *name, double coord)
807 {
808     SANE_Status status;
809     status = sane_option_set_fixed(activeDS.deviceHandle, name, SANE_FIX(coord), NULL);
810     if (status != SANE_STATUS_GOOD)
811         return sane_status_to_twcc(status);
812     return TWCC_SUCCESS;
813 }
814
815 static TW_UINT16 set_width_height(double width, double height)
816 {
817     TW_UINT16 rc = TWCC_SUCCESS;
818     rc = set_one_coord("tl-x", 0);
819     if (rc != TWCC_SUCCESS)
820         return rc;
821     rc = set_one_coord("br-x", width);
822     if (rc != TWCC_SUCCESS)
823         return rc;
824     rc = set_one_coord("tl-y", 0);
825     if (rc != TWCC_SUCCESS)
826         return rc;
827     rc = set_one_coord("br-y", height);
828
829     return rc;
830 }
831
832 typedef struct
833 {
834     TW_UINT32 size;
835     double x;
836     double y;
837 } supported_size_t;
838
839 static const supported_size_t supported_sizes[] =
840 {
841     { TWSS_NONE,        0,      0       },
842     { TWSS_A4,          210,    297     },
843     { TWSS_JISB5,       182,    257     },
844     { TWSS_USLETTER,    215.9,  279.4   },
845     { TWSS_USLEGAL,     215.9,  355.6   },
846     { TWSS_A5,          148,    210     },
847     { TWSS_B4,          250,    353     },
848     { TWSS_B6,          125,    176     },
849     { TWSS_USLEDGER,    215.9,  431.8   },
850     { TWSS_USEXECUTIVE, 184.15, 266.7   },
851     { TWSS_A3,          297,    420     },
852 };
853 #define SUPPORTED_SIZE_COUNT (sizeof(supported_sizes) / sizeof(supported_sizes[0]))
854
855 static TW_UINT16 get_default_paper_size(const supported_size_t *s, int n)
856 {
857     DWORD paper;
858     int rc;
859     int defsize = -1;
860     double width, height;
861     int i;
862     rc = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_IPAPERSIZE | LOCALE_RETURN_NUMBER, (void *) &paper, sizeof(paper));
863     if (rc > 0)
864         switch (paper)
865         {
866             case 1:
867                 defsize = TWSS_USLETTER;
868                 break;
869             case 5:
870                 defsize = TWSS_USLEGAL;
871                 break;
872             case 8:
873                 defsize = TWSS_A3;
874                 break;
875             case 9:
876                 defsize = TWSS_A4;
877                 break;
878         }
879
880     if (defsize == -1)
881         return TWSS_NONE;
882
883     if (get_width_height(&width, &height, TRUE) != TWCC_SUCCESS)
884         return TWSS_NONE;
885
886     for (i = 0; i < n; i++)
887         if (s[i].size == defsize)
888         {
889             /* Sane's use of integers to store floats is a hair lossy; deal with it */
890             if (s[i].x > (width + .01) || s[i].y > (height + 0.01))
891                 return TWSS_NONE;
892             else
893                 return s[i].size;
894         }
895
896     return TWSS_NONE;
897 }
898
899 static TW_UINT16 get_current_paper_size(const supported_size_t *s, int n)
900 {
901     int i;
902     double width, height;
903     double xdelta, ydelta;
904
905     if (get_width_height(&width, &height, FALSE) != TWCC_SUCCESS)
906         return TWSS_NONE;
907
908     for (i = 0; i < n; i++)
909     {
910         /* Sane's use of integers to store floats results
911          * in a very small error; cope with that */
912         xdelta = s[i].x - width;
913         ydelta = s[i].y - height;
914         if (xdelta < 0.01 && xdelta > -0.01 &&
915             ydelta < 0.01 && ydelta > -0.01)
916             return s[i].size;
917     }
918
919     return TWSS_NONE;
920 }
921 #endif
922
923 /* ICAP_SUPPORTEDSIZES */
924 static TW_UINT16 SANE_ICAPSupportedSizes (pTW_CAPABILITY pCapability, TW_UINT16 action)
925 {
926     TW_UINT16 twCC = TWCC_BADCAP;
927 #ifdef SONAME_LIBSANE
928
929     static TW_UINT32 possible_values[SUPPORTED_SIZE_COUNT];
930     int i;
931     TW_UINT32 val;
932     TW_UINT16 default_size = get_default_paper_size(supported_sizes, SUPPORTED_SIZE_COUNT);
933     TW_UINT16 current_size = get_current_paper_size(supported_sizes, SUPPORTED_SIZE_COUNT);
934
935     TRACE("ICAP_SUPPORTEDSIZES\n");
936
937     switch (action)
938     {
939         case MSG_QUERYSUPPORT:
940             twCC = set_onevalue(pCapability, TWTY_INT32,
941                     TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
942             break;
943
944         case MSG_GET:
945             for (i = 0; i < sizeof(supported_sizes) / sizeof(supported_sizes[0]); i++)
946                 possible_values[i] = supported_sizes[i].size;
947             twCC = msg_get_enum(pCapability, possible_values, sizeof(possible_values) / sizeof(possible_values[0]),
948                     TWTY_UINT16, current_size, default_size);
949             WARN("Partial Stub:  our supported size selection is a bit thin.\n");
950             break;
951
952         case MSG_SET:
953             twCC = msg_set(pCapability, &val);
954             if (twCC == TWCC_SUCCESS)
955                 for (i = 1; i < SUPPORTED_SIZE_COUNT; i++)
956                     if (supported_sizes[i].size == val)
957                         return set_width_height(supported_sizes[i].x, supported_sizes[i].y);
958
959             ERR("Unsupported size %d\n", val);
960             twCC = TWCC_BADCAP;
961             break;
962
963         case MSG_GETDEFAULT:
964             twCC = set_onevalue(pCapability, TWTY_UINT16, default_size);
965             break;
966
967         case MSG_RESET:
968             twCC = TWCC_BADCAP;
969             for (i = 1; i < SUPPORTED_SIZE_COUNT; i++)
970                 if (supported_sizes[i].size == default_size)
971                 {
972                     twCC = set_width_height(supported_sizes[i].x, supported_sizes[i].y);
973                     break;
974                 }
975             if (twCC != TWCC_SUCCESS)
976                 return twCC;
977
978             /* .. fall through intentional .. */
979
980         case MSG_GETCURRENT:
981             twCC = set_onevalue(pCapability, TWTY_UINT16, current_size);
982             break;
983     }
984
985 #undef SUPPORTED_SIZE_COUNT
986 #endif
987     return twCC;
988 }
989
990 /* CAP_AUTOFEED */
991 static TW_UINT16 SANE_CAPAutofeed (pTW_CAPABILITY pCapability, TW_UINT16 action)
992 {
993     TW_UINT16 twCC = TWCC_BADCAP;
994 #ifdef SONAME_LIBSANE
995     TW_UINT32 val;
996     SANE_Bool autofeed;
997     SANE_Status status;
998
999     TRACE("CAP_AUTOFEED\n");
1000
1001     if (sane_option_get_bool(activeDS.deviceHandle, "batch-scan", &autofeed, NULL) != SANE_STATUS_GOOD)
1002         return TWCC_BADCAP;
1003
1004     switch (action)
1005     {
1006         case MSG_QUERYSUPPORT:
1007             twCC = set_onevalue(pCapability, TWTY_INT32,
1008                     TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
1009             break;
1010
1011         case MSG_GET:
1012             twCC = set_onevalue(pCapability, TWTY_BOOL, autofeed);
1013             break;
1014
1015         case MSG_SET:
1016             twCC = msg_set(pCapability, &val);
1017             if (twCC == TWCC_SUCCESS)
1018             {
1019                 if (val)
1020                     autofeed = SANE_TRUE;
1021                 else
1022                     autofeed = SANE_FALSE;
1023
1024                 status = sane_option_set_bool(activeDS.deviceHandle, "batch-scan", autofeed, NULL);
1025                 if (status != SANE_STATUS_GOOD)
1026                 {
1027                     ERR("Error %s: Could not set batch-scan to %d\n", psane_strstatus(status), autofeed);
1028                     return sane_status_to_twcc(status);
1029                 }
1030             }
1031             break;
1032
1033         case MSG_GETDEFAULT:
1034             twCC = set_onevalue(pCapability, TWTY_BOOL, SANE_TRUE);
1035             break;
1036
1037         case MSG_RESET:
1038             autofeed = SANE_TRUE;
1039             status = sane_option_set_bool(activeDS.deviceHandle, "batch-scan", autofeed, NULL);
1040             if (status != SANE_STATUS_GOOD)
1041             {
1042                 ERR("Error %s: Could not reset batch-scan to SANE_TRUE\n", psane_strstatus(status));
1043                 return sane_status_to_twcc(status);
1044             }
1045             /* .. fall through intentional .. */
1046
1047         case MSG_GETCURRENT:
1048             twCC = set_onevalue(pCapability, TWTY_BOOL, autofeed);
1049             break;
1050     }
1051 #endif
1052     return twCC;
1053 }
1054
1055 /* CAP_FEEDERENABLED */
1056 static TW_UINT16 SANE_CAPFeederEnabled (pTW_CAPABILITY pCapability, TW_UINT16 action)
1057 {
1058     TW_UINT16 twCC = TWCC_BADCAP;
1059 #ifdef SONAME_LIBSANE
1060     TW_UINT32 val;
1061     TW_BOOL enabled;
1062     SANE_Status status;
1063     SANE_Char source[64];
1064
1065     TRACE("CAP_FEEDERENABLED\n");
1066
1067     if (sane_option_get_str(activeDS.deviceHandle, SANE_NAME_SCAN_SOURCE, source, sizeof(source), NULL) != SANE_STATUS_GOOD)
1068         return TWCC_BADCAP;
1069
1070     if (strcmp(source, "Auto") == 0 || strcmp(source, "ADF") == 0)
1071         enabled = TRUE;
1072     else
1073         enabled = FALSE;
1074
1075     switch (action)
1076     {
1077         case MSG_QUERYSUPPORT:
1078             twCC = set_onevalue(pCapability, TWTY_INT32,
1079                     TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
1080             break;
1081
1082         case MSG_GET:
1083             twCC = set_onevalue(pCapability, TWTY_BOOL, enabled);
1084             break;
1085
1086         case MSG_SET:
1087             twCC = msg_set(pCapability, &val);
1088             if (twCC == TWCC_SUCCESS)
1089             {
1090                 if (val)
1091                     enabled = TRUE;
1092                 else
1093                     enabled = FALSE;
1094
1095                 strcpy(source, "ADF");
1096                 status = sane_option_set_str(activeDS.deviceHandle, SANE_NAME_SCAN_SOURCE, source, NULL);
1097                 if (status != SANE_STATUS_GOOD)
1098                 {
1099                     strcpy(source, "Auto");
1100                     status = sane_option_set_str(activeDS.deviceHandle, SANE_NAME_SCAN_SOURCE, source, NULL);
1101                 }
1102                 if (status != SANE_STATUS_GOOD)
1103                 {
1104                     ERR("Error %s: Could not set source to either ADF or Auto\n", psane_strstatus(status));
1105                     return sane_status_to_twcc(status);
1106                 }
1107             }
1108             break;
1109
1110         case MSG_GETDEFAULT:
1111             twCC = set_onevalue(pCapability, TWTY_BOOL, TRUE);
1112             break;
1113
1114         case MSG_RESET:
1115             strcpy(source, "Auto");
1116             if (sane_option_set_str(activeDS.deviceHandle, SANE_NAME_SCAN_SOURCE, source, NULL) == SANE_STATUS_GOOD)
1117                 enabled = TRUE;
1118             twCC = TWCC_SUCCESS;
1119             /* .. fall through intentional .. */
1120
1121         case MSG_GETCURRENT:
1122             twCC = set_onevalue(pCapability, TWTY_BOOL, enabled);
1123             break;
1124     }
1125 #endif
1126     return twCC;
1127 }
1128
1129
1130
1131 TW_UINT16 SANE_SaneCapability (pTW_CAPABILITY pCapability, TW_UINT16 action)
1132 {
1133     TW_UINT16 twCC = TWCC_CAPUNSUPPORTED;
1134
1135     TRACE("capability=%d action=%d\n", pCapability->Cap, action);
1136
1137     switch (pCapability->Cap)
1138     {
1139         case CAP_SUPPORTEDCAPS:
1140             if (action == MSG_GET)
1141                 twCC = TWAIN_GetSupportedCaps(pCapability);
1142             else
1143                 twCC = TWCC_BADVALUE;
1144             break;
1145
1146         case CAP_XFERCOUNT:
1147             twCC = SANE_CAPXferCount (pCapability, action);
1148             break;
1149
1150         case CAP_UICONTROLLABLE:
1151             twCC = SANE_CAPUiControllable (pCapability, action);
1152             break;
1153
1154         case CAP_AUTOFEED:
1155             twCC = SANE_CAPAutofeed (pCapability, action);
1156             break;
1157
1158         case CAP_FEEDERENABLED:
1159             twCC = SANE_CAPFeederEnabled (pCapability, action);
1160             break;
1161
1162         case ICAP_PIXELTYPE:
1163             twCC = SANE_ICAPPixelType (pCapability, action);
1164             break;
1165
1166         case ICAP_UNITS:
1167             twCC = SANE_ICAPUnits (pCapability, action);
1168             break;
1169
1170         case ICAP_BITDEPTH:
1171             twCC = SANE_ICAPBitDepth(pCapability, action);
1172             break;
1173
1174         case ICAP_XFERMECH:
1175             twCC = SANE_ICAPXferMech (pCapability, action);
1176             break;
1177
1178         case ICAP_PIXELFLAVOR:
1179             twCC = SANE_ICAPPixelFlavor (pCapability, action);
1180             break;
1181
1182         case ICAP_COMPRESSION:
1183             twCC = SANE_ICAPCompression(pCapability, action);
1184             break;
1185
1186         case ICAP_XRESOLUTION:
1187             twCC = SANE_ICAPResolution(pCapability, action, pCapability->Cap);
1188             break;
1189
1190         case ICAP_YRESOLUTION:
1191             twCC = SANE_ICAPResolution(pCapability, action, pCapability->Cap);
1192             break;
1193
1194         case ICAP_PHYSICALHEIGHT:
1195             twCC = SANE_ICAPPhysical(pCapability, action, pCapability->Cap);
1196             break;
1197
1198         case ICAP_PHYSICALWIDTH:
1199             twCC = SANE_ICAPPhysical(pCapability, action, pCapability->Cap);
1200             break;
1201
1202         case ICAP_SUPPORTEDSIZES:
1203             twCC = SANE_ICAPSupportedSizes (pCapability, action);
1204             break;
1205
1206         case ICAP_PLANARCHUNKY:
1207             FIXME("ICAP_PLANARCHUNKY not implemented\n");
1208             break;
1209
1210         case ICAP_BITORDER:
1211             FIXME("ICAP_BITORDER not implemented\n");
1212             break;
1213
1214     }
1215
1216     /* Twain specifies that you should return a 0 in response to QUERYSUPPORT,
1217      *   even if you don't formally support the capability */
1218     if (twCC == TWCC_CAPUNSUPPORTED && action == MSG_QUERYSUPPORT)
1219         twCC = set_onevalue(pCapability, 0, TWTY_INT32);
1220
1221     if (twCC == TWCC_CAPUNSUPPORTED)
1222         TRACE("capability 0x%x/action=%d being reported as unsupported\n", pCapability->Cap, action);
1223
1224     return twCC;
1225 }
1226
1227 TW_UINT16 SANE_SaneSetDefaults (void)
1228 {
1229     TW_CAPABILITY cap;
1230
1231     memset(&cap, 0, sizeof(cap));
1232     cap.Cap = CAP_AUTOFEED;
1233     cap.ConType = TWON_DONTCARE16;
1234
1235     if (SANE_SaneCapability(&cap, MSG_RESET) == TWCC_SUCCESS)
1236         GlobalFree(cap.hContainer);
1237
1238     memset(&cap, 0, sizeof(cap));
1239     cap.Cap = CAP_FEEDERENABLED;
1240     cap.ConType = TWON_DONTCARE16;
1241
1242     if (SANE_SaneCapability(&cap, MSG_RESET) == TWCC_SUCCESS)
1243         GlobalFree(cap.hContainer);
1244
1245     memset(&cap, 0, sizeof(cap));
1246     cap.Cap = ICAP_SUPPORTEDSIZES;
1247     cap.ConType = TWON_DONTCARE16;
1248
1249     if (SANE_SaneCapability(&cap, MSG_RESET) == TWCC_SUCCESS)
1250         GlobalFree(cap.hContainer);
1251
1252    return TWCC_SUCCESS;
1253 }