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
;
59 swprintf( path
, MAX_PATH
, L
"c:\\windows\\twain_%u\\%s", sizeof(void *) * 8, dsname
);
60 hmod
= LoadLibraryW(path
);
62 ERR("Failed to load TWAIN Source %s\n", debugstr_w(path
));
65 dsEntry
= (DSENTRYPROC
)GetProcAddress(hmod
, "DS_Entry");
67 ERR("Failed to find DS_Entry() in TWAIN DS %s\n", debugstr_w(path
));
70 /* Loop to do multiple detects, mostly for sane.ds and gphoto2.ds */
74 sourceId
.Id
= DSM_sourceId
;
75 sourceId
.ProtocolMajor
= TWON_PROTOCOLMAJOR
;
76 sourceId
.ProtocolMinor
= TWON_PROTOCOLMINOR
;
77 ret
= dsEntry (&fakeOrigin
, DG_CONTROL
, DAT_IDENTITY
, MSG_GET
, &sourceId
);
78 if (ret
!= TWRC_SUCCESS
) {
79 ERR("Source->(DG_CONTROL,DAT_IDENTITY,MSG_GET) failed!\n");
82 TRACE("Manufacturer: %s\n", debugstr_a(sourceId
.Manufacturer
));
83 TRACE("ProductFamily: %s\n", debugstr_a(sourceId
.ProductFamily
));
84 TRACE("ProductName: %s\n", debugstr_a(sourceId
.ProductName
));
86 for (i
=0;i
<nrdevices
;i
++) {
87 if (!strcmp(sourceId
.ProductName
,devices
[i
].identity
.ProductName
))
92 devices
= realloc( devices
, sizeof(devices
[0])*(nrdevices
+1) );
93 devices
[nrdevices
].modname
= wcsdup( path
);
94 devices
[nrdevices
].identity
= sourceId
;
101 static BOOL detectionrun
= FALSE
;
104 twain_autodetect(void) {
105 if (detectionrun
) return;
108 twain_add_onedriver(L
"sane.ds");
109 twain_add_onedriver(L
"gphoto2.ds");
111 twain_add_onedriver(L
"Largan\\sp503a.ds");
112 twain_add_onedriver(L
"vivicam10\\vivicam10.ds");
113 twain_add_onedriver(L
"ws30slim\\sp500a.ds");
117 /* DG_CONTROL/DAT_NULL/MSG_CLOSEDSREQ|MSG_DEVICEEVENT|MSG_XFERREADY */
118 TW_UINT16
TWAIN_ControlNull (pTW_IDENTITY pOrigin
, pTW_IDENTITY pDest
, activeDS
*pSource
, TW_UINT16 MSG
, TW_MEMREF pData
)
120 struct pending_message
*message
;
122 TRACE ("DG_CONTROL/DAT_NULL MSG=%i\n", MSG
);
124 if (MSG
!= MSG_CLOSEDSREQ
&&
125 MSG
!= MSG_DEVICEEVENT
&&
126 MSG
!= MSG_XFERREADY
)
128 DSM_twCC
= TWCC_BADPROTOCOL
;
132 message
= HeapAlloc(GetProcessHeap(), 0, sizeof(*message
));
135 DSM_twCC
= TWCC_LOWMEMORY
;
140 list_add_tail(&pSource
->pending_messages
, &message
->entry
);
142 /* Delphi twain only sends us messages from one window, and it
143 doesn't even give us the real handle to that window. Other
144 applications might decide to forward messages sent to DSM_parent
145 or to the one supplied to ENABLEDS. So let's try very hard to
146 find a window that will work. */
148 PostMessageW(DSM_parent
, event_message
, 0, 0);
149 if (pSource
->ui_window
&& pSource
->ui_window
!= DSM_parent
)
150 PostMessageW(pSource
->ui_window
, event_message
, 0, 0);
151 if (pSource
->event_window
&& pSource
->event_window
!= pSource
->ui_window
&&
152 pSource
->event_window
!= DSM_parent
)
153 PostMessageW(pSource
->event_window
, event_message
, 0, 0);
154 PostMessageW(0, event_message
, 0, 0);
159 /* Filters MSG_PROCESSEVENT messages before reaching the data source */
160 TW_UINT16
TWAIN_ProcessEvent (pTW_IDENTITY pOrigin
, activeDS
*pSource
, TW_MEMREF pData
)
162 TW_EVENT
*event
= (TW_EVENT
*)pData
;
163 MSG
*msg
= (MSG
*)event
->pEvent
;
164 TW_UINT16 result
= TWRC_NOTDSEVENT
;
166 TRACE("%x,%x\n", msg
->message
, event_message
);
168 if (msg
->message
== event_message
)
170 if (!list_empty (&pSource
->pending_messages
))
172 struct list
*entry
= list_head (&pSource
->pending_messages
);
173 struct pending_message
*message
= LIST_ENTRY(entry
, struct pending_message
, entry
);
174 event
->TWMessage
= message
->msg
;
176 TRACE("<-- %x\n", event
->TWMessage
);
179 event
->TWMessage
= MSG_NULL
;
180 result
= TWRC_DSEVENT
;
186 pSource
->event_window
= msg
->hwnd
;
187 if (!list_empty (&pSource
->pending_messages
) &&
188 !PeekMessageW(&dummy
, msg
->hwnd
, event_message
, event_message
, PM_NOREMOVE
))
190 PostMessageW(msg
->hwnd
, event_message
, 0, 0);
197 /* DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS */
198 TW_UINT16
TWAIN_CloseDS (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
200 TW_UINT16 twRC
= TWRC_SUCCESS
;
201 pTW_IDENTITY pIdentity
= (pTW_IDENTITY
) pData
;
202 activeDS
*currentDS
= NULL
, *prevDS
= NULL
;
204 TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS\n");
206 for (currentDS
= activeSources
; currentDS
; currentDS
= currentDS
->next
) {
207 if (currentDS
->identity
.Id
== pIdentity
->Id
)
212 DSM_twCC
= TWCC_NODS
;
215 twRC
= currentDS
->dsEntry (pOrigin
, DG_CONTROL
, DAT_IDENTITY
, MSG_CLOSEDS
, pData
);
216 /* This causes crashes due to still open Windows, so leave out for now.
217 * FreeLibrary (currentDS->hmod);
220 prevDS
->next
= currentDS
->next
;
222 activeSources
= currentDS
->next
;
223 HeapFree (GetProcessHeap(), 0, currentDS
);
224 if (twRC
== TWRC_SUCCESS
)
225 DSM_twCC
= TWCC_SUCCESS
;
226 else /* FIXME: unclear how to get TWCC */
227 DSM_twCC
= TWCC_SEQERROR
;
231 /* DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT */
232 TW_UINT16
TWAIN_IdentityGetDefault (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
234 pTW_IDENTITY pSourceIdentity
= (pTW_IDENTITY
) pData
;
236 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT\n");
237 DSM_twCC
= TWCC_NODS
;
241 *pSourceIdentity
= devices
[0].identity
;
242 DSM_twCC
= TWCC_SUCCESS
;
246 /* DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST */
247 TW_UINT16
TWAIN_IdentityGetFirst (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
249 pTW_IDENTITY pSourceIdentity
= (pTW_IDENTITY
) pData
;
251 TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST\n");
254 TRACE ("no entries found.\n");
255 DSM_twCC
= TWCC_NODS
;
258 DSM_currentDevice
= 0;
259 *pSourceIdentity
= devices
[DSM_currentDevice
++].identity
;
263 /* DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT */
264 TW_UINT16
TWAIN_IdentityGetNext (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
266 pTW_IDENTITY pSourceIdentity
= (pTW_IDENTITY
) pData
;
268 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT\n");
269 if (!nrdevices
|| (DSM_currentDevice
== nrdevices
)) {
270 DSM_twCC
= TWCC_SUCCESS
;
271 return TWRC_ENDOFLIST
;
273 *pSourceIdentity
= devices
[DSM_currentDevice
++].identity
;
277 /* DG_CONTROL/DAT_IDENTITY/MSG_OPENDS */
278 TW_UINT16
TWAIN_OpenDS (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
281 pTW_IDENTITY pIdentity
= (pTW_IDENTITY
) pData
;
285 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_OPENDS\n");
286 TRACE("pIdentity is %s\n", pIdentity
->ProductName
);
287 if (!DSM_initialized
) {
288 FIXME("seq error\n");
289 DSM_twCC
= TWCC_SEQERROR
;
295 DSM_twCC
= TWCC_NODS
;
299 if (pIdentity
->ProductName
[0] != '\0') {
300 /* Make sure the source to be opened exists in the device list */
301 for (i
= 0; i
<nrdevices
; i
++)
302 if (!strcmp (devices
[i
].identity
.ProductName
, pIdentity
->ProductName
))
306 } /* else use the first device */
308 /* the source is found in the device list */
309 newSource
= HeapAlloc (GetProcessHeap(), 0, sizeof (activeDS
));
311 DSM_twCC
= TWCC_LOWMEMORY
;
312 FIXME("Out of memory.\n");
315 hmod
= LoadLibraryW(devices
[i
].modname
);
317 ERR("Failed to load TWAIN Source %s\n", debugstr_w(devices
[i
].modname
));
318 DSM_twCC
= TWCC_OPERATIONERROR
;
319 HeapFree(GetProcessHeap(), 0, newSource
);
322 newSource
->hmod
= hmod
;
323 newSource
->dsEntry
= (DSENTRYPROC
)GetProcAddress(hmod
, "DS_Entry");
324 /* Assign id for the opened data source */
325 pIdentity
->Id
= DSM_sourceId
++;
326 if (TWRC_SUCCESS
!= newSource
->dsEntry (pOrigin
, DG_CONTROL
, DAT_IDENTITY
, MSG_OPENDS
, pIdentity
)) {
327 DSM_twCC
= TWCC_OPERATIONERROR
;
328 HeapFree(GetProcessHeap(), 0, newSource
);
332 /* add the data source to an internal active source list */
333 newSource
->next
= activeSources
;
334 newSource
->identity
.Id
= pIdentity
->Id
;
335 strcpy (newSource
->identity
.ProductName
, pIdentity
->ProductName
);
336 list_init(&newSource
->pending_messages
);
337 newSource
->ui_window
= NULL
;
338 newSource
->event_window
= NULL
;
339 activeSources
= newSource
;
340 DSM_twCC
= TWCC_SUCCESS
;
349 static INT_PTR CALLBACK
userselect_dlgproc (HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
355 userselect_data
*data
= (userselect_data
*)lparam
;
358 BOOL any_devices
= FALSE
;
360 SetWindowLongPtrW(hwnd
, DWLP_USER
, (LONG_PTR
)data
);
362 sourcelist
= GetDlgItem(hwnd
, IDC_LISTSOURCE
);
364 for (i
=0; i
<nrdevices
; i
++)
366 TW_IDENTITY
*id
= &devices
[i
].identity
;
369 if ((id
->SupportedGroups
& data
->origin
->SupportedGroups
) == 0)
372 index
= SendMessageA(sourcelist
, LB_ADDSTRING
, 0, (LPARAM
)id
->ProductName
);
373 SendMessageW(sourcelist
, LB_SETITEMDATA
, (WPARAM
)index
, (LPARAM
)i
);
379 EnableWindow(GetDlgItem(hwnd
, IDOK
), TRUE
);
381 /* FIXME: Select the supplied product name or default source. */
382 SendMessageW(sourcelist
, LB_SETCURSEL
, 0, 0);
391 if (wparam
== MAKEWPARAM(IDCANCEL
, BN_CLICKED
))
396 else if (wparam
== MAKEWPARAM(IDOK
, BN_CLICKED
) ||
397 wparam
== MAKEWPARAM(IDC_LISTSOURCE
, LBN_DBLCLK
))
399 userselect_data
*data
= (userselect_data
*)GetWindowLongPtrW(hwnd
, DWLP_USER
);
403 sourcelist
= GetDlgItem(hwnd
, IDC_LISTSOURCE
);
405 index
= SendMessageW(sourcelist
, LB_GETCURSEL
, 0, 0);
410 index
= SendMessageW(sourcelist
, LB_GETITEMDATA
, (WPARAM
)index
, 0);
412 *data
->result
= devices
[index
].identity
;
414 /* FIXME: Save this as the default source */
424 /* DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT */
425 TW_UINT16
TWAIN_UserSelect (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
427 userselect_data param
= {pOrigin
, pData
};
428 HWND parent
= DSM_parent
;
430 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT SupportedGroups=0x%x ProductName=%s\n",
431 pOrigin
->SupportedGroups
, wine_dbgstr_a(param
.result
->ProductName
));
435 if (!IsWindow(parent
))
438 if (DialogBoxParamW(DSM_hinstance
, MAKEINTRESOURCEW(DLG_USERSELECT
),
439 parent
, userselect_dlgproc
, (LPARAM
)¶m
) == 0)
442 DSM_twCC
= TWCC_SUCCESS
;
446 TRACE("<-- %s\n", wine_dbgstr_a(param
.result
->ProductName
));
447 DSM_twCC
= TWCC_SUCCESS
;
451 /* DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM */
452 TW_UINT16
TWAIN_CloseDSM (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
454 activeDS
*currentDS
= activeSources
, *nextDS
;
456 TRACE("DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM\n");
460 DSM_initialized
= FALSE
;
462 /* If there are data sources still open, close them now. */
463 while (currentDS
!= NULL
)
465 nextDS
= currentDS
->next
;
466 currentDS
->dsEntry (pOrigin
, DG_CONTROL
, DAT_IDENTITY
, MSG_CLOSEDS
, pData
);
467 HeapFree (GetProcessHeap(), 0, currentDS
);
470 activeSources
= NULL
;
472 DSM_twCC
= TWCC_SUCCESS
;
475 DSM_twCC
= TWCC_SEQERROR
;
480 /* DG_CONTROL/DAT_PARENT/MSG_OPENDSM */
481 TW_UINT16
TWAIN_OpenDSM (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
483 TW_UINT16 twRC
= TWRC_SUCCESS
;
485 TRACE("DG_CONTROL/DAT_PARENT/MSG_OPENDSM\n");
486 if (!DSM_initialized
) {
487 event_message
= RegisterWindowMessageA("WINE TWAIN_32 EVENT");
488 DSM_currentDevice
= 0;
489 DSM_initialized
= TRUE
;
490 DSM_twCC
= TWCC_SUCCESS
;
493 /* operation invoked in invalid state */
494 DSM_twCC
= TWCC_SEQERROR
;
497 DSM_parent
= (HWND
)pData
;
501 /* DG_CONTROL/DAT_STATUS/MSG_GET */
502 TW_UINT16
TWAIN_GetDSMStatus (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
504 pTW_STATUS pSourceStatus
= (pTW_STATUS
) pData
;
506 TRACE ("DG_CONTROL/DAT_STATUS/MSG_GET\n");
508 pSourceStatus
->ConditionCode
= DSM_twCC
;
509 DSM_twCC
= TWCC_SUCCESS
; /* clear the condition code */