2 * TWAIN32 Source Manager
4 * Copyright 2000 Corel Corporation
5 * Copyright 2006 Marcus Meissner
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
32 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(twain
);
36 static TW_UINT16 DSM_initialized
; /* whether Source Manager is initialized */
37 static TW_UINT32 DSM_sourceId
; /* source id generator */
38 static TW_UINT16 DSM_currentDevice
; /* keep track of device during enumeration */
39 static HWND DSM_parent
;
40 static UINT event_message
;
47 static int nrdevices
= 0;
48 static struct all_devices
*devices
= NULL
;
51 twain_add_onedriver(const WCHAR
*dsname
) {
54 TW_IDENTITY fakeOrigin
;
56 struct all_devices
*new_devices
;
60 swprintf( path
, MAX_PATH
, L
"c:\\windows\\twain_%u\\%s", sizeof(void *) * 8, dsname
);
61 hmod
= LoadLibraryW(path
);
63 ERR("Failed to load TWAIN Source %s\n", debugstr_w(path
));
66 dsEntry
= (DSENTRYPROC
)GetProcAddress(hmod
, "DS_Entry");
68 ERR("Failed to find DS_Entry() in TWAIN DS %s\n", debugstr_w(path
));
71 /* Loop to do multiple detects, mostly for sane.ds and gphoto2.ds */
75 sourceId
.Id
= DSM_sourceId
;
76 sourceId
.ProtocolMajor
= TWON_PROTOCOLMAJOR
;
77 sourceId
.ProtocolMinor
= TWON_PROTOCOLMINOR
;
78 ret
= dsEntry (&fakeOrigin
, DG_CONTROL
, DAT_IDENTITY
, MSG_GET
, &sourceId
);
79 if (ret
!= TWRC_SUCCESS
) {
80 ERR("Source->(DG_CONTROL,DAT_IDENTITY,MSG_GET) failed!\n");
83 TRACE("Manufacturer: %s\n", debugstr_a(sourceId
.Manufacturer
));
84 TRACE("ProductFamily: %s\n", debugstr_a(sourceId
.ProductFamily
));
85 TRACE("ProductName: %s\n", debugstr_a(sourceId
.ProductName
));
87 for (i
=0;i
<nrdevices
;i
++) {
88 if (!strcmp(sourceId
.ProductName
,devices
[i
].identity
.ProductName
))
93 new_devices
= realloc( devices
, sizeof(devices
[0]) * (nrdevices
+ 1) );
96 devices
= new_devices
;
97 devices
[nrdevices
].modname
= wcsdup( path
);
98 devices
[nrdevices
].identity
= sourceId
;
105 static BOOL detectionrun
= FALSE
;
108 twain_autodetect(void) {
109 if (detectionrun
) return;
112 twain_add_onedriver(L
"sane.ds");
113 twain_add_onedriver(L
"gphoto2.ds");
115 twain_add_onedriver(L
"Largan\\sp503a.ds");
116 twain_add_onedriver(L
"vivicam10\\vivicam10.ds");
117 twain_add_onedriver(L
"ws30slim\\sp500a.ds");
121 /* DG_CONTROL/DAT_NULL/MSG_CLOSEDSREQ|MSG_DEVICEEVENT|MSG_XFERREADY */
122 TW_UINT16
TWAIN_ControlNull (pTW_IDENTITY pOrigin
, pTW_IDENTITY pDest
, activeDS
*pSource
, TW_UINT16 MSG
, TW_MEMREF pData
)
124 struct pending_message
*message
;
126 TRACE ("DG_CONTROL/DAT_NULL MSG=%i\n", MSG
);
128 if (MSG
!= MSG_CLOSEDSREQ
&&
129 MSG
!= MSG_DEVICEEVENT
&&
130 MSG
!= MSG_XFERREADY
)
132 DSM_twCC
= TWCC_BADPROTOCOL
;
136 message
= HeapAlloc(GetProcessHeap(), 0, sizeof(*message
));
139 DSM_twCC
= TWCC_LOWMEMORY
;
144 list_add_tail(&pSource
->pending_messages
, &message
->entry
);
146 /* Delphi twain only sends us messages from one window, and it
147 doesn't even give us the real handle to that window. Other
148 applications might decide to forward messages sent to DSM_parent
149 or to the one supplied to ENABLEDS. So let's try very hard to
150 find a window that will work. */
152 PostMessageW(DSM_parent
, event_message
, 0, 0);
153 if (pSource
->ui_window
&& pSource
->ui_window
!= DSM_parent
)
154 PostMessageW(pSource
->ui_window
, event_message
, 0, 0);
155 if (pSource
->event_window
&& pSource
->event_window
!= pSource
->ui_window
&&
156 pSource
->event_window
!= DSM_parent
)
157 PostMessageW(pSource
->event_window
, event_message
, 0, 0);
158 PostMessageW(0, event_message
, 0, 0);
163 /* Filters MSG_PROCESSEVENT messages before reaching the data source */
164 TW_UINT16
TWAIN_ProcessEvent (pTW_IDENTITY pOrigin
, activeDS
*pSource
, TW_MEMREF pData
)
166 TW_EVENT
*event
= (TW_EVENT
*)pData
;
167 MSG
*msg
= (MSG
*)event
->pEvent
;
168 TW_UINT16 result
= TWRC_NOTDSEVENT
;
170 TRACE("%x,%x\n", msg
->message
, event_message
);
172 if (msg
->message
== event_message
)
174 if (!list_empty (&pSource
->pending_messages
))
176 struct list
*entry
= list_head (&pSource
->pending_messages
);
177 struct pending_message
*message
= LIST_ENTRY(entry
, struct pending_message
, entry
);
178 event
->TWMessage
= message
->msg
;
180 TRACE("<-- %x\n", event
->TWMessage
);
183 event
->TWMessage
= MSG_NULL
;
184 result
= TWRC_DSEVENT
;
190 pSource
->event_window
= msg
->hwnd
;
191 if (!list_empty (&pSource
->pending_messages
) &&
192 !PeekMessageW(&dummy
, msg
->hwnd
, event_message
, event_message
, PM_NOREMOVE
))
194 PostMessageW(msg
->hwnd
, event_message
, 0, 0);
201 /* DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS */
202 TW_UINT16
TWAIN_CloseDS (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
204 TW_UINT16 twRC
= TWRC_SUCCESS
;
205 pTW_IDENTITY pIdentity
= (pTW_IDENTITY
) pData
;
206 activeDS
*currentDS
= NULL
, *prevDS
= NULL
;
208 TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS\n");
210 for (currentDS
= activeSources
; currentDS
; currentDS
= currentDS
->next
) {
211 if (currentDS
->identity
.Id
== pIdentity
->Id
)
216 DSM_twCC
= TWCC_NODS
;
219 twRC
= currentDS
->dsEntry (pOrigin
, DG_CONTROL
, DAT_IDENTITY
, MSG_CLOSEDS
, pData
);
220 /* This causes crashes due to still open Windows, so leave out for now.
221 * FreeLibrary (currentDS->hmod);
224 prevDS
->next
= currentDS
->next
;
226 activeSources
= currentDS
->next
;
227 HeapFree (GetProcessHeap(), 0, currentDS
);
228 if (twRC
== TWRC_SUCCESS
)
229 DSM_twCC
= TWCC_SUCCESS
;
230 else /* FIXME: unclear how to get TWCC */
231 DSM_twCC
= TWCC_SEQERROR
;
235 /* DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT */
236 TW_UINT16
TWAIN_IdentityGetDefault (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
238 pTW_IDENTITY pSourceIdentity
= (pTW_IDENTITY
) pData
;
240 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT\n");
241 DSM_twCC
= TWCC_NODS
;
245 *pSourceIdentity
= devices
[0].identity
;
246 DSM_twCC
= TWCC_SUCCESS
;
250 /* DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST */
251 TW_UINT16
TWAIN_IdentityGetFirst (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
253 pTW_IDENTITY pSourceIdentity
= (pTW_IDENTITY
) pData
;
255 TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST\n");
258 TRACE ("no entries found.\n");
259 DSM_twCC
= TWCC_NODS
;
262 DSM_currentDevice
= 0;
263 *pSourceIdentity
= devices
[DSM_currentDevice
++].identity
;
267 /* DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT */
268 TW_UINT16
TWAIN_IdentityGetNext (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
270 pTW_IDENTITY pSourceIdentity
= (pTW_IDENTITY
) pData
;
272 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT\n");
273 if (!nrdevices
|| (DSM_currentDevice
== nrdevices
)) {
274 DSM_twCC
= TWCC_SUCCESS
;
275 return TWRC_ENDOFLIST
;
277 *pSourceIdentity
= devices
[DSM_currentDevice
++].identity
;
281 /* DG_CONTROL/DAT_IDENTITY/MSG_OPENDS */
282 TW_UINT16
TWAIN_OpenDS (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
285 pTW_IDENTITY pIdentity
= (pTW_IDENTITY
) pData
;
289 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_OPENDS\n");
290 TRACE("pIdentity is %s\n", pIdentity
->ProductName
);
291 if (!DSM_initialized
) {
292 FIXME("seq error\n");
293 DSM_twCC
= TWCC_SEQERROR
;
299 DSM_twCC
= TWCC_NODS
;
303 if (pIdentity
->ProductName
[0] != '\0') {
304 /* Make sure the source to be opened exists in the device list */
305 for (i
= 0; i
<nrdevices
; i
++)
306 if (!strcmp (devices
[i
].identity
.ProductName
, pIdentity
->ProductName
))
310 } /* else use the first device */
312 /* the source is found in the device list */
313 newSource
= HeapAlloc (GetProcessHeap(), 0, sizeof (activeDS
));
315 DSM_twCC
= TWCC_LOWMEMORY
;
316 FIXME("Out of memory.\n");
319 hmod
= LoadLibraryW(devices
[i
].modname
);
321 ERR("Failed to load TWAIN Source %s\n", debugstr_w(devices
[i
].modname
));
322 DSM_twCC
= TWCC_OPERATIONERROR
;
323 HeapFree(GetProcessHeap(), 0, newSource
);
326 newSource
->hmod
= hmod
;
327 newSource
->dsEntry
= (DSENTRYPROC
)GetProcAddress(hmod
, "DS_Entry");
328 /* Assign id for the opened data source */
329 pIdentity
->Id
= DSM_sourceId
++;
330 if (TWRC_SUCCESS
!= newSource
->dsEntry (pOrigin
, DG_CONTROL
, DAT_IDENTITY
, MSG_OPENDS
, pIdentity
)) {
331 DSM_twCC
= TWCC_OPERATIONERROR
;
332 HeapFree(GetProcessHeap(), 0, newSource
);
336 /* add the data source to an internal active source list */
337 newSource
->next
= activeSources
;
338 newSource
->identity
.Id
= pIdentity
->Id
;
339 strcpy (newSource
->identity
.ProductName
, pIdentity
->ProductName
);
340 list_init(&newSource
->pending_messages
);
341 newSource
->ui_window
= NULL
;
342 newSource
->event_window
= NULL
;
343 activeSources
= newSource
;
344 DSM_twCC
= TWCC_SUCCESS
;
353 static INT_PTR CALLBACK
userselect_dlgproc (HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
359 userselect_data
*data
= (userselect_data
*)lparam
;
362 BOOL any_devices
= FALSE
;
364 SetWindowLongPtrW(hwnd
, DWLP_USER
, (LONG_PTR
)data
);
366 sourcelist
= GetDlgItem(hwnd
, IDC_LISTSOURCE
);
368 for (i
=0; i
<nrdevices
; i
++)
370 TW_IDENTITY
*id
= &devices
[i
].identity
;
373 if ((id
->SupportedGroups
& data
->origin
->SupportedGroups
) == 0)
376 index
= SendMessageA(sourcelist
, LB_ADDSTRING
, 0, (LPARAM
)id
->ProductName
);
377 SendMessageW(sourcelist
, LB_SETITEMDATA
, (WPARAM
)index
, (LPARAM
)i
);
383 EnableWindow(GetDlgItem(hwnd
, IDOK
), TRUE
);
385 /* FIXME: Select the supplied product name or default source. */
386 SendMessageW(sourcelist
, LB_SETCURSEL
, 0, 0);
395 if (wparam
== MAKEWPARAM(IDCANCEL
, BN_CLICKED
))
400 else if (wparam
== MAKEWPARAM(IDOK
, BN_CLICKED
) ||
401 wparam
== MAKEWPARAM(IDC_LISTSOURCE
, LBN_DBLCLK
))
403 userselect_data
*data
= (userselect_data
*)GetWindowLongPtrW(hwnd
, DWLP_USER
);
407 sourcelist
= GetDlgItem(hwnd
, IDC_LISTSOURCE
);
409 index
= SendMessageW(sourcelist
, LB_GETCURSEL
, 0, 0);
414 index
= SendMessageW(sourcelist
, LB_GETITEMDATA
, (WPARAM
)index
, 0);
416 *data
->result
= devices
[index
].identity
;
418 /* FIXME: Save this as the default source */
428 /* DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT */
429 TW_UINT16
TWAIN_UserSelect (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
431 userselect_data param
= {pOrigin
, pData
};
432 HWND parent
= DSM_parent
;
434 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT SupportedGroups=0x%lx ProductName=%s\n",
435 pOrigin
->SupportedGroups
, wine_dbgstr_a(param
.result
->ProductName
));
439 if (!IsWindow(parent
))
442 if (DialogBoxParamW(DSM_hinstance
, MAKEINTRESOURCEW(DLG_USERSELECT
),
443 parent
, userselect_dlgproc
, (LPARAM
)¶m
) == 0)
446 DSM_twCC
= TWCC_SUCCESS
;
450 TRACE("<-- %s\n", wine_dbgstr_a(param
.result
->ProductName
));
451 DSM_twCC
= TWCC_SUCCESS
;
455 /* DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM */
456 TW_UINT16
TWAIN_CloseDSM (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
458 activeDS
*currentDS
= activeSources
, *nextDS
;
460 TRACE("DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM\n");
464 DSM_initialized
= FALSE
;
466 /* If there are data sources still open, close them now. */
467 while (currentDS
!= NULL
)
469 nextDS
= currentDS
->next
;
470 currentDS
->dsEntry (pOrigin
, DG_CONTROL
, DAT_IDENTITY
, MSG_CLOSEDS
, pData
);
471 HeapFree (GetProcessHeap(), 0, currentDS
);
474 activeSources
= NULL
;
476 DSM_twCC
= TWCC_SUCCESS
;
479 DSM_twCC
= TWCC_SEQERROR
;
484 /* DG_CONTROL/DAT_PARENT/MSG_OPENDSM */
485 TW_UINT16
TWAIN_OpenDSM (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
487 TW_UINT16 twRC
= TWRC_SUCCESS
;
489 TRACE("DG_CONTROL/DAT_PARENT/MSG_OPENDSM\n");
490 if (!DSM_initialized
) {
491 event_message
= RegisterWindowMessageA("WINE TWAIN_32 EVENT");
492 DSM_currentDevice
= 0;
493 DSM_initialized
= TRUE
;
494 DSM_twCC
= TWCC_SUCCESS
;
497 /* operation invoked in invalid state */
498 DSM_twCC
= TWCC_SEQERROR
;
501 DSM_parent
= (HWND
)pData
;
505 /* DG_CONTROL/DAT_STATUS/MSG_GET */
506 TW_UINT16
TWAIN_GetDSMStatus (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
508 pTW_STATUS pSourceStatus
= (pTW_STATUS
) pData
;
510 TRACE ("DG_CONTROL/DAT_STATUS/MSG_GET\n");
512 pSourceStatus
->ConditionCode
= DSM_twCC
;
513 DSM_twCC
= TWCC_SUCCESS
; /* clear the condition code */