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
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(twain
);
38 static TW_UINT16 DSM_initialized
; /* whether Source Manager is initialized */
39 static TW_UINT32 DSM_sourceId
; /* source id generator */
40 static TW_UINT16 DSM_currentDevice
; /* keep track of device during enumeration */
41 static HWND DSM_parent
;
42 static UINT event_message
;
49 static int nrdevices
= 0;
50 static struct all_devices
*devices
= NULL
;
53 twain_add_onedriver(const char *dsname
) {
56 TW_IDENTITY fakeOrigin
;
60 hmod
= LoadLibraryA(dsname
);
62 ERR("Failed to load TWAIN Source %s\n", dsname
);
65 dsEntry
= (DSENTRYPROC
)GetProcAddress(hmod
, "DS_Entry");
67 ERR("Failed to find DS_Entry() in TWAIN DS %s\n", dsname
);
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
))
93 devices
= HeapReAlloc(GetProcessHeap(), 0, devices
, sizeof(devices
[0])*(nrdevices
+1));
95 devices
= HeapAlloc(GetProcessHeap(), 0, sizeof(devices
[0]));
96 if ((devices
[nrdevices
].modname
= HeapAlloc(GetProcessHeap(), 0, strlen(dsname
) + 1)))
97 lstrcpyA(devices
[nrdevices
].modname
, dsname
);
98 devices
[nrdevices
].identity
= sourceId
;
105 static BOOL detectionrun
= FALSE
;
108 twain_autodetect(void) {
109 if (detectionrun
) return;
112 twain_add_onedriver("sane.ds");
113 twain_add_onedriver("gphoto2.ds");
115 twain_add_onedriver("c:\\windows\\Twain_32\\Largan\\sp503a.ds");
116 twain_add_onedriver("c:\\windows\\Twain_32\\vivicam10\\vivicam10.ds");
117 twain_add_onedriver("c:\\windows\\Twain_32\\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
;
287 const char *modname
= NULL
;
290 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_OPENDS\n");
291 TRACE("pIdentity is %s\n", pIdentity
->ProductName
);
292 if (!DSM_initialized
) {
293 FIXME("seq error\n");
294 DSM_twCC
= TWCC_SEQERROR
;
300 DSM_twCC
= TWCC_NODS
;
304 if (pIdentity
->ProductName
[0] != '\0') {
305 /* Make sure the source to be opened exists in the device list */
306 for (i
= 0; i
<nrdevices
; i
++)
307 if (!strcmp (devices
[i
].identity
.ProductName
, pIdentity
->ProductName
))
311 } /* else use the first device */
313 /* the source is found in the device list */
314 newSource
= HeapAlloc (GetProcessHeap(), 0, sizeof (activeDS
));
316 DSM_twCC
= TWCC_LOWMEMORY
;
317 FIXME("Out of memory.\n");
320 hmod
= LoadLibraryA(devices
[i
].modname
);
322 ERR("Failed to load TWAIN Source %s\n", modname
);
323 DSM_twCC
= TWCC_OPERATIONERROR
;
324 HeapFree(GetProcessHeap(), 0, newSource
);
327 newSource
->hmod
= hmod
;
328 newSource
->dsEntry
= (DSENTRYPROC
)GetProcAddress(hmod
, "DS_Entry");
329 /* Assign id for the opened data source */
330 pIdentity
->Id
= DSM_sourceId
++;
331 if (TWRC_SUCCESS
!= newSource
->dsEntry (pOrigin
, DG_CONTROL
, DAT_IDENTITY
, MSG_OPENDS
, pIdentity
)) {
332 DSM_twCC
= TWCC_OPERATIONERROR
;
333 HeapFree(GetProcessHeap(), 0, newSource
);
337 /* add the data source to an internal active source list */
338 newSource
->next
= activeSources
;
339 newSource
->identity
.Id
= pIdentity
->Id
;
340 strcpy (newSource
->identity
.ProductName
, pIdentity
->ProductName
);
341 list_init(&newSource
->pending_messages
);
342 newSource
->ui_window
= NULL
;
343 newSource
->event_window
= NULL
;
344 activeSources
= newSource
;
345 DSM_twCC
= TWCC_SUCCESS
;
354 static INT_PTR CALLBACK
userselect_dlgproc (HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
360 userselect_data
*data
= (userselect_data
*)lparam
;
363 BOOL any_devices
= FALSE
;
365 SetWindowLongPtrW(hwnd
, DWLP_USER
, (LONG_PTR
)data
);
367 sourcelist
= GetDlgItem(hwnd
, IDC_LISTSOURCE
);
369 for (i
=0; i
<nrdevices
; i
++)
371 TW_IDENTITY
*id
= &devices
[i
].identity
;
374 if ((id
->SupportedGroups
& data
->origin
->SupportedGroups
) == 0)
377 index
= SendMessageA(sourcelist
, LB_ADDSTRING
, 0, (LPARAM
)id
->ProductName
);
378 SendMessageW(sourcelist
, LB_SETITEMDATA
, (WPARAM
)index
, (LPARAM
)i
);
384 EnableWindow(GetDlgItem(hwnd
, IDOK
), TRUE
);
386 /* FIXME: Select the supplied product name or default source. */
387 SendMessageW(sourcelist
, LB_SETCURSEL
, 0, 0);
396 if (wparam
== MAKEWPARAM(IDCANCEL
, BN_CLICKED
))
401 else if (wparam
== MAKEWPARAM(IDOK
, BN_CLICKED
) ||
402 wparam
== MAKEWPARAM(IDC_LISTSOURCE
, LBN_DBLCLK
))
404 userselect_data
*data
= (userselect_data
*)GetWindowLongPtrW(hwnd
, DWLP_USER
);
408 sourcelist
= GetDlgItem(hwnd
, IDC_LISTSOURCE
);
410 index
= SendMessageW(sourcelist
, LB_GETCURSEL
, 0, 0);
415 index
= SendMessageW(sourcelist
, LB_GETITEMDATA
, (WPARAM
)index
, 0);
417 *data
->result
= devices
[index
].identity
;
419 /* FIXME: Save this as the default source */
429 /* DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT */
430 TW_UINT16
TWAIN_UserSelect (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
432 userselect_data param
= {pOrigin
, pData
};
433 HWND parent
= DSM_parent
;
435 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT SupportedGroups=0x%x ProductName=%s\n",
436 pOrigin
->SupportedGroups
, wine_dbgstr_a(param
.result
->ProductName
));
440 if (!IsWindow(parent
))
443 if (DialogBoxParamW(DSM_hinstance
, MAKEINTRESOURCEW(DLG_USERSELECT
),
444 parent
, userselect_dlgproc
, (LPARAM
)¶m
) == 0)
447 DSM_twCC
= TWCC_SUCCESS
;
451 TRACE("<-- %s\n", wine_dbgstr_a(param
.result
->ProductName
));
452 DSM_twCC
= TWCC_SUCCESS
;
456 /* DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM */
457 TW_UINT16
TWAIN_CloseDSM (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
459 activeDS
*currentDS
= activeSources
, *nextDS
;
461 TRACE("DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM\n");
465 DSM_initialized
= FALSE
;
467 /* If there are data sources still open, close them now. */
468 while (currentDS
!= NULL
)
470 nextDS
= currentDS
->next
;
471 currentDS
->dsEntry (pOrigin
, DG_CONTROL
, DAT_IDENTITY
, MSG_CLOSEDS
, pData
);
472 HeapFree (GetProcessHeap(), 0, currentDS
);
475 activeSources
= NULL
;
477 DSM_twCC
= TWCC_SUCCESS
;
480 DSM_twCC
= TWCC_SEQERROR
;
485 /* DG_CONTROL/DAT_PARENT/MSG_OPENDSM */
486 TW_UINT16
TWAIN_OpenDSM (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
488 TW_UINT16 twRC
= TWRC_SUCCESS
;
490 TRACE("DG_CONTROL/DAT_PARENT/MSG_OPENDSM\n");
491 if (!DSM_initialized
) {
492 event_message
= RegisterWindowMessageA("WINE TWAIN_32 EVENT");
493 DSM_currentDevice
= 0;
494 DSM_initialized
= TRUE
;
495 DSM_twCC
= TWCC_SUCCESS
;
498 /* operation invoked in invalid state */
499 DSM_twCC
= TWCC_SEQERROR
;
502 DSM_parent
= (HWND
)pData
;
506 /* DG_CONTROL/DAT_STATUS/MSG_GET */
507 TW_UINT16
TWAIN_GetDSMStatus (pTW_IDENTITY pOrigin
, TW_MEMREF pData
)
509 pTW_STATUS pSourceStatus
= (pTW_STATUS
) pData
;
511 TRACE ("DG_CONTROL/DAT_STATUS/MSG_GET\n");
513 pSourceStatus
->ConditionCode
= DSM_twCC
;
514 DSM_twCC
= TWCC_SUCCESS
; /* clear the condition code */