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 #define NONAMELESSUNION
20 #define NONAMELESSSTRUCT
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(twain
);
37 #ifndef SANE_VALUE_SCAN_MODE_COLOR
38 #define SANE_VALUE_SCAN_MODE_COLOR SANE_I18N("Color")
40 #ifndef SANE_VALUE_SCAN_MODE_GRAY
41 #define SANE_VALUE_SCAN_MODE_GRAY SANE_I18N("Gray")
43 #ifndef SANE_VALUE_SCAN_MODE_LINEART
44 #define SANE_VALUE_SCAN_MODE_LINEART SANE_I18N("Lineart")
47 static TW_UINT16
get_onevalue(pTW_CAPABILITY pCapability
, TW_UINT16
*type
, TW_UINT32
*value
)
49 if (pCapability
->hContainer
)
51 pTW_ONEVALUE pVal
= GlobalLock (pCapability
->hContainer
);
56 *type
= pVal
->ItemType
;
57 GlobalUnlock (pCapability
->hContainer
);
65 static TW_UINT16
set_onevalue(pTW_CAPABILITY pCapability
, TW_UINT16 type
, TW_UINT32 value
)
67 pCapability
->hContainer
= GlobalAlloc (0, sizeof(TW_ONEVALUE
));
69 if (pCapability
->hContainer
)
71 pTW_ONEVALUE pVal
= GlobalLock (pCapability
->hContainer
);
74 pCapability
->ConType
= TWON_ONEVALUE
;
75 pVal
->ItemType
= type
;
77 GlobalUnlock (pCapability
->hContainer
);
81 return TWCC_LOWMEMORY
;
84 static TW_UINT16
msg_set(pTW_CAPABILITY pCapability
, TW_UINT32
*val
)
86 if (pCapability
->ConType
== TWON_ONEVALUE
)
87 return get_onevalue(pCapability
, NULL
, val
);
89 FIXME("Partial Stub: MSG_SET only supports TW_ONEVALUE\n");
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
)
97 TW_ENUMERATION
*enumv
= NULL
;
102 pCapability
->ConType
= TWON_ENUMERATION
;
103 pCapability
->hContainer
= 0;
105 if (type
== TWTY_INT16
|| type
== TWTY_UINT16
)
106 pCapability
->hContainer
= GlobalAlloc (0, FIELD_OFFSET( TW_ENUMERATION
, ItemList
[value_count
* sizeof(TW_UINT16
)]));
108 if (type
== TWTY_INT32
|| type
== TWTY_UINT32
)
109 pCapability
->hContainer
= GlobalAlloc (0, FIELD_OFFSET( TW_ENUMERATION
, ItemList
[value_count
* sizeof(TW_UINT32
)]));
111 if (pCapability
->hContainer
)
112 enumv
= GlobalLock(pCapability
->hContainer
);
115 return TWCC_LOWMEMORY
;
117 enumv
->ItemType
= type
;
118 enumv
->NumItems
= value_count
;
120 p16
= (TW_UINT16
*) enumv
->ItemList
;
121 p32
= (TW_UINT32
*) enumv
->ItemList
;
122 for (i
= 0; i
< value_count
; i
++)
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
)
130 if (type
== TWTY_INT32
|| type
== TWTY_UINT32
)
134 GlobalUnlock(pCapability
->hContainer
);
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
)
142 TW_RANGE
*range
= NULL
;
144 pCapability
->ConType
= TWON_RANGE
;
145 pCapability
->hContainer
= 0;
147 pCapability
->hContainer
= GlobalAlloc (0, sizeof(*range
));
148 if (pCapability
->hContainer
)
149 range
= GlobalLock(pCapability
->hContainer
);
152 return TWCC_LOWMEMORY
;
154 range
->ItemType
= type
;
155 range
->MinValue
= minval
;
156 range
->MaxValue
= maxval
;
157 range
->StepSize
= step
;
158 range
->DefaultValue
= def
;
159 range
->CurrentValue
= current
;
161 GlobalUnlock(pCapability
->hContainer
);
166 static TW_UINT16
TWAIN_GetSupportedCaps(pTW_CAPABILITY pCapability
)
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
};
174 pCapability
->hContainer
= GlobalAlloc (0, FIELD_OFFSET( TW_ARRAY
, ItemList
[sizeof(supported_caps
)] ));
175 pCapability
->ConType
= TWON_ARRAY
;
177 if (pCapability
->hContainer
)
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
);
191 return TWCC_LOWMEMORY
;
196 static TW_UINT16
SANE_ICAPXferMech (pTW_CAPABILITY pCapability
, TW_UINT16 action
)
198 static const TW_UINT32 possible_values
[] = { TWSX_NATIVE
, TWSX_MEMORY
};
200 TW_UINT16 twCC
= TWCC_BADCAP
;
202 TRACE("ICAP_XFERMECH\n");
206 case MSG_QUERYSUPPORT
:
207 twCC
= set_onevalue(pCapability
, TWTY_INT32
,
208 TWQC_GET
| TWQC_SET
| TWQC_GETDEFAULT
| TWQC_GETCURRENT
| TWQC_RESET
);
212 twCC
= msg_get_enum(pCapability
, possible_values
, sizeof(possible_values
) / sizeof(possible_values
[0]),
213 TWTY_UINT16
, activeDS
.capXferMech
, TWSX_NATIVE
);
217 twCC
= msg_set(pCapability
, &val
);
218 if (twCC
== TWCC_SUCCESS
)
220 activeDS
.capXferMech
= (TW_UINT16
) val
;
221 FIXME("Partial Stub: XFERMECH set to %d, but ignored\n", val
);
226 twCC
= set_onevalue(pCapability
, TWTY_UINT16
, TWSX_NATIVE
);
230 activeDS
.capXferMech
= TWSX_NATIVE
;
231 /* .. fall through intentional .. */
234 twCC
= set_onevalue(pCapability
, TWTY_UINT16
, activeDS
.capXferMech
);
235 FIXME("Partial Stub: XFERMECH of %d not actually used\n", activeDS
.capXferMech
);
243 static TW_UINT16
SANE_CAPXferCount (pTW_CAPABILITY pCapability
, TW_UINT16 action
)
246 TW_UINT16 twCC
= TWCC_BADCAP
;
248 TRACE("CAP_XFERCOUNT\n");
252 case MSG_QUERYSUPPORT
:
253 twCC
= set_onevalue(pCapability
, TWTY_INT32
,
254 TWQC_GET
| TWQC_SET
| TWQC_GETDEFAULT
| TWQC_GETCURRENT
| TWQC_RESET
);
258 twCC
= set_onevalue(pCapability
, TWTY_INT16
, -1);
259 FIXME("Partial Stub: Reporting only support for transfer all\n");
263 twCC
= msg_set(pCapability
, &val
);
264 if (twCC
== TWCC_SUCCESS
)
265 FIXME("Partial Stub: XFERCOUNT set to %d, but ignored\n", val
);
269 twCC
= set_onevalue(pCapability
, TWTY_INT16
, -1);
273 /* .. fall through intentional .. */
276 twCC
= set_onevalue(pCapability
, TWTY_INT16
, -1);
282 #ifdef SONAME_LIBSANE
283 static BOOL
pixeltype_to_sane_mode(TW_UINT16 pixeltype
, SANE_String mode
, int len
)
285 SANE_String_Const m
= NULL
;
289 m
= SANE_VALUE_SCAN_MODE_GRAY
;
292 m
= SANE_VALUE_SCAN_MODE_COLOR
;
295 m
= SANE_VALUE_SCAN_MODE_LINEART
;
300 if (strlen(m
) >= len
)
305 static BOOL
sane_mode_to_pixeltype(SANE_String_Const mode
, TW_UINT16
*pixeltype
)
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
;
321 static TW_UINT16
SANE_ICAPPixelType (pTW_CAPABILITY pCapability
, TW_UINT16 action
)
323 TW_UINT16 twCC
= TWCC_BADCAP
;
324 #ifdef SONAME_LIBSANE
325 TW_UINT32 possible_values
[3];
326 int possible_value_count
;
330 SANE_String_Const
*choices
;
331 char current_mode
[64];
332 TW_UINT16 current_pixeltype
= TWPT_BW
;
335 TRACE("ICAP_PIXELTYPE\n");
337 rc
= sane_option_probe_mode(activeDS
.deviceHandle
, &choices
, current_mode
, sizeof(current_mode
));
338 if (rc
!= SANE_STATUS_GOOD
)
340 ERR("Unable to retrieve mode from sane, ICAP_PIXELTYPE unsupported\n");
344 sane_mode_to_pixeltype(current_mode
, ¤t_pixeltype
);
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
)
350 activeDS
.PixelTypeSet
= TRUE
;
351 activeDS
.defaultPixelType
= current_pixeltype
;
356 case MSG_QUERYSUPPORT
:
357 twCC
= set_onevalue(pCapability
, TWTY_INT32
,
358 TWQC_GET
| TWQC_SET
| TWQC_GETDEFAULT
| TWQC_GETCURRENT
| TWQC_RESET
);
362 for (possible_value_count
= 0; choices
&& *choices
&& possible_value_count
< 3; choices
++)
365 if (sane_mode_to_pixeltype(*choices
, &pix
))
366 possible_values
[possible_value_count
++] = pix
;
368 twCC
= msg_get_enum(pCapability
, possible_values
, possible_value_count
,
369 TWTY_UINT16
, current_pixeltype
, activeDS
.defaultPixelType
);
373 twCC
= msg_set(pCapability
, &val
);
374 if (twCC
== TWCC_SUCCESS
)
376 TRACE("Setting pixeltype to %d\n", val
);
377 if (! pixeltype_to_sane_mode(val
, mode
, sizeof(mode
)))
378 return TWCC_BADVALUE
;
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)
385 strcpy(mode
, "Grayscale");
386 rc
= sane_option_set_str(activeDS
.deviceHandle
, "mode", mode
, &status
);
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
);
396 twCC
= set_onevalue(pCapability
, TWTY_UINT16
, activeDS
.defaultPixelType
);
400 current_pixeltype
= activeDS
.defaultPixelType
;
401 if (! pixeltype_to_sane_mode(current_pixeltype
, mode
, sizeof(mode
)))
402 return TWCC_BADVALUE
;
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)
409 strcpy(mode
, "Grayscale");
410 rc
= sane_option_set_str(activeDS
.deviceHandle
, "mode", mode
, &status
);
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
);
417 /* .. fall through intentional .. */
420 twCC
= set_onevalue(pCapability
, TWTY_UINT16
, current_pixeltype
);
421 TRACE("Returning current pixeltype of %d\n", current_pixeltype
);
430 static TW_UINT16
SANE_ICAPUnits (pTW_CAPABILITY pCapability
, TW_UINT16 action
)
433 TW_UINT16 twCC
= TWCC_BADCAP
;
435 TRACE("ICAP_UNITS\n");
439 case MSG_QUERYSUPPORT
:
440 twCC
= set_onevalue(pCapability
, TWTY_INT32
,
441 TWQC_GET
| TWQC_SET
| TWQC_GETDEFAULT
| TWQC_GETCURRENT
| TWQC_RESET
);
445 twCC
= set_onevalue(pCapability
, TWTY_UINT16
, TWUN_INCHES
);
449 twCC
= msg_set(pCapability
, &val
);
450 if (twCC
== TWCC_SUCCESS
)
452 if (val
!= TWUN_INCHES
)
454 ERR("Sane supports only SANE_UNIT_DPI\n");
455 twCC
= TWCC_BADVALUE
;
462 /* .. fall through intentional .. */
465 twCC
= set_onevalue(pCapability
, TWTY_UINT16
, TWUN_INCHES
);
473 static TW_UINT16
SANE_ICAPBitDepth(pTW_CAPABILITY pCapability
, TW_UINT16 action
)
475 TW_UINT16 twCC
= TWCC_BADCAP
;
476 #ifdef SONAME_LIBSANE
477 TW_UINT32 possible_values
[1];
479 TRACE("ICAP_BITDEPTH\n");
481 possible_values
[0] = activeDS
.sane_param
.depth
;
485 case MSG_QUERYSUPPORT
:
486 twCC
= set_onevalue(pCapability
, TWTY_INT32
,
487 TWQC_GET
| TWQC_GETDEFAULT
| TWQC_GETCURRENT
);
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
);
496 /* .. Fall through intentional .. */
499 TRACE("Returning current bitdepth of %d\n", activeDS
.sane_param
.depth
);
500 twCC
= set_onevalue(pCapability
, TWTY_UINT16
, activeDS
.sane_param
.depth
);
507 /* CAP_UICONTROLLABLE */
508 static TW_UINT16
SANE_CAPUiControllable(pTW_CAPABILITY pCapability
, TW_UINT16 action
)
510 TW_UINT16 twCC
= TWCC_BADCAP
;
512 TRACE("CAP_UICONTROLLABLE\n");
516 case MSG_QUERYSUPPORT
:
517 twCC
= set_onevalue(pCapability
, TWTY_INT32
, TWQC_GET
);
521 twCC
= set_onevalue(pCapability
, TWTY_BOOL
, TRUE
);
528 /* ICAP_COMPRESSION */
529 static TW_UINT16
SANE_ICAPCompression (pTW_CAPABILITY pCapability
, TW_UINT16 action
)
531 static const TW_UINT32 possible_values
[] = { TWCP_NONE
};
533 TW_UINT16 twCC
= TWCC_BADCAP
;
535 TRACE("ICAP_COMPRESSION\n");
539 case MSG_QUERYSUPPORT
:
540 twCC
= set_onevalue(pCapability
, TWTY_INT32
,
541 TWQC_GET
| TWQC_SET
| TWQC_GETDEFAULT
| TWQC_GETCURRENT
| TWQC_RESET
);
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");
551 twCC
= msg_set(pCapability
, &val
);
552 if (twCC
== TWCC_SUCCESS
)
553 FIXME("Partial Stub: COMPRESSION set to %d, but ignored\n", val
);
557 twCC
= set_onevalue(pCapability
, TWTY_UINT16
, TWCP_NONE
);
561 /* .. fall through intentional .. */
564 twCC
= set_onevalue(pCapability
, TWTY_UINT16
, TWCP_NONE
);
570 /* ICAP_XRESOLUTION, ICAP_YRESOLUTION */
571 static TW_UINT16
SANE_ICAPResolution (pTW_CAPABILITY pCapability
, TW_UINT16 action
, TW_UINT16 cap
)
573 TW_UINT16 twCC
= TWCC_BADCAP
;
574 #ifdef SONAME_LIBSANE
576 SANE_Int current_resolution
;
577 TW_FIX32
*default_res
;
578 const char *best_option_name
;
579 SANE_Int minval
, maxval
, quantval
;
583 TRACE("ICAP_%cRESOLUTION\n", cap
== ICAP_XRESOLUTION
? 'X' : 'Y');
585 /* Some scanners support 'x-resolution', most seem to just support 'resolution' */
586 if (cap
== ICAP_XRESOLUTION
)
588 best_option_name
= "x-resolution";
589 default_res
= &activeDS
.defaultXResolution
;
593 best_option_name
= "y-resolution";
594 default_res
= &activeDS
.defaultYResolution
;
596 if (sane_option_get_int(activeDS
.deviceHandle
, best_option_name
, ¤t_resolution
) != SANE_STATUS_GOOD
)
598 best_option_name
= "resolution";
599 if (sane_option_get_int(activeDS
.deviceHandle
, best_option_name
, ¤t_resolution
) != SANE_STATUS_GOOD
)
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
606 if (cap
== ICAP_XRESOLUTION
&& ! activeDS
.XResolutionSet
)
608 default_res
->Whole
= current_resolution
;
609 default_res
->Frac
= 0;
610 activeDS
.XResolutionSet
= TRUE
;
613 if (cap
== ICAP_YRESOLUTION
&& ! activeDS
.YResolutionSet
)
615 default_res
->Whole
= current_resolution
;
616 default_res
->Frac
= 0;
617 activeDS
.YResolutionSet
= TRUE
;
622 case MSG_QUERYSUPPORT
:
623 twCC
= set_onevalue(pCapability
, TWTY_INT32
,
624 TWQC_GET
| TWQC_SET
| TWQC_GETDEFAULT
| TWQC_GETCURRENT
| TWQC_RESET
);
628 sane_rc
= sane_option_probe_resolution(activeDS
.deviceHandle
, best_option_name
, &minval
, &maxval
, &quantval
);
629 if (sane_rc
!= SANE_STATUS_GOOD
)
632 twCC
= msg_get_range(pCapability
, TWTY_FIX32
,
633 minval
, maxval
, quantval
== 0 ? 1 : quantval
, default_res
->Whole
, current_resolution
);
637 twCC
= msg_set(pCapability
, &val
);
638 if (twCC
== TWCC_SUCCESS
)
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
)
645 FIXME("Status of %d not expected or handled\n", sane_rc
);
648 else if (set_status
== SANE_INFO_INEXACT
)
649 twCC
= TWCC_CHECKSTATUS
;
654 twCC
= set_onevalue(pCapability
, TWTY_FIX32
, default_res
->Whole
);
658 sane_rc
= sane_option_set_int(activeDS
.deviceHandle
, best_option_name
, default_res
->Whole
, NULL
);
659 if (sane_rc
!= SANE_STATUS_GOOD
)
662 /* .. fall through intentional .. */
665 twCC
= set_onevalue(pCapability
, TWTY_FIX32
, current_resolution
);
672 /* ICAP_PHYSICALHEIGHT, ICAP_PHYSICALWIDTH */
673 static TW_UINT16
SANE_ICAPPhysical (pTW_CAPABILITY pCapability
, TW_UINT16 action
, TW_UINT16 cap
)
675 TW_UINT16 twCC
= TWCC_BADCAP
;
676 #ifdef SONAME_LIBSANE
678 char option_name
[64];
679 SANE_Fixed lower
, upper
;
680 SANE_Unit lowerunit
, upperunit
;
683 TRACE("ICAP_PHYSICAL%s\n", cap
== ICAP_PHYSICALHEIGHT
? "HEIGHT" : "WIDTH");
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
);
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
);
695 if (upperunit
!= lowerunit
)
698 if (! convert_sane_res_to_twain(SANE_UNFIX(upper
) - SANE_UNFIX(lower
), upperunit
, &res
, TWUN_INCHES
))
703 case MSG_QUERYSUPPORT
:
704 twCC
= set_onevalue(pCapability
, TWTY_INT32
,
705 TWQC_GET
| TWQC_GETDEFAULT
| TWQC_GETCURRENT
);
711 /* .. fall through intentional .. */
714 twCC
= set_onevalue(pCapability
, TWTY_FIX32
, res
.Whole
| (res
.Frac
<< 16));
721 /* ICAP_PIXELFLAVOR */
722 static TW_UINT16
SANE_ICAPPixelFlavor (pTW_CAPABILITY pCapability
, TW_UINT16 action
)
724 TW_UINT16 twCC
= TWCC_BADCAP
;
725 #ifdef SONAME_LIBSANE
726 static const TW_UINT32 possible_values
[] = { TWPF_CHOCOLATE
, TWPF_VANILLA
};
728 TW_UINT32 flavor
= activeDS
.sane_param
.depth
== 1 ? TWPF_VANILLA
: TWPF_CHOCOLATE
;
730 TRACE("ICAP_PIXELFLAVOR\n");
734 case MSG_QUERYSUPPORT
:
735 twCC
= set_onevalue(pCapability
, TWTY_INT32
,
736 TWQC_GET
| TWQC_SET
| TWQC_GETDEFAULT
| TWQC_GETCURRENT
| TWQC_RESET
);
740 twCC
= msg_get_enum(pCapability
, possible_values
, sizeof(possible_values
) / sizeof(possible_values
[0]),
741 TWTY_UINT16
, flavor
, flavor
);
745 twCC
= msg_set(pCapability
, &val
);
746 if (twCC
== TWCC_SUCCESS
)
748 FIXME("Stub: PIXELFLAVOR set to %d, but ignored\n", val
);
753 twCC
= set_onevalue(pCapability
, TWTY_UINT16
, flavor
);
757 /* .. fall through intentional .. */
760 twCC
= set_onevalue(pCapability
, TWTY_UINT16
, flavor
);
767 #ifdef SONAME_LIBSANE
768 static TW_UINT16
get_width_height(double *width
, double *height
, BOOL max
)
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
;
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
);
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
);
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
);
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
);
794 *width
= SANE_UNFIX(brx_max
) - SANE_UNFIX(tlx_min
);
796 *width
= SANE_UNFIX(brx_current
) - SANE_UNFIX(tlx_current
);
799 *height
= SANE_UNFIX(bry_max
) - SANE_UNFIX(tly_min
);
801 *height
= SANE_UNFIX(bry_current
) - SANE_UNFIX(tly_current
);
803 return(TWCC_SUCCESS
);
806 static TW_UINT16
set_one_coord(const char *name
, double coord
)
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
);
815 static TW_UINT16
set_width_height(double width
, double height
)
817 TW_UINT16 rc
= TWCC_SUCCESS
;
818 rc
= set_one_coord("tl-x", 0);
819 if (rc
!= TWCC_SUCCESS
)
821 rc
= set_one_coord("br-x", width
);
822 if (rc
!= TWCC_SUCCESS
)
824 rc
= set_one_coord("tl-y", 0);
825 if (rc
!= TWCC_SUCCESS
)
827 rc
= set_one_coord("br-y", height
);
839 static const supported_size_t supported_sizes
[] =
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 },
853 #define SUPPORTED_SIZE_COUNT (sizeof(supported_sizes) / sizeof(supported_sizes[0]))
855 static TW_UINT16
get_default_paper_size(const supported_size_t
*s
, int n
)
860 double width
, height
;
862 rc
= GetLocaleInfoA(LOCALE_USER_DEFAULT
, LOCALE_IPAPERSIZE
| LOCALE_RETURN_NUMBER
, (void *) &paper
, sizeof(paper
));
867 defsize
= TWSS_USLETTER
;
870 defsize
= TWSS_USLEGAL
;
883 if (get_width_height(&width
, &height
, TRUE
) != TWCC_SUCCESS
)
886 for (i
= 0; i
< n
; i
++)
887 if (s
[i
].size
== defsize
)
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))
899 static TW_UINT16
get_current_paper_size(const supported_size_t
*s
, int n
)
902 double width
, height
;
903 double xdelta
, ydelta
;
905 if (get_width_height(&width
, &height
, FALSE
) != TWCC_SUCCESS
)
908 for (i
= 0; i
< n
; i
++)
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)
923 /* ICAP_SUPPORTEDSIZES */
924 static TW_UINT16
SANE_ICAPSupportedSizes (pTW_CAPABILITY pCapability
, TW_UINT16 action
)
926 TW_UINT16 twCC
= TWCC_BADCAP
;
927 #ifdef SONAME_LIBSANE
929 static TW_UINT32 possible_values
[SUPPORTED_SIZE_COUNT
];
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
);
935 TRACE("ICAP_SUPPORTEDSIZES\n");
939 case MSG_QUERYSUPPORT
:
940 twCC
= set_onevalue(pCapability
, TWTY_INT32
,
941 TWQC_GET
| TWQC_SET
| TWQC_GETDEFAULT
| TWQC_GETCURRENT
| TWQC_RESET
);
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");
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
);
959 ERR("Unsupported size %d\n", val
);
964 twCC
= set_onevalue(pCapability
, TWTY_UINT16
, default_size
);
969 for (i
= 1; i
< SUPPORTED_SIZE_COUNT
; i
++)
970 if (supported_sizes
[i
].size
== default_size
)
972 twCC
= set_width_height(supported_sizes
[i
].x
, supported_sizes
[i
].y
);
975 if (twCC
!= TWCC_SUCCESS
)
978 /* .. fall through intentional .. */
981 twCC
= set_onevalue(pCapability
, TWTY_UINT16
, current_size
);
985 #undef SUPPORTED_SIZE_COUNT
991 static TW_UINT16
SANE_CAPAutofeed (pTW_CAPABILITY pCapability
, TW_UINT16 action
)
993 TW_UINT16 twCC
= TWCC_BADCAP
;
994 #ifdef SONAME_LIBSANE
999 TRACE("CAP_AUTOFEED\n");
1001 if (sane_option_get_bool(activeDS
.deviceHandle
, "batch-scan", &autofeed
, NULL
) != SANE_STATUS_GOOD
)
1006 case MSG_QUERYSUPPORT
:
1007 twCC
= set_onevalue(pCapability
, TWTY_INT32
,
1008 TWQC_GET
| TWQC_SET
| TWQC_GETDEFAULT
| TWQC_GETCURRENT
| TWQC_RESET
);
1012 twCC
= set_onevalue(pCapability
, TWTY_BOOL
, autofeed
);
1016 twCC
= msg_set(pCapability
, &val
);
1017 if (twCC
== TWCC_SUCCESS
)
1020 autofeed
= SANE_TRUE
;
1022 autofeed
= SANE_FALSE
;
1024 status
= sane_option_set_bool(activeDS
.deviceHandle
, "batch-scan", autofeed
, NULL
);
1025 if (status
!= SANE_STATUS_GOOD
)
1027 ERR("Error %s: Could not set batch-scan to %d\n", psane_strstatus(status
), autofeed
);
1028 return sane_status_to_twcc(status
);
1033 case MSG_GETDEFAULT
:
1034 twCC
= set_onevalue(pCapability
, TWTY_BOOL
, SANE_TRUE
);
1038 autofeed
= SANE_TRUE
;
1039 status
= sane_option_set_bool(activeDS
.deviceHandle
, "batch-scan", autofeed
, NULL
);
1040 if (status
!= SANE_STATUS_GOOD
)
1042 ERR("Error %s: Could not reset batch-scan to SANE_TRUE\n", psane_strstatus(status
));
1043 return sane_status_to_twcc(status
);
1045 /* .. fall through intentional .. */
1047 case MSG_GETCURRENT
:
1048 twCC
= set_onevalue(pCapability
, TWTY_BOOL
, autofeed
);
1055 /* CAP_FEEDERENABLED */
1056 static TW_UINT16
SANE_CAPFeederEnabled (pTW_CAPABILITY pCapability
, TW_UINT16 action
)
1058 TW_UINT16 twCC
= TWCC_BADCAP
;
1059 #ifdef SONAME_LIBSANE
1063 SANE_Char source
[64];
1065 TRACE("CAP_FEEDERENABLED\n");
1067 if (sane_option_get_str(activeDS
.deviceHandle
, SANE_NAME_SCAN_SOURCE
, source
, sizeof(source
), NULL
) != SANE_STATUS_GOOD
)
1070 if (strcmp(source
, "Auto") == 0 || strcmp(source
, "ADF") == 0)
1077 case MSG_QUERYSUPPORT
:
1078 twCC
= set_onevalue(pCapability
, TWTY_INT32
,
1079 TWQC_GET
| TWQC_SET
| TWQC_GETDEFAULT
| TWQC_GETCURRENT
| TWQC_RESET
);
1083 twCC
= set_onevalue(pCapability
, TWTY_BOOL
, enabled
);
1087 twCC
= msg_set(pCapability
, &val
);
1088 if (twCC
== TWCC_SUCCESS
)
1095 strcpy(source
, "ADF");
1096 status
= sane_option_set_str(activeDS
.deviceHandle
, SANE_NAME_SCAN_SOURCE
, source
, NULL
);
1097 if (status
!= SANE_STATUS_GOOD
)
1099 strcpy(source
, "Auto");
1100 status
= sane_option_set_str(activeDS
.deviceHandle
, SANE_NAME_SCAN_SOURCE
, source
, NULL
);
1102 if (status
!= SANE_STATUS_GOOD
)
1104 ERR("Error %s: Could not set source to either ADF or Auto\n", psane_strstatus(status
));
1105 return sane_status_to_twcc(status
);
1110 case MSG_GETDEFAULT
:
1111 twCC
= set_onevalue(pCapability
, TWTY_BOOL
, TRUE
);
1115 strcpy(source
, "Auto");
1116 if (sane_option_set_str(activeDS
.deviceHandle
, SANE_NAME_SCAN_SOURCE
, source
, NULL
) == SANE_STATUS_GOOD
)
1118 twCC
= TWCC_SUCCESS
;
1119 /* .. fall through intentional .. */
1121 case MSG_GETCURRENT
:
1122 twCC
= set_onevalue(pCapability
, TWTY_BOOL
, enabled
);
1131 TW_UINT16
SANE_SaneCapability (pTW_CAPABILITY pCapability
, TW_UINT16 action
)
1133 TW_UINT16 twCC
= TWCC_CAPUNSUPPORTED
;
1135 TRACE("capability=%d action=%d\n", pCapability
->Cap
, action
);
1137 switch (pCapability
->Cap
)
1139 case CAP_SUPPORTEDCAPS
:
1140 if (action
== MSG_GET
)
1141 twCC
= TWAIN_GetSupportedCaps(pCapability
);
1143 twCC
= TWCC_BADVALUE
;
1147 twCC
= SANE_CAPXferCount (pCapability
, action
);
1150 case CAP_UICONTROLLABLE
:
1151 twCC
= SANE_CAPUiControllable (pCapability
, action
);
1155 twCC
= SANE_CAPAutofeed (pCapability
, action
);
1158 case CAP_FEEDERENABLED
:
1159 twCC
= SANE_CAPFeederEnabled (pCapability
, action
);
1162 case ICAP_PIXELTYPE
:
1163 twCC
= SANE_ICAPPixelType (pCapability
, action
);
1167 twCC
= SANE_ICAPUnits (pCapability
, action
);
1171 twCC
= SANE_ICAPBitDepth(pCapability
, action
);
1175 twCC
= SANE_ICAPXferMech (pCapability
, action
);
1178 case ICAP_PIXELFLAVOR
:
1179 twCC
= SANE_ICAPPixelFlavor (pCapability
, action
);
1182 case ICAP_COMPRESSION
:
1183 twCC
= SANE_ICAPCompression(pCapability
, action
);
1186 case ICAP_XRESOLUTION
:
1187 twCC
= SANE_ICAPResolution(pCapability
, action
, pCapability
->Cap
);
1190 case ICAP_YRESOLUTION
:
1191 twCC
= SANE_ICAPResolution(pCapability
, action
, pCapability
->Cap
);
1194 case ICAP_PHYSICALHEIGHT
:
1195 twCC
= SANE_ICAPPhysical(pCapability
, action
, pCapability
->Cap
);
1198 case ICAP_PHYSICALWIDTH
:
1199 twCC
= SANE_ICAPPhysical(pCapability
, action
, pCapability
->Cap
);
1202 case ICAP_SUPPORTEDSIZES
:
1203 twCC
= SANE_ICAPSupportedSizes (pCapability
, action
);
1206 case ICAP_PLANARCHUNKY
:
1207 FIXME("ICAP_PLANARCHUNKY not implemented\n");
1211 FIXME("ICAP_BITORDER not implemented\n");
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
);
1221 if (twCC
== TWCC_CAPUNSUPPORTED
)
1222 TRACE("capability 0x%x/action=%d being reported as unsupported\n", pCapability
->Cap
, action
);
1227 TW_UINT16
SANE_SaneSetDefaults (void)
1231 memset(&cap
, 0, sizeof(cap
));
1232 cap
.Cap
= CAP_AUTOFEED
;
1233 cap
.ConType
= TWON_DONTCARE16
;
1235 if (SANE_SaneCapability(&cap
, MSG_RESET
) == TWCC_SUCCESS
)
1236 GlobalFree(cap
.hContainer
);
1238 memset(&cap
, 0, sizeof(cap
));
1239 cap
.Cap
= CAP_FEEDERENABLED
;
1240 cap
.ConType
= TWON_DONTCARE16
;
1242 if (SANE_SaneCapability(&cap
, MSG_RESET
) == TWCC_SUCCESS
)
1243 GlobalFree(cap
.hContainer
);
1245 memset(&cap
, 0, sizeof(cap
));
1246 cap
.Cap
= ICAP_SUPPORTEDSIZES
;
1247 cap
.ConType
= TWON_DONTCARE16
;
1249 if (SANE_SaneCapability(&cap
, MSG_RESET
) == TWCC_SUCCESS
)
1250 GlobalFree(cap
.hContainer
);
1252 return TWCC_SUCCESS
;