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
28 static TW_UINT16 DSM_initialized
; /* whether Source Manager is initialized */
29 static TW_UINT32 DSM_sourceId
; /* source id generator */
30 static TW_UINT16 DSM_currentDevice
; /* keep track of device during enumeration */
31 static HWND DSM_parent
;
32 static UINT event_message
;
39 static int nrdevices
= 0;
40 static struct all_devices
*devices
= NULL
;
43 twain_add_onedriver(const char *dsname
) {
46 TW_IDENTITY fakeOrigin
;
50 hmod
= LoadLibraryA(dsname
);
52 ERR("Failed to load TWAIN Source %s\n", dsname
);
55 dsEntry
= (DSENTRYPROC
)GetProcAddress(hmod
, "DS_Entry");
57 ERR("Failed to find DS_Entry() in TWAIN DS %s\n", dsname
);
60 /* Loop to do multiple detects, mostly for sane.ds and gphoto2.ds */
64 sourceId
.Id
= DSM_sourceId
;
65 sourceId
.ProtocolMajor
= TWON_PROTOCOLMAJOR
;
66 sourceId
.ProtocolMinor
= TWON_PROTOCOLMINOR
;
67 ret
= dsEntry (&fakeOrigin
, DG_CONTROL
, DAT_IDENTITY
, MSG_GET
, &sourceId
);
68 if (ret
!= TWRC_SUCCESS
) {
69 ERR("Source->(DG_CONTROL,DAT_IDENTITY,MSG_GET) failed!\n");
72 TRACE("Manufacturer: %s\n", debugstr_a(sourceId
.Manufacturer
));
73 TRACE("ProductFamily: %s\n", debugstr_a(sourceId
.ProductFamily
));
74 TRACE("ProductName: %s\n", debugstr_a(sourceId
.ProductName
));
76 for (i
=0;i
<nrdevices
;i
++) {
77 if (!strcmp(sourceId
.ProductName
,devices
[i
].identity
.ProductName
))
83 devices
= HeapReAlloc(GetProcessHeap(), 0, devices
, sizeof(devices
[0])*(nrdevices
+1));
85 devices
= HeapAlloc(GetProcessHeap(), 0, sizeof(devices
[0]));
86 if ((devices
[nrdevices
].modname
= HeapAlloc(GetProcessHeap(), 0, strlen(dsname
) + 1)))
87 lstrcpyA(devices
[nrdevices
].modname
, dsname
);
88 devices
[nrdevices
].identity
= sourceId
;
95 static BOOL detectionrun
= FALSE
;
98 twain_autodetect(void) {
99 if (detectionrun
) return;
102 twain_add_onedriver("sane.ds");
103 twain_add_onedriver("gphoto2.ds");
105 twain_add_onedriver("c:\\windows\\Twain_32\\Largan\\sp503a.ds");
106 twain_add_onedriver("c:\\windows\\Twain_32\\vivicam10\\vivicam10.ds");
107 twain_add_onedriver("c:\\windows\\Twain_32\\ws30slim\\sp500a.ds");
111 /* DG_CONTROL/DAT_NULL/MSG_CLOSEDSREQ|MSG_DEVICEEVENT|MSG_XFERREADY */
112 TW_UINT16
TWAIN_ControlNull (pTW_IDENTITY pOrigin
, pTW_IDENTITY pDest
, activeDS
*pSource
, TW_UINT16 MSG
, TW_MEMREF pData
)
114 struct pending_message
*message
;
116 TRACE ("DG_CONTROL/DAT_NULL MSG=%i\n", MSG
);
118 if (MSG
!= MSG_CLOSEDSREQ
&&
119 MSG
!= MSG_DEVICEEVENT
&&
120 MSG
!= MSG_XFERREADY
)
122 DSM_twCC
= TWCC_BADPROTOCOL
;
126 message
= HeapAlloc(GetProcessHeap(), 0, sizeof(*message
));
129 DSM_twCC
= TWCC_LOWMEMORY
;
134 list_add_tail(&pSource
->pending_messages
, &message
->entry
);
136 /* Delphi twain only sends us messages from one window, and it
137 doesn't even give us the real handle to that window. Other
138 applications might decide to forward messages sent to DSM_parent
139 or to the one supplied to ENABLEDS. So let's try very hard to
140 find a window that will work. */
142 PostMessageW(DSM_parent
, event_message
, 0, 0);
143 if (pSource
->ui_window
&& pSource
->ui_window
!= DSM_parent
)
144 PostMessageW(pSource
->ui_window
, event_message
, 0, 0);
145 if (pSource
->event_window
&& pSource
->event_window
!= pSource
->ui_window
&&
146 pSource
->event_window
!= DSM_parent
)
147 PostMessageW(pSource
->event_window
, event_message
, 0, 0);
148 PostMessageW(0, event_message
, 0, 0);
153 /* Filters MSG_PROCESSEVENT messages before reaching the data source */
154 TW_UINT16
TWAIN_ProcessEvent (pTW_IDENTITY pOrigin
, activeDS
*pSource
, TW_MEMREF pData
)
156 TW_EVENT
*event
= (TW_EVENT
*)pData
;
157 MSG
*msg
= (MSG
*)event
->pEvent
;
158 TW_UINT16 result
= TWRC_NOTDSEVENT
;
160 TRACE("%x,%x\n", msg
->message
, event_message
);
162 if (msg
->message
== event_message
)
164 if (!list_empty (&pSource
->pending_messages
))
166 struct list
*entry
= list_head (&pSource
->pending_messages
);
167 struct pending_message
*message
= LIST_ENTRY(entry
, struct pending_message
, entry
);
168 event
->TWMessage
= message
->msg
;
170 TRACE("<-- %x\n", event
->TWMessage
);
173 event
->TWMessage
= MSG_NULL
;
174 result
= TWRC_DSEVENT
;
180 pSource
->event_window
= msg
->hwnd
;
181 if (!list_empty (&pSource
->pending_messages
) &&
182 !PeekMessageW(&dummy
, msg
->hwnd
, event_message
, event_message
, PM_NOREMOVE
))
184 PostMessageW(msg
->hwnd
, event_message
, 0, 0);
191 /* DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS */
192 TW_UINT16
TWAIN_CloseDS (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
194 TW_UINT16 twRC
= TWRC_SUCCESS
;
195 pTW_IDENTITY pIdentity
= (pTW_IDENTITY
) pData
;
196 activeDS
*currentDS
= NULL
, *prevDS
= NULL
;
198 TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS\n");
200 for (currentDS
= activeSources
; currentDS
; currentDS
= currentDS
->next
) {
201 if (currentDS
->identity
.Id
== pIdentity
->Id
)
206 DSM_twCC
= TWCC_NODS
;
209 twRC
= currentDS
->dsEntry (pOrigin
, DG_CONTROL
, DAT_IDENTITY
, MSG_CLOSEDS
, pData
);
210 /* This causes crashes due to still open Windows, so leave out for now.
211 * FreeLibrary (currentDS->hmod);
214 prevDS
->next
= currentDS
->next
;
216 activeSources
= currentDS
->next
;
217 HeapFree (GetProcessHeap(), 0, currentDS
);
218 if (twRC
== TWRC_SUCCESS
)
219 DSM_twCC
= TWCC_SUCCESS
;
220 else /* FIXME: unclear how to get TWCC */
221 DSM_twCC
= TWCC_SEQERROR
;
225 /* DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT */
226 TW_UINT16
TWAIN_IdentityGetDefault (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
228 pTW_IDENTITY pSourceIdentity
= (pTW_IDENTITY
) pData
;
230 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT\n");
231 DSM_twCC
= TWCC_NODS
;
235 *pSourceIdentity
= devices
[0].identity
;
236 DSM_twCC
= TWCC_SUCCESS
;
240 /* DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST */
241 TW_UINT16
TWAIN_IdentityGetFirst (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
243 pTW_IDENTITY pSourceIdentity
= (pTW_IDENTITY
) pData
;
245 TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST\n");
248 TRACE ("no entries found.\n");
249 DSM_twCC
= TWCC_NODS
;
252 DSM_currentDevice
= 0;
253 *pSourceIdentity
= devices
[DSM_currentDevice
++].identity
;
257 /* DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT */
258 TW_UINT16
TWAIN_IdentityGetNext (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
260 pTW_IDENTITY pSourceIdentity
= (pTW_IDENTITY
) pData
;
262 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT\n");
263 if (!nrdevices
|| (DSM_currentDevice
== nrdevices
)) {
264 DSM_twCC
= TWCC_SUCCESS
;
265 return TWRC_ENDOFLIST
;
267 *pSourceIdentity
= devices
[DSM_currentDevice
++].identity
;
271 /* DG_CONTROL/DAT_IDENTITY/MSG_OPENDS */
272 TW_UINT16
TWAIN_OpenDS (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
275 pTW_IDENTITY pIdentity
= (pTW_IDENTITY
) pData
;
277 const char *modname
= NULL
;
280 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_OPENDS\n");
281 TRACE("pIdentity is %s\n", pIdentity
->ProductName
);
282 if (!DSM_initialized
) {
283 FIXME("seq error\n");
284 DSM_twCC
= TWCC_SEQERROR
;
290 DSM_twCC
= TWCC_NODS
;
294 if (pIdentity
->ProductName
[0] != '\0') {
295 /* Make sure the source to be opened exists in the device list */
296 for (i
= 0; i
<nrdevices
; i
++)
297 if (!strcmp (devices
[i
].identity
.ProductName
, pIdentity
->ProductName
))
301 } /* else use the first device */
303 /* the source is found in the device list */
304 newSource
= HeapAlloc (GetProcessHeap(), 0, sizeof (activeDS
));
306 DSM_twCC
= TWCC_LOWMEMORY
;
307 FIXME("Out of memory.\n");
310 hmod
= LoadLibraryA(devices
[i
].modname
);
312 ERR("Failed to load TWAIN Source %s\n", modname
);
313 DSM_twCC
= TWCC_OPERATIONERROR
;
314 HeapFree(GetProcessHeap(), 0, newSource
);
317 newSource
->hmod
= hmod
;
318 newSource
->dsEntry
= (DSENTRYPROC
)GetProcAddress(hmod
, "DS_Entry");
319 /* Assign id for the opened data source */
320 pIdentity
->Id
= DSM_sourceId
++;
321 if (TWRC_SUCCESS
!= newSource
->dsEntry (pOrigin
, DG_CONTROL
, DAT_IDENTITY
, MSG_OPENDS
, pIdentity
)) {
322 DSM_twCC
= TWCC_OPERATIONERROR
;
323 HeapFree(GetProcessHeap(), 0, newSource
);
327 /* add the data source to an internal active source list */
328 newSource
->next
= activeSources
;
329 newSource
->identity
.Id
= pIdentity
->Id
;
330 strcpy (newSource
->identity
.ProductName
, pIdentity
->ProductName
);
331 list_init(&newSource
->pending_messages
);
332 newSource
->ui_window
= NULL
;
333 newSource
->event_window
= NULL
;
334 activeSources
= newSource
;
335 DSM_twCC
= TWCC_SUCCESS
;
344 static INT_PTR CALLBACK
userselect_dlgproc (HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
350 userselect_data
*data
= (userselect_data
*)lparam
;
353 BOOL any_devices
= FALSE
;
355 SetWindowLongPtrW(hwnd
, DWLP_USER
, (LONG_PTR
)data
);
357 sourcelist
= GetDlgItem(hwnd
, IDC_LISTSOURCE
);
359 for (i
=0; i
<nrdevices
; i
++)
361 TW_IDENTITY
*id
= &devices
[i
].identity
;
364 if ((id
->SupportedGroups
& data
->origin
->SupportedGroups
) == 0)
367 index
= SendMessageA(sourcelist
, LB_ADDSTRING
, 0, (LPARAM
)id
->ProductName
);
368 SendMessageW(sourcelist
, LB_SETITEMDATA
, (WPARAM
)index
, (LPARAM
)i
);
374 EnableWindow(GetDlgItem(hwnd
, IDOK
), TRUE
);
376 /* FIXME: Select the supplied product name or default source. */
377 SendMessageW(sourcelist
, LB_SETCURSEL
, 0, 0);
386 if (wparam
== MAKEWPARAM(IDCANCEL
, BN_CLICKED
))
391 else if (wparam
== MAKEWPARAM(IDOK
, BN_CLICKED
) ||
392 wparam
== MAKEWPARAM(IDC_LISTSOURCE
, LBN_DBLCLK
))
394 userselect_data
*data
= (userselect_data
*)GetWindowLongPtrW(hwnd
, DWLP_USER
);
398 sourcelist
= GetDlgItem(hwnd
, IDC_LISTSOURCE
);
400 index
= SendMessageW(sourcelist
, LB_GETCURSEL
, 0, 0);
405 index
= SendMessageW(sourcelist
, LB_GETITEMDATA
, (WPARAM
)index
, 0);
407 *data
->result
= devices
[index
].identity
;
409 /* FIXME: Save this as the default source */
419 /* DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT */
420 TW_UINT16
TWAIN_UserSelect (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
422 userselect_data param
= {pOrigin
, pData
};
423 HWND parent
= DSM_parent
;
425 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT SupportedGroups=0x%x ProductName=%s\n",
426 pOrigin
->SupportedGroups
, wine_dbgstr_a(param
.result
->ProductName
));
430 if (!IsWindow(parent
))
433 if (DialogBoxParamW(DSM_hinstance
, MAKEINTRESOURCEW(DLG_USERSELECT
),
434 parent
, userselect_dlgproc
, (LPARAM
)¶m
) == 0)
437 DSM_twCC
= TWCC_SUCCESS
;
441 TRACE("<-- %s\n", wine_dbgstr_a(param
.result
->ProductName
));
442 DSM_twCC
= TWCC_SUCCESS
;
446 /* DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM */
447 TW_UINT16
TWAIN_CloseDSM (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
449 activeDS
*currentDS
= activeSources
, *nextDS
;
451 TRACE("DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM\n");
455 DSM_initialized
= FALSE
;
457 /* If there are data sources still open, close them now. */
458 while (currentDS
!= NULL
)
460 nextDS
= currentDS
->next
;
461 currentDS
->dsEntry (pOrigin
, DG_CONTROL
, DAT_IDENTITY
, MSG_CLOSEDS
, pData
);
462 HeapFree (GetProcessHeap(), 0, currentDS
);
465 activeSources
= NULL
;
467 DSM_twCC
= TWCC_SUCCESS
;
470 DSM_twCC
= TWCC_SEQERROR
;
475 /* DG_CONTROL/DAT_PARENT/MSG_OPENDSM */
476 TW_UINT16
TWAIN_OpenDSM (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
478 TW_UINT16 twRC
= TWRC_SUCCESS
;
480 TRACE("DG_CONTROL/DAT_PARENT/MSG_OPENDSM\n");
481 if (!DSM_initialized
) {
482 event_message
= RegisterWindowMessageA("WINE TWAIN_32 EVENT");
483 DSM_currentDevice
= 0;
484 DSM_initialized
= TRUE
;
485 DSM_twCC
= TWCC_SUCCESS
;
488 /* operation invoked in invalid state */
489 DSM_twCC
= TWCC_SEQERROR
;
492 DSM_parent
= (HWND
)pData
;
496 /* DG_CONTROL/DAT_STATUS/MSG_GET */
497 TW_UINT16
TWAIN_GetDSMStatus (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
499 pTW_STATUS pSourceStatus
= (pTW_STATUS
) pData
;
501 TRACE ("DG_CONTROL/DAT_STATUS/MSG_GET\n");
503 pSourceStatus
->ConditionCode
= DSM_twCC
;
504 DSM_twCC
= TWCC_SUCCESS
; /* clear the condition code */