Release 8.16.
[wine.git] / dlls / sane.ds / capability.c
blob349126f6b95ae09862325c5a1d3f47b54c0204e9
1 /*
2 * Copyright 2000 Corel Corporation
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.
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.
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
19 #include <stdarg.h>
20 #include <stdio.h>
21 #include <math.h>
23 #include "sane_i.h"
24 #include "winnls.h"
25 #include "wine/debug.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(twain);
29 static TW_UINT16 get_onevalue(pTW_CAPABILITY pCapability, TW_UINT16 *type, TW_UINT32 *value)
31 if (pCapability->hContainer)
33 pTW_ONEVALUE pVal = GlobalLock (pCapability->hContainer);
34 if (pVal)
36 *value = pVal->Item;
37 if (type)
38 *type = pVal->ItemType;
39 GlobalUnlock (pCapability->hContainer);
40 return TWCC_SUCCESS;
43 return TWCC_BUMMER;
47 static TW_UINT16 set_onevalue(pTW_CAPABILITY pCapability, TW_UINT16 type, TW_UINT32 value)
49 pCapability->hContainer = GlobalAlloc (0, sizeof(TW_ONEVALUE));
51 if (pCapability->hContainer)
53 pTW_ONEVALUE pVal = GlobalLock (pCapability->hContainer);
54 if (pVal)
56 pCapability->ConType = TWON_ONEVALUE;
57 pVal->ItemType = type;
58 pVal->Item = value;
59 GlobalUnlock (pCapability->hContainer);
60 return TWCC_SUCCESS;
63 return TWCC_LOWMEMORY;
66 static TW_UINT16 msg_set(pTW_CAPABILITY pCapability, TW_UINT32 *val)
68 if (pCapability->ConType == TWON_ONEVALUE)
69 return get_onevalue(pCapability, NULL, val);
71 FIXME("Partial Stub: MSG_SET only supports TW_ONEVALUE\n");
72 return TWCC_BADCAP;
76 static TW_UINT16 msg_get_enum(pTW_CAPABILITY pCapability, const TW_UINT32 *values, int value_count,
77 TW_UINT16 type, TW_UINT32 current, TW_UINT32 default_value)
79 TW_ENUMERATION *enumv = NULL;
80 TW_UINT32 *p32;
81 TW_UINT16 *p16;
82 int i;
84 pCapability->ConType = TWON_ENUMERATION;
85 pCapability->hContainer = 0;
87 if (type == TWTY_INT16 || type == TWTY_UINT16)
88 pCapability->hContainer = GlobalAlloc (0, FIELD_OFFSET( TW_ENUMERATION, ItemList[value_count * sizeof(TW_UINT16)]));
90 if (type == TWTY_INT32 || type == TWTY_UINT32)
91 pCapability->hContainer = GlobalAlloc (0, FIELD_OFFSET( TW_ENUMERATION, ItemList[value_count * sizeof(TW_UINT32)]));
93 if (pCapability->hContainer)
94 enumv = GlobalLock(pCapability->hContainer);
96 if (! enumv)
97 return TWCC_LOWMEMORY;
99 enumv->ItemType = type;
100 enumv->NumItems = value_count;
102 p16 = (TW_UINT16 *) enumv->ItemList;
103 p32 = (TW_UINT32 *) enumv->ItemList;
104 for (i = 0; i < value_count; i++)
106 if (values[i] == current)
107 enumv->CurrentIndex = i;
108 if (values[i] == default_value)
109 enumv->DefaultIndex = i;
110 if (type == TWTY_INT16 || type == TWTY_UINT16)
111 p16[i] = values[i];
112 if (type == TWTY_INT32 || type == TWTY_UINT32)
113 p32[i] = values[i];
116 GlobalUnlock(pCapability->hContainer);
117 return TWCC_SUCCESS;
120 static TW_UINT16 msg_get_range(pTW_CAPABILITY pCapability, TW_UINT16 type,
121 TW_UINT32 minval, TW_UINT32 maxval, TW_UINT32 step, TW_UINT32 def, TW_UINT32 current)
123 TW_RANGE *range = NULL;
125 pCapability->ConType = TWON_RANGE;
126 pCapability->hContainer = 0;
128 pCapability->hContainer = GlobalAlloc (0, sizeof(*range));
129 if (pCapability->hContainer)
130 range = GlobalLock(pCapability->hContainer);
132 if (! range)
133 return TWCC_LOWMEMORY;
135 range->ItemType = type;
136 range->MinValue = minval;
137 range->MaxValue = maxval;
138 range->StepSize = step;
139 range->DefaultValue = def;
140 range->CurrentValue = current;
142 GlobalUnlock(pCapability->hContainer);
143 return TWCC_SUCCESS;
146 static TW_UINT16 msg_get_array(pTW_CAPABILITY pCapability, TW_UINT16 type, const int *values, int value_count)
148 TW_ARRAY *a;
149 TW_UINT32 *p32;
150 TW_UINT16 *p16;
152 pCapability->hContainer = 0;
153 pCapability->ConType = TWON_ARRAY;
155 if (type == TWTY_INT16 || type == TWTY_UINT16)
156 pCapability->hContainer = GlobalAlloc (0, FIELD_OFFSET( TW_ARRAY, ItemList[value_count * sizeof(TW_UINT16)]));
157 else if (type == TWTY_INT32 || type == TWTY_UINT32)
158 pCapability->hContainer = GlobalAlloc (0, FIELD_OFFSET( TW_ARRAY, ItemList[value_count * sizeof(TW_UINT32)]));
160 if (pCapability->hContainer)
162 TW_UINT32 i;
163 a = GlobalLock (pCapability->hContainer);
164 a->ItemType = type;
165 a->NumItems = value_count;
166 p16 = (TW_UINT16 *) a->ItemList;
167 p32 = (TW_UINT32 *) a->ItemList;
168 for (i = 0; i < a->NumItems; i++)
170 if (type == TWTY_INT16 || type == TWTY_UINT16)
171 p16[i] = values[i];
172 else if (type == TWTY_INT32 || type == TWTY_UINT32)
173 p32[i] = values[i];
175 GlobalUnlock (pCapability->hContainer);
176 return TWCC_SUCCESS;
178 else
179 return TWCC_LOWMEMORY;
182 static TW_UINT16 TWAIN_GetSupportedCaps(pTW_CAPABILITY pCapability)
184 static const int supported_caps[] = { CAP_SUPPORTEDCAPS, CAP_XFERCOUNT, CAP_UICONTROLLABLE,
185 CAP_AUTOFEED, CAP_FEEDERENABLED,
186 ICAP_XFERMECH, ICAP_PIXELTYPE, ICAP_UNITS, ICAP_BITDEPTH, ICAP_COMPRESSION, ICAP_PIXELFLAVOR,
187 ICAP_XRESOLUTION, ICAP_YRESOLUTION, ICAP_PHYSICALHEIGHT, ICAP_PHYSICALWIDTH, ICAP_SUPPORTEDSIZES };
189 return msg_get_array(pCapability, TWTY_UINT16, supported_caps, sizeof(supported_caps) / sizeof(supported_caps[0]));
193 /* ICAP_XFERMECH */
194 static TW_UINT16 SANE_ICAPXferMech (pTW_CAPABILITY pCapability, TW_UINT16 action)
196 static const TW_UINT32 possible_values[] = { TWSX_NATIVE, TWSX_MEMORY };
197 TW_UINT32 val;
198 TW_UINT16 twCC = TWCC_BADCAP;
200 TRACE("ICAP_XFERMECH\n");
202 switch (action)
204 case MSG_QUERYSUPPORT:
205 twCC = set_onevalue(pCapability, TWTY_INT32,
206 TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
207 break;
209 case MSG_GET:
210 twCC = msg_get_enum(pCapability, possible_values, ARRAY_SIZE(possible_values),
211 TWTY_UINT16, activeDS.capXferMech, TWSX_NATIVE);
212 break;
214 case MSG_SET:
215 twCC = msg_set(pCapability, &val);
216 if (twCC == TWCC_SUCCESS)
218 activeDS.capXferMech = (TW_UINT16) val;
219 FIXME("Partial Stub: XFERMECH set to %ld, but ignored\n", val);
221 break;
223 case MSG_GETDEFAULT:
224 twCC = set_onevalue(pCapability, TWTY_UINT16, TWSX_NATIVE);
225 break;
227 case MSG_RESET:
228 activeDS.capXferMech = TWSX_NATIVE;
229 /* .. fall through intentional .. */
231 case MSG_GETCURRENT:
232 twCC = set_onevalue(pCapability, TWTY_UINT16, activeDS.capXferMech);
233 FIXME("Partial Stub: XFERMECH of %d not actually used\n", activeDS.capXferMech);
234 break;
236 return twCC;
240 /* CAP_XFERCOUNT */
241 static TW_UINT16 SANE_CAPXferCount (pTW_CAPABILITY pCapability, TW_UINT16 action)
243 TW_UINT32 val;
244 TW_UINT16 twCC = TWCC_BADCAP;
246 TRACE("CAP_XFERCOUNT\n");
248 switch (action)
250 case MSG_QUERYSUPPORT:
251 twCC = set_onevalue(pCapability, TWTY_INT32,
252 TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
253 break;
255 case MSG_GET:
256 twCC = set_onevalue(pCapability, TWTY_INT16, -1);
257 FIXME("Partial Stub: Reporting only support for transfer all\n");
258 break;
260 case MSG_SET:
261 twCC = msg_set(pCapability, &val);
262 if (twCC == TWCC_SUCCESS)
263 FIXME("Partial Stub: XFERCOUNT set to %ld, but ignored\n", val);
264 break;
266 case MSG_GETDEFAULT:
267 twCC = set_onevalue(pCapability, TWTY_INT16, -1);
268 break;
270 case MSG_RESET:
271 /* .. fall through intentional .. */
273 case MSG_GETCURRENT:
274 twCC = set_onevalue(pCapability, TWTY_INT16, -1);
275 break;
277 return twCC;
280 static BOOL pixeltype_to_sane_mode(TW_UINT16 pixeltype, char *mode, int len)
282 const char *m = NULL;
283 switch (pixeltype)
285 case TWPT_GRAY:
286 m = "Gray";
287 break;
288 case TWPT_RGB:
289 m = "Color";
290 break;
291 case TWPT_BW:
292 m = "Lineart";
293 break;
295 if (! m)
296 return FALSE;
297 if (strlen(m) >= len)
298 return FALSE;
299 strcpy(mode, m);
300 return TRUE;
303 /* ICAP_PIXELTYPE */
304 static TW_UINT16 SANE_ICAPPixelType (pTW_CAPABILITY pCapability, TW_UINT16 action)
306 TW_UINT16 twCC;
307 TW_UINT32 possible_values[3];
308 int possible_value_count;
309 TW_UINT32 val;
310 BOOL reload = FALSE;
311 TW_UINT16 current_pixeltype = TWPT_BW;
312 char mode[64];
314 TRACE("ICAP_PIXELTYPE\n");
316 twCC = sane_option_probe_mode(&current_pixeltype, possible_values, &possible_value_count);
317 if (twCC != TWCC_SUCCESS)
319 ERR("Unable to retrieve mode from sane, ICAP_PIXELTYPE unsupported\n");
320 return twCC;
323 /* Sane does not support a concept of a default mode, so we simply cache
324 * the first mode we find */
325 if (! activeDS.PixelTypeSet)
327 activeDS.PixelTypeSet = TRUE;
328 activeDS.defaultPixelType = current_pixeltype;
331 switch (action)
333 case MSG_QUERYSUPPORT:
334 twCC = set_onevalue(pCapability, TWTY_INT32,
335 TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
336 break;
338 case MSG_GET:
339 twCC = msg_get_enum(pCapability, possible_values, possible_value_count,
340 TWTY_UINT16, current_pixeltype, activeDS.defaultPixelType);
341 break;
343 case MSG_SET:
344 twCC = msg_set(pCapability, &val);
345 if (twCC == TWCC_SUCCESS)
347 TRACE("Setting pixeltype to %ld\n", val);
348 if (! pixeltype_to_sane_mode(val, mode, sizeof(mode)))
349 return TWCC_BADVALUE;
351 twCC = sane_option_set_str("mode", mode, &reload);
352 /* Some SANE devices use 'Grayscale' instead of the standard 'Gray' */
353 if (twCC != TWCC_SUCCESS && strcmp(mode, "Gray") == 0)
355 strcpy(mode, "Grayscale");
356 twCC = sane_option_set_str("mode", mode, &reload);
358 if (reload) get_sane_params( &activeDS.frame_params );
360 break;
362 case MSG_GETDEFAULT:
363 twCC = set_onevalue(pCapability, TWTY_UINT16, activeDS.defaultPixelType);
364 break;
366 case MSG_RESET:
367 current_pixeltype = activeDS.defaultPixelType;
368 if (! pixeltype_to_sane_mode(current_pixeltype, mode, sizeof(mode)))
369 return TWCC_BADVALUE;
371 twCC = sane_option_set_str("mode", mode, &reload);
372 /* Some SANE devices use 'Grayscale' instead of the standard 'Gray' */
373 if (twCC != TWCC_SUCCESS && strcmp(mode, "Gray") == 0)
375 strcpy(mode, "Grayscale");
376 twCC = sane_option_set_str("mode", mode, &reload);
378 if (twCC != TWCC_SUCCESS) break;
379 if (reload) get_sane_params( &activeDS.frame_params );
381 /* .. fall through intentional .. */
383 case MSG_GETCURRENT:
384 twCC = set_onevalue(pCapability, TWTY_UINT16, current_pixeltype);
385 TRACE("Returning current pixeltype of %d\n", current_pixeltype);
386 break;
389 return twCC;
392 /* ICAP_UNITS */
393 static TW_UINT16 SANE_ICAPUnits (pTW_CAPABILITY pCapability, TW_UINT16 action)
395 TW_UINT32 val;
396 TW_UINT16 twCC = TWCC_BADCAP;
398 TRACE("ICAP_UNITS\n");
400 switch (action)
402 case MSG_QUERYSUPPORT:
403 twCC = set_onevalue(pCapability, TWTY_INT32,
404 TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
405 break;
407 case MSG_GET:
408 twCC = set_onevalue(pCapability, TWTY_UINT16, TWUN_INCHES);
409 break;
411 case MSG_SET:
412 twCC = msg_set(pCapability, &val);
413 if (twCC == TWCC_SUCCESS)
415 if (val != TWUN_INCHES)
417 ERR("Sane supports only SANE_UNIT_DPI\n");
418 twCC = TWCC_BADVALUE;
421 break;
423 case MSG_GETDEFAULT:
424 case MSG_RESET:
425 /* .. fall through intentional .. */
427 case MSG_GETCURRENT:
428 twCC = set_onevalue(pCapability, TWTY_UINT16, TWUN_INCHES);
429 break;
432 return twCC;
435 /* ICAP_BITDEPTH */
436 static TW_UINT16 SANE_ICAPBitDepth(pTW_CAPABILITY pCapability, TW_UINT16 action)
438 TW_UINT16 twCC = TWCC_BADCAP;
439 TW_UINT32 possible_values[1];
441 TRACE("ICAP_BITDEPTH\n");
443 possible_values[0] = activeDS.frame_params.depth;
445 switch (action)
447 case MSG_QUERYSUPPORT:
448 twCC = set_onevalue(pCapability, TWTY_INT32,
449 TWQC_GET | TWQC_GETDEFAULT | TWQC_GETCURRENT );
450 break;
452 case MSG_GET:
453 twCC = msg_get_enum(pCapability, possible_values, ARRAY_SIZE(possible_values),
454 TWTY_UINT16, activeDS.frame_params.depth, activeDS.frame_params.depth);
455 break;
457 case MSG_GETDEFAULT:
458 /* .. Fall through intentional .. */
460 case MSG_GETCURRENT:
461 TRACE("Returning current bitdepth of %d\n", activeDS.frame_params.depth);
462 twCC = set_onevalue(pCapability, TWTY_UINT16, activeDS.frame_params.depth);
463 break;
465 return twCC;
468 /* CAP_UICONTROLLABLE */
469 static TW_UINT16 SANE_CAPUiControllable(pTW_CAPABILITY pCapability, TW_UINT16 action)
471 TW_UINT16 twCC = TWCC_BADCAP;
473 TRACE("CAP_UICONTROLLABLE\n");
475 switch (action)
477 case MSG_QUERYSUPPORT:
478 twCC = set_onevalue(pCapability, TWTY_INT32, TWQC_GET);
479 break;
481 case MSG_GET:
482 twCC = set_onevalue(pCapability, TWTY_BOOL, TRUE);
483 break;
486 return twCC;
489 /* ICAP_COMPRESSION */
490 static TW_UINT16 SANE_ICAPCompression (pTW_CAPABILITY pCapability, TW_UINT16 action)
492 static const TW_UINT32 possible_values[] = { TWCP_NONE };
493 TW_UINT32 val;
494 TW_UINT16 twCC = TWCC_BADCAP;
496 TRACE("ICAP_COMPRESSION\n");
498 switch (action)
500 case MSG_QUERYSUPPORT:
501 twCC = set_onevalue(pCapability, TWTY_INT32,
502 TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
503 break;
505 case MSG_GET:
506 twCC = msg_get_enum(pCapability, possible_values, ARRAY_SIZE(possible_values),
507 TWTY_UINT16, TWCP_NONE, TWCP_NONE);
508 FIXME("Partial stub: We don't attempt to support compression\n");
509 break;
511 case MSG_SET:
512 twCC = msg_set(pCapability, &val);
513 if (twCC == TWCC_SUCCESS)
514 FIXME("Partial Stub: COMPRESSION set to %ld, but ignored\n", val);
515 break;
517 case MSG_GETDEFAULT:
518 twCC = set_onevalue(pCapability, TWTY_UINT16, TWCP_NONE);
519 break;
521 case MSG_RESET:
522 /* .. fall through intentional .. */
524 case MSG_GETCURRENT:
525 twCC = set_onevalue(pCapability, TWTY_UINT16, TWCP_NONE);
526 break;
528 return twCC;
531 /* ICAP_XRESOLUTION, ICAP_YRESOLUTION */
532 static TW_UINT16 SANE_ICAPResolution (pTW_CAPABILITY pCapability, TW_UINT16 action, TW_UINT16 cap)
534 TW_UINT16 twCC = TWCC_BADCAP;
535 TW_UINT32 val;
536 int current_resolution;
537 TW_FIX32 *default_res;
538 const char *best_option_name;
539 struct option_descriptor opt;
541 TRACE("ICAP_%cRESOLUTION\n", cap == ICAP_XRESOLUTION ? 'X' : 'Y');
543 /* Some scanners support 'x-resolution', most seem to just support 'resolution' */
544 if (cap == ICAP_XRESOLUTION)
546 best_option_name = "x-resolution";
547 default_res = &activeDS.defaultXResolution;
549 else
551 best_option_name = "y-resolution";
552 default_res = &activeDS.defaultYResolution;
554 if (sane_option_get_int(best_option_name, &current_resolution) != TWCC_SUCCESS)
556 best_option_name = "resolution";
557 if (sane_option_get_int(best_option_name, &current_resolution) != TWCC_SUCCESS)
558 return TWCC_BADCAP;
561 /* Sane does not support a concept of 'default' resolution, so we have to
562 * cache the resolution the very first time we load the scanner, and use that
563 * as the default */
564 if (cap == ICAP_XRESOLUTION && ! activeDS.XResolutionSet)
566 default_res->Whole = current_resolution;
567 default_res->Frac = 0;
568 activeDS.XResolutionSet = TRUE;
571 if (cap == ICAP_YRESOLUTION && ! activeDS.YResolutionSet)
573 default_res->Whole = current_resolution;
574 default_res->Frac = 0;
575 activeDS.YResolutionSet = TRUE;
578 switch (action)
580 case MSG_QUERYSUPPORT:
581 twCC = set_onevalue(pCapability, TWTY_INT32,
582 TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
583 break;
585 case MSG_GET:
586 twCC = sane_option_probe_resolution(best_option_name, &opt);
587 if (twCC == TWCC_SUCCESS)
589 if (opt.constraint_type == CONSTRAINT_RANGE)
590 twCC = msg_get_range(pCapability, TWTY_FIX32,
591 opt.constraint.range.min, opt.constraint.range.max,
592 opt.constraint.range.quant == 0 ? 1 : opt.constraint.range.quant,
593 default_res->Whole, current_resolution);
594 else if (opt.constraint_type == CONSTRAINT_WORD_LIST)
595 twCC = msg_get_array(pCapability, TWTY_UINT32, &(opt.constraint.word_list[1]),
596 opt.constraint.word_list[0]);
597 else
598 twCC = TWCC_CAPUNSUPPORTED;
600 break;
602 case MSG_SET:
603 twCC = msg_set(pCapability, &val);
604 if (twCC == TWCC_SUCCESS)
606 TW_FIX32 f32;
607 BOOL reload = FALSE;
608 memcpy(&f32, &val, sizeof(f32));
609 twCC = sane_option_set_int(best_option_name, f32.Whole, &reload);
610 if (reload) twCC = TWCC_CHECKSTATUS;
612 break;
614 case MSG_GETDEFAULT:
615 twCC = set_onevalue(pCapability, TWTY_FIX32, default_res->Whole);
616 break;
618 case MSG_RESET:
619 twCC = sane_option_set_int(best_option_name, default_res->Whole, NULL);
620 if (twCC != TWCC_SUCCESS) return twCC;
622 /* .. fall through intentional .. */
624 case MSG_GETCURRENT:
625 twCC = set_onevalue(pCapability, TWTY_FIX32, current_resolution);
626 break;
628 return twCC;
631 /* ICAP_PHYSICALHEIGHT, ICAP_PHYSICALWIDTH */
632 static TW_UINT16 SANE_ICAPPhysical (pTW_CAPABILITY pCapability, TW_UINT16 action, TW_UINT16 cap)
634 TW_UINT16 twCC;
635 TW_FIX32 res;
636 int tlx, tly, brx, bry;
638 TRACE("ICAP_PHYSICAL%s\n", cap == ICAP_PHYSICALHEIGHT? "HEIGHT" : "WIDTH");
640 twCC = sane_option_get_max_scan_area( &tlx, &tly, &brx, &bry );
641 if (twCC != TWCC_SUCCESS) return twCC;
643 res = convert_sane_res_to_twain( (cap == ICAP_PHYSICALHEIGHT) ? bry - tly : brx - tlx );
645 switch (action)
647 case MSG_QUERYSUPPORT:
648 twCC = set_onevalue(pCapability, TWTY_INT32,
649 TWQC_GET | TWQC_GETDEFAULT | TWQC_GETCURRENT );
650 break;
652 case MSG_GET:
653 case MSG_GETDEFAULT:
655 /* .. fall through intentional .. */
657 case MSG_GETCURRENT:
658 twCC = set_onevalue(pCapability, TWTY_FIX32, res.Whole | (res.Frac << 16));
659 break;
661 return twCC;
664 /* ICAP_PIXELFLAVOR */
665 static TW_UINT16 SANE_ICAPPixelFlavor (pTW_CAPABILITY pCapability, TW_UINT16 action)
667 TW_UINT16 twCC = TWCC_BADCAP;
668 static const TW_UINT32 possible_values[] = { TWPF_CHOCOLATE, TWPF_VANILLA };
669 TW_UINT32 val;
670 TW_UINT32 flavor = activeDS.frame_params.depth == 1 ? TWPF_VANILLA : TWPF_CHOCOLATE;
672 TRACE("ICAP_PIXELFLAVOR\n");
674 switch (action)
676 case MSG_QUERYSUPPORT:
677 twCC = set_onevalue(pCapability, TWTY_INT32,
678 TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
679 break;
681 case MSG_GET:
682 twCC = msg_get_enum(pCapability, possible_values, ARRAY_SIZE(possible_values),
683 TWTY_UINT16, flavor, flavor);
684 break;
686 case MSG_SET:
687 twCC = msg_set(pCapability, &val);
688 if (twCC == TWCC_SUCCESS)
690 FIXME("Stub: PIXELFLAVOR set to %ld, but ignored\n", val);
692 break;
694 case MSG_GETDEFAULT:
695 twCC = set_onevalue(pCapability, TWTY_UINT16, flavor);
696 break;
698 case MSG_RESET:
699 /* .. fall through intentional .. */
701 case MSG_GETCURRENT:
702 twCC = set_onevalue(pCapability, TWTY_UINT16, flavor);
703 break;
705 return twCC;
708 static TW_UINT16 get_width_height(double *width, double *height, BOOL max)
710 TW_UINT16 rc;
711 int tlx, tly, brx, bry;
713 if (max) rc = sane_option_get_max_scan_area( &tlx, &tly, &brx, &bry );
714 else rc = sane_option_get_scan_area( &tlx, &tly, &brx, &bry );
716 if (rc == TWCC_SUCCESS)
718 *width = (brx - tlx) / 65536.0;
719 *height = (bry - tly) / 65536.0;
721 return rc;
724 static TW_UINT16 set_width_height(double width, double height)
726 return sane_option_set_scan_area( 0, 0, width * 65536, height * 65536, NULL );
729 typedef struct
731 TW_UINT32 size;
732 double x;
733 double y;
734 } supported_size_t;
736 static const supported_size_t supported_sizes[] =
738 { TWSS_NONE, 0, 0 },
739 { TWSS_A4, 210, 297 },
740 { TWSS_JISB5, 182, 257 },
741 { TWSS_USLETTER, 215.9, 279.4 },
742 { TWSS_USLEGAL, 215.9, 355.6 },
743 { TWSS_A5, 148, 210 },
744 { TWSS_B4, 250, 353 },
745 { TWSS_B6, 125, 176 },
746 { TWSS_USLEDGER, 215.9, 431.8 },
747 { TWSS_USEXECUTIVE, 184.15, 266.7 },
748 { TWSS_A3, 297, 420 },
751 static TW_UINT16 get_default_paper_size(const supported_size_t *s, int n)
753 DWORD paper;
754 int rc;
755 int defsize = -1;
756 double width, height;
757 int i;
758 rc = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_IPAPERSIZE | LOCALE_RETURN_NUMBER, (void *) &paper, sizeof(paper));
759 if (rc > 0)
760 switch (paper)
762 case 1:
763 defsize = TWSS_USLETTER;
764 break;
765 case 5:
766 defsize = TWSS_USLEGAL;
767 break;
768 case 8:
769 defsize = TWSS_A3;
770 break;
771 case 9:
772 defsize = TWSS_A4;
773 break;
776 if (defsize == -1)
777 return TWSS_NONE;
779 if (get_width_height(&width, &height, TRUE) != TWCC_SUCCESS)
780 return TWSS_NONE;
782 for (i = 0; i < n; i++)
783 if (s[i].size == defsize)
785 /* Sane's use of integers to store floats is a hair lossy; deal with it */
786 if (s[i].x > (width + .01) || s[i].y > (height + 0.01))
787 return TWSS_NONE;
788 else
789 return s[i].size;
792 return TWSS_NONE;
795 static TW_UINT16 get_current_paper_size(const supported_size_t *s, int n)
797 int i;
798 double width, height;
799 double xdelta, ydelta;
801 if (get_width_height(&width, &height, FALSE) != TWCC_SUCCESS)
802 return TWSS_NONE;
804 for (i = 0; i < n; i++)
806 /* Sane's use of integers to store floats results
807 * in a very small error; cope with that */
808 xdelta = s[i].x - width;
809 ydelta = s[i].y - height;
810 if (xdelta < 0.01 && xdelta > -0.01 &&
811 ydelta < 0.01 && ydelta > -0.01)
812 return s[i].size;
815 return TWSS_NONE;
818 /* ICAP_SUPPORTEDSIZES */
819 static TW_UINT16 SANE_ICAPSupportedSizes (pTW_CAPABILITY pCapability, TW_UINT16 action)
821 TW_UINT16 twCC = TWCC_BADCAP;
823 static TW_UINT32 possible_values[ARRAY_SIZE(supported_sizes)];
824 unsigned int i;
825 TW_UINT32 val;
826 double max_width, max_height;
827 TW_UINT16 default_size = get_default_paper_size(supported_sizes, ARRAY_SIZE(supported_sizes));
828 TW_UINT16 current_size = get_current_paper_size(supported_sizes, ARRAY_SIZE(supported_sizes));
830 TRACE("ICAP_SUPPORTEDSIZES\n");
832 switch (action)
834 case MSG_QUERYSUPPORT:
835 twCC = set_onevalue(pCapability, TWTY_INT32,
836 TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
837 break;
839 case MSG_GET:
840 for (i = 0; i < ARRAY_SIZE(supported_sizes); i++)
841 possible_values[i] = supported_sizes[i].size;
842 twCC = msg_get_enum(pCapability, possible_values, ARRAY_SIZE(possible_values),
843 TWTY_UINT16, current_size, default_size);
844 WARN("Partial Stub: our supported size selection is a bit thin.\n");
845 break;
847 case MSG_SET:
848 twCC = msg_set(pCapability, &val);
849 if (twCC == TWCC_SUCCESS)
850 for (i = 1; i < ARRAY_SIZE(supported_sizes); i++)
851 if (supported_sizes[i].size == val)
852 return set_width_height(supported_sizes[i].x, supported_sizes[i].y);
853 /* TWAIN: For devices that support physical dimensions TWSS_NONE indicates that the maximum
854 image size supported by the device is to be used. */
855 if (val == TWSS_NONE && get_width_height(&max_width, &max_height, TRUE) == TWCC_SUCCESS)
856 return set_width_height(max_width, max_height);
857 ERR("Unsupported size %ld\n", val);
858 twCC = TWCC_BADCAP;
859 break;
861 case MSG_GETDEFAULT:
862 twCC = set_onevalue(pCapability, TWTY_UINT16, default_size);
863 break;
865 case MSG_RESET:
866 twCC = TWCC_BADCAP;
867 for (i = 1; i < ARRAY_SIZE(supported_sizes); i++)
868 if (supported_sizes[i].size == default_size)
870 twCC = set_width_height(supported_sizes[i].x, supported_sizes[i].y);
871 break;
873 if (twCC != TWCC_SUCCESS)
874 return twCC;
876 /* .. fall through intentional .. */
878 case MSG_GETCURRENT:
879 twCC = set_onevalue(pCapability, TWTY_UINT16, current_size);
880 break;
883 return twCC;
886 /* CAP_AUTOFEED */
887 static TW_UINT16 SANE_CAPAutofeed (pTW_CAPABILITY pCapability, TW_UINT16 action)
889 TW_UINT16 twCC = TWCC_BADCAP;
890 TW_UINT32 val;
891 BOOL autofeed;
893 TRACE("CAP_AUTOFEED\n");
895 if (sane_option_get_bool("batch-scan", &autofeed) != TWCC_SUCCESS)
896 return TWCC_BADCAP;
898 switch (action)
900 case MSG_QUERYSUPPORT:
901 twCC = set_onevalue(pCapability, TWTY_INT32,
902 TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
903 break;
905 case MSG_GET:
906 twCC = set_onevalue(pCapability, TWTY_BOOL, autofeed);
907 break;
909 case MSG_SET:
910 twCC = msg_set(pCapability, &val);
911 if (twCC == TWCC_SUCCESS)
912 twCC = sane_option_set_bool("batch-scan", !!val);
913 break;
915 case MSG_GETDEFAULT:
916 twCC = set_onevalue(pCapability, TWTY_BOOL, TRUE);
917 break;
919 case MSG_RESET:
920 autofeed = TRUE;
921 twCC = sane_option_set_bool("batch-scan", autofeed);
922 if (twCC != TWCC_SUCCESS) break;
923 /* .. fall through intentional .. */
925 case MSG_GETCURRENT:
926 twCC = set_onevalue(pCapability, TWTY_BOOL, autofeed);
927 break;
929 return twCC;
932 /* CAP_FEEDERENABLED */
933 static TW_UINT16 SANE_CAPFeederEnabled (pTW_CAPABILITY pCapability, TW_UINT16 action)
935 TW_UINT16 twCC = TWCC_BADCAP;
936 TW_UINT32 val;
937 TW_BOOL enabled;
938 char source[64];
940 TRACE("CAP_FEEDERENABLED\n");
942 if (sane_option_get_str("source", source, sizeof(source)) != TWCC_SUCCESS)
943 return TWCC_BADCAP;
945 if (strcmp(source, "Auto") == 0 || strcmp(source, "ADF") == 0)
946 enabled = TRUE;
947 else
948 enabled = FALSE;
950 switch (action)
952 case MSG_QUERYSUPPORT:
953 twCC = set_onevalue(pCapability, TWTY_INT32,
954 TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET );
955 break;
957 case MSG_GET:
958 twCC = set_onevalue(pCapability, TWTY_BOOL, enabled);
959 break;
961 case MSG_SET:
962 twCC = msg_set(pCapability, &val);
963 if (twCC == TWCC_SUCCESS)
965 strcpy(source, "ADF");
966 twCC = sane_option_set_str("source", source, NULL);
967 if (twCC != TWCC_SUCCESS)
969 strcpy(source, "Auto");
970 twCC = sane_option_set_str("source", source, NULL);
973 break;
975 case MSG_GETDEFAULT:
976 twCC = set_onevalue(pCapability, TWTY_BOOL, TRUE);
977 break;
979 case MSG_RESET:
980 strcpy(source, "Auto");
981 if (sane_option_set_str("source", source, NULL) == TWCC_SUCCESS)
982 enabled = TRUE;
983 /* .. fall through intentional .. */
985 case MSG_GETCURRENT:
986 twCC = set_onevalue(pCapability, TWTY_BOOL, enabled);
987 break;
989 return twCC;
994 TW_UINT16 SANE_SaneCapability (pTW_CAPABILITY pCapability, TW_UINT16 action)
996 TW_UINT16 twCC = TWCC_CAPUNSUPPORTED;
998 TRACE("capability=%d action=%d\n", pCapability->Cap, action);
1000 switch (pCapability->Cap)
1002 case CAP_SUPPORTEDCAPS:
1003 if (action == MSG_GET)
1004 twCC = TWAIN_GetSupportedCaps(pCapability);
1005 else
1006 twCC = TWCC_BADVALUE;
1007 break;
1009 case CAP_XFERCOUNT:
1010 twCC = SANE_CAPXferCount (pCapability, action);
1011 break;
1013 case CAP_UICONTROLLABLE:
1014 twCC = SANE_CAPUiControllable (pCapability, action);
1015 break;
1017 case CAP_AUTOFEED:
1018 twCC = SANE_CAPAutofeed (pCapability, action);
1019 break;
1021 case CAP_FEEDERENABLED:
1022 twCC = SANE_CAPFeederEnabled (pCapability, action);
1023 break;
1025 case ICAP_PIXELTYPE:
1026 twCC = SANE_ICAPPixelType (pCapability, action);
1027 break;
1029 case ICAP_UNITS:
1030 twCC = SANE_ICAPUnits (pCapability, action);
1031 break;
1033 case ICAP_BITDEPTH:
1034 twCC = SANE_ICAPBitDepth(pCapability, action);
1035 break;
1037 case ICAP_XFERMECH:
1038 twCC = SANE_ICAPXferMech (pCapability, action);
1039 break;
1041 case ICAP_PIXELFLAVOR:
1042 twCC = SANE_ICAPPixelFlavor (pCapability, action);
1043 break;
1045 case ICAP_COMPRESSION:
1046 twCC = SANE_ICAPCompression(pCapability, action);
1047 break;
1049 case ICAP_XRESOLUTION:
1050 twCC = SANE_ICAPResolution(pCapability, action, pCapability->Cap);
1051 break;
1053 case ICAP_YRESOLUTION:
1054 twCC = SANE_ICAPResolution(pCapability, action, pCapability->Cap);
1055 break;
1057 case ICAP_PHYSICALHEIGHT:
1058 twCC = SANE_ICAPPhysical(pCapability, action, pCapability->Cap);
1059 break;
1061 case ICAP_PHYSICALWIDTH:
1062 twCC = SANE_ICAPPhysical(pCapability, action, pCapability->Cap);
1063 break;
1065 case ICAP_SUPPORTEDSIZES:
1066 twCC = SANE_ICAPSupportedSizes (pCapability, action);
1067 break;
1069 case ICAP_PLANARCHUNKY:
1070 FIXME("ICAP_PLANARCHUNKY not implemented\n");
1071 break;
1073 case ICAP_BITORDER:
1074 FIXME("ICAP_BITORDER not implemented\n");
1075 break;
1079 /* Twain specifies that you should return a 0 in response to QUERYSUPPORT,
1080 * even if you don't formally support the capability */
1081 if (twCC == TWCC_CAPUNSUPPORTED && action == MSG_QUERYSUPPORT)
1082 twCC = set_onevalue(pCapability, 0, TWTY_INT32);
1084 if (twCC == TWCC_CAPUNSUPPORTED)
1085 TRACE("capability 0x%x/action=%d being reported as unsupported\n", pCapability->Cap, action);
1087 return twCC;
1090 TW_UINT16 SANE_SaneSetDefaults (void)
1092 TW_CAPABILITY cap;
1094 memset(&cap, 0, sizeof(cap));
1095 cap.Cap = CAP_AUTOFEED;
1096 cap.ConType = TWON_DONTCARE16;
1098 if (SANE_SaneCapability(&cap, MSG_RESET) == TWCC_SUCCESS)
1099 GlobalFree(cap.hContainer);
1101 memset(&cap, 0, sizeof(cap));
1102 cap.Cap = CAP_FEEDERENABLED;
1103 cap.ConType = TWON_DONTCARE16;
1105 if (SANE_SaneCapability(&cap, MSG_RESET) == TWCC_SUCCESS)
1106 GlobalFree(cap.hContainer);
1108 memset(&cap, 0, sizeof(cap));
1109 cap.Cap = ICAP_SUPPORTEDSIZES;
1110 cap.ConType = TWON_DONTCARE16;
1112 if (SANE_SaneCapability(&cap, MSG_RESET) == TWCC_SUCCESS)
1113 GlobalFree(cap.hContainer);
1115 return TWCC_SUCCESS;