1 /*
2 * TWAIN32 Source Manager
3 *
4 * Copyright 2000 Corel Corporation
5 * Copyright 2006 Marcus Meissner
6 *
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.
11 *
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.
16 *
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
20 */
21
22 #include <stdlib.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winuser.h"
29 #include "twain.h"
30 #include "twain_i.h"
31 #include "resource.h"
32 #include "wine/debug.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(twain);
35
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;
41
42 struct all_devices {
43 char *modname;
44 TW_IDENTITY identity;
45 };
46
47 static int nrdevices = 0;
48 static struct all_devices *devices = NULL;
49
50 static void
twain_add_onedriver(const char * dsname)51 twain_add_onedriver(const char *dsname) {
52 HMODULE hmod;
53 DSENTRYPROC dsEntry;
54 TW_IDENTITY fakeOrigin;
55 TW_IDENTITY sourceId;
56 TW_UINT16 ret;
57
58 hmod = LoadLibraryA(dsname);
59 if (!hmod) {
60 ERR("Failed to load TWAIN Source %s\n", debugstr_a(dsname));
61 return;
62 }
63 dsEntry = (DSENTRYPROC)GetProcAddress(hmod, "DS_Entry");
64 if (!dsEntry) {
65 ERR("Failed to find DS_Entry() in TWAIN DS %s\n", debugstr_a(dsname));
66 return;
67 }
68 /* Loop to do multiple detects, mostly for sane.ds and gphoto2.ds */
69 do {
70 int i;
71
72 sourceId.Id = DSM_sourceId;
73 sourceId.ProtocolMajor = TWON_PROTOCOLMAJOR;
74 sourceId.ProtocolMinor = TWON_PROTOCOLMINOR;
75 ret = dsEntry (&fakeOrigin, DG_CONTROL, DAT_IDENTITY, MSG_GET, &sourceId);
76 if (ret != TWRC_SUCCESS) {
77 ERR("Source->(DG_CONTROL,DAT_IDENTITY,MSG_GET) failed!\n");
78 break;
79 }
80 TRACE("Manufacturer: %s\n", debugstr_a(sourceId.Manufacturer));
81 TRACE("ProductFamily: %s\n", debugstr_a(sourceId.ProductFamily));
82 TRACE("ProductName: %s\n", debugstr_a(sourceId.ProductName));
83
84 for (i=0;i<nrdevices;i++) {
85 if (!strcmp(sourceId.ProductName,devices[i].identity.ProductName))
86 break;
87 }
88 if (i < nrdevices)
89 break;
90 if (nrdevices)
91 devices = HeapReAlloc(GetProcessHeap(), 0, devices, sizeof(devices[0])*(nrdevices+1));
92 else
93 devices = HeapAlloc(GetProcessHeap(), 0, sizeof(devices[0]));
94 if ((devices[nrdevices].modname = HeapAlloc(GetProcessHeap(), 0, strlen(dsname) + 1)))
95 lstrcpyA(devices[nrdevices].modname, dsname);
96 devices[nrdevices].identity = sourceId;
97 nrdevices++;
98 DSM_sourceId++;
99 } while (1);
100 FreeLibrary (hmod);
101 }
102
103 static BOOL detectionrun = FALSE;
104
105 static void
twain_autodetect(void)106 twain_autodetect(void) {
107 if (detectionrun) return;
108 detectionrun = TRUE;
109
110 twain_add_onedriver("sane.ds");
111 twain_add_onedriver("gphoto2.ds");
112 #if 0
113 twain_add_onedriver("c:\\windows\\Twain_32\\Largan\\sp503a.ds");
114 twain_add_onedriver("c:\\windows\\Twain_32\\vivicam10\\vivicam10.ds");
115 twain_add_onedriver("c:\\windows\\Twain_32\\ws30slim\\sp500a.ds");
116 #endif
117 }
118
119 /* DG_CONTROL/DAT_NULL/MSG_CLOSEDSREQ|MSG_DEVICEEVENT|MSG_XFERREADY */
TWAIN_ControlNull(pTW_IDENTITY pOrigin,pTW_IDENTITY pDest,activeDS * pSource,TW_UINT16 MSG,TW_MEMREF pData)120 TW_UINT16 TWAIN_ControlNull (pTW_IDENTITY pOrigin, pTW_IDENTITY pDest, activeDS *pSource, TW_UINT16 MSG, TW_MEMREF pData)
121 {
122 struct pending_message *message;
123
124 TRACE ("DG_CONTROL/DAT_NULL MSG=%i\n", MSG);
125
126 if (MSG != MSG_CLOSEDSREQ &&
127 MSG != MSG_DEVICEEVENT &&
128 MSG != MSG_XFERREADY)
129 {
130 DSM_twCC = TWCC_BADPROTOCOL;
131 return TWRC_FAILURE;
132 }
133
134 message = HeapAlloc(GetProcessHeap(), 0, sizeof(*message));
135 if (!message)
136 {
137 DSM_twCC = TWCC_LOWMEMORY;
138 return TWRC_FAILURE;
139 }
140
141 message->msg = MSG;
142 list_add_tail(&pSource->pending_messages, &message->entry);
143
144 /* Delphi twain only sends us messages from one window, and it
145 doesn't even give us the real handle to that window. Other
146 applications might decide to forward messages sent to DSM_parent
147 or to the one supplied to ENABLEDS. So let's try very hard to
148 find a window that will work. */
149 if (DSM_parent)
150 PostMessageW(DSM_parent, event_message, 0, 0);
151 if (pSource->ui_window && pSource->ui_window != DSM_parent)
152 PostMessageW(pSource->ui_window, event_message, 0, 0);
153 if (pSource->event_window && pSource->event_window != pSource->ui_window &&
154 pSource->event_window != DSM_parent)
155 PostMessageW(pSource->event_window, event_message, 0, 0);
156 PostMessageW(0, event_message, 0, 0);
157
158 return TWRC_SUCCESS;
159 }
160
161 /* Filters MSG_PROCESSEVENT messages before reaching the data source */
TWAIN_ProcessEvent(pTW_IDENTITY pOrigin,activeDS * pSource,TW_MEMREF pData)162 TW_UINT16 TWAIN_ProcessEvent (pTW_IDENTITY pOrigin, activeDS *pSource, TW_MEMREF pData)
163 {
164 TW_EVENT *event = (TW_EVENT*)pData;
165 MSG *msg = (MSG*)event->pEvent;
166 TW_UINT16 result = TWRC_NOTDSEVENT;
167
168 TRACE("%x,%x\n", msg->message, event_message);
169
170 if (msg->message == event_message)
171 {
172 if (!list_empty (&pSource->pending_messages))
173 {
174 struct list *entry = list_head (&pSource->pending_messages);
175 struct pending_message *message = LIST_ENTRY(entry, struct pending_message, entry);
176 event->TWMessage = message->msg;
177 list_remove (entry);
178 TRACE("<-- %x\n", event->TWMessage);
179 }
180 else
181 event->TWMessage = MSG_NULL;
182 result = TWRC_DSEVENT;
183 }
184
185 if (msg->hwnd)
186 {
187 MSG dummy;
188 pSource->event_window = msg->hwnd;
189 if (!list_empty (&pSource->pending_messages) &&
190 !PeekMessageW(&dummy, msg->hwnd, event_message, event_message, PM_NOREMOVE))
191 {
192 PostMessageW(msg->hwnd, event_message, 0, 0);
193 }
194 }
195
196 return result;
197 }
198
199 /* DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS */
TWAIN_CloseDS(pTW_IDENTITY pOrigin,TW_MEMREF pData)200 TW_UINT16 TWAIN_CloseDS (pTW_IDENTITY pOrigin, TW_MEMREF pData)
201 {
202 TW_UINT16 twRC = TWRC_SUCCESS;
203 pTW_IDENTITY pIdentity = (pTW_IDENTITY) pData;
204 activeDS *currentDS = NULL, *prevDS = NULL;
205
206 TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS\n");
207
208 for (currentDS = activeSources; currentDS; currentDS = currentDS->next) {
209 if (currentDS->identity.Id == pIdentity->Id)
210 break;
211 prevDS = currentDS;
212 }
213 if (!currentDS) {
214 DSM_twCC = TWCC_NODS;
215 return TWRC_FAILURE;
216 }
217 twRC = currentDS->dsEntry (pOrigin, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, pData);
218 /* This causes crashes due to still open Windows, so leave out for now.
219 * FreeLibrary (currentDS->hmod);
220 */
221 if (prevDS)
222 prevDS->next = currentDS->next;
223 else
224 activeSources = currentDS->next;
225 HeapFree (GetProcessHeap(), 0, currentDS);
226 if (twRC == TWRC_SUCCESS)
227 DSM_twCC = TWCC_SUCCESS;
228 else /* FIXME: unclear how to get TWCC */
229 DSM_twCC = TWCC_SEQERROR;
230 return twRC;
231 }
232
233 /* DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT */
TWAIN_IdentityGetDefault(pTW_IDENTITY pOrigin,TW_MEMREF pData)234 TW_UINT16 TWAIN_IdentityGetDefault (pTW_IDENTITY pOrigin, TW_MEMREF pData)
235 {
236 pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
237
238 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT\n");
239 DSM_twCC = TWCC_NODS;
240 twain_autodetect();
241 if (!nrdevices)
242 return TWRC_FAILURE;
243 *pSourceIdentity = devices[0].identity;
244 DSM_twCC = TWCC_SUCCESS;
245 return TWRC_SUCCESS;
246 }
247
248 /* DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST */
TWAIN_IdentityGetFirst(pTW_IDENTITY pOrigin,TW_MEMREF pData)249 TW_UINT16 TWAIN_IdentityGetFirst (pTW_IDENTITY pOrigin, TW_MEMREF pData)
250 {
251 pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
252
253 TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST\n");
254 twain_autodetect();
255 if (!nrdevices) {
256 TRACE ("no entries found.\n");
257 DSM_twCC = TWCC_NODS;
258 return TWRC_FAILURE;
259 }
260 DSM_currentDevice = 0;
261 *pSourceIdentity = devices[DSM_currentDevice++].identity;
262 return TWRC_SUCCESS;
263 }
264
265 /* DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT */
TWAIN_IdentityGetNext(pTW_IDENTITY pOrigin,TW_MEMREF pData)266 TW_UINT16 TWAIN_IdentityGetNext (pTW_IDENTITY pOrigin, TW_MEMREF pData)
267 {
268 pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
269
270 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT\n");
271 if (!nrdevices || (DSM_currentDevice == nrdevices)) {
272 DSM_twCC = TWCC_SUCCESS;
273 return TWRC_ENDOFLIST;
274 }
275 *pSourceIdentity = devices[DSM_currentDevice++].identity;
276 return TWRC_SUCCESS;
277 }
278
279 /* DG_CONTROL/DAT_IDENTITY/MSG_OPENDS */
TWAIN_OpenDS(pTW_IDENTITY pOrigin,TW_MEMREF pData)280 TW_UINT16 TWAIN_OpenDS (pTW_IDENTITY pOrigin, TW_MEMREF pData)
281 {
282 TW_UINT16 i = 0;
283 pTW_IDENTITY pIdentity = (pTW_IDENTITY) pData;
284 activeDS *newSource;
285 const char *modname = NULL;
286 HMODULE hmod;
287
288 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_OPENDS\n");
289 TRACE("pIdentity is %s\n", pIdentity->ProductName);
290 if (!DSM_initialized) {
291 FIXME("seq error\n");
292 DSM_twCC = TWCC_SEQERROR;
293 return TWRC_FAILURE;
294 }
295 twain_autodetect();
296 if (!nrdevices) {
297 FIXME("no devs.\n");
298 DSM_twCC = TWCC_NODS;
299 return TWRC_FAILURE;
300 }
301
302 if (pIdentity->ProductName[0] != '\0') {
303 /* Make sure the source to be opened exists in the device list */
304 for (i = 0; i<nrdevices; i++)
305 if (!strcmp (devices[i].identity.ProductName, pIdentity->ProductName))
306 break;
307 if (i == nrdevices)
308 i = 0;
309 } /* else use the first device */
310
311 /* the source is found in the device list */
312 newSource = HeapAlloc (GetProcessHeap(), 0, sizeof (activeDS));
313 if (!newSource) {
314 DSM_twCC = TWCC_LOWMEMORY;
315 FIXME("Out of memory.\n");
316 return TWRC_FAILURE;
317 }
318 hmod = LoadLibraryA(devices[i].modname);
319 if (!hmod) {
320 ERR("Failed to load TWAIN Source %s\n", debugstr_a(modname));
321 DSM_twCC = TWCC_OPERATIONERROR;
322 HeapFree(GetProcessHeap(), 0, newSource);
323 return TWRC_FAILURE;
324 }
325 newSource->hmod = hmod;
326 newSource->dsEntry = (DSENTRYPROC)GetProcAddress(hmod, "DS_Entry");
327 /* Assign id for the opened data source */
328 pIdentity->Id = DSM_sourceId ++;
329 if (TWRC_SUCCESS != newSource->dsEntry (pOrigin, DG_CONTROL, DAT_IDENTITY, MSG_OPENDS, pIdentity)) {
330 DSM_twCC = TWCC_OPERATIONERROR;
331 HeapFree(GetProcessHeap(), 0, newSource);
332 DSM_sourceId--;
333 return TWRC_FAILURE;
334 }
335 /* add the data source to an internal active source list */
336 newSource->next = activeSources;
337 newSource->identity.Id = pIdentity->Id;
338 strcpy (newSource->identity.ProductName, pIdentity->ProductName);
339 list_init(&newSource->pending_messages);
340 newSource->ui_window = NULL;
341 newSource->event_window = NULL;
342 activeSources = newSource;
343 DSM_twCC = TWCC_SUCCESS;
344 return TWRC_SUCCESS;
345 }
346
347 typedef struct {
348 pTW_IDENTITY origin;
349 pTW_IDENTITY result;
350 } userselect_data;
351
userselect_dlgproc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam)352 static INT_PTR CALLBACK userselect_dlgproc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
353 {
354 switch (msg)
355 {
356 case WM_INITDIALOG:
357 {
358 userselect_data *data = (userselect_data*)lparam;
359 int i;
360 HWND sourcelist;
361 BOOL any_devices = FALSE;
362
363 SetWindowLongPtrW(hwnd, DWLP_USER, (LONG_PTR)data);
364
365 sourcelist = GetDlgItem(hwnd, IDC_LISTSOURCE);
366
367 for (i=0; i<nrdevices; i++)
368 {
369 TW_IDENTITY *id = &devices[i].identity;
370 LRESULT index;
371
372 if ((id->SupportedGroups & data->origin->SupportedGroups) == 0)
373 continue;
374
375 index = SendMessageA(sourcelist, LB_ADDSTRING, 0, (LPARAM)id->ProductName);
376 SendMessageW(sourcelist, LB_SETITEMDATA, (WPARAM)index, (LPARAM)i);
377 any_devices = TRUE;
378 }
379
380 if (any_devices)
381 {
382 EnableWindow(GetDlgItem(hwnd, IDOK), TRUE);
383
384 /* FIXME: Select the supplied product name or default source. */
385 SendMessageW(sourcelist, LB_SETCURSEL, 0, 0);
386 }
387
388 return TRUE;
389 }
390 case WM_CLOSE:
391 EndDialog(hwnd, 0);
392 return TRUE;
393 case WM_COMMAND:
394 if (wparam == MAKEWPARAM(IDCANCEL, BN_CLICKED))
395 {
396 EndDialog(hwnd, 0);
397 return TRUE;
398 }
399 else if (wparam == MAKEWPARAM(IDOK, BN_CLICKED) ||
400 wparam == MAKEWPARAM(IDC_LISTSOURCE, LBN_DBLCLK))
401 {
402 userselect_data *data = (userselect_data*)GetWindowLongPtrW(hwnd, DWLP_USER);
403 HWND sourcelist;
404 LRESULT index;
405
406 sourcelist = GetDlgItem(hwnd, IDC_LISTSOURCE);
407
408 index = SendMessageW(sourcelist, LB_GETCURSEL, 0, 0);
409
410 if (index == LB_ERR)
411 return TRUE;
412
413 index = SendMessageW(sourcelist, LB_GETITEMDATA, (WPARAM)index, 0);
414
415 *data->result = devices[index].identity;
416
417 /* FIXME: Save this as the default source */
418
419 EndDialog(hwnd, 1);
420 return TRUE;
421 }
422 break;
423 }
424 return FALSE;
425 }
426
427 /* DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT */
TWAIN_UserSelect(pTW_IDENTITY pOrigin,TW_MEMREF pData)428 TW_UINT16 TWAIN_UserSelect (pTW_IDENTITY pOrigin, TW_MEMREF pData)
429 {
430 userselect_data param = {pOrigin, pData};
431 HWND parent = DSM_parent;
432
433 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT SupportedGroups=0x%x ProductName=%s\n",
434 pOrigin->SupportedGroups, wine_dbgstr_a(param.result->ProductName));
435
436 twain_autodetect();
437
438 if (!IsWindow(parent))
439 parent = NULL;
440
441 if (DialogBoxParamW(DSM_hinstance, MAKEINTRESOURCEW(DLG_USERSELECT),
442 parent, userselect_dlgproc, (LPARAM)¶m) == 0)
443 {
444 TRACE("canceled\n");
445 DSM_twCC = TWCC_SUCCESS;
446 return TWRC_CANCEL;
447 }
448
449 TRACE("<-- %s\n", wine_dbgstr_a(param.result->ProductName));
450 DSM_twCC = TWCC_SUCCESS;
451 return TWRC_SUCCESS;
452 }
453
454 /* DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM */
TWAIN_CloseDSM(pTW_IDENTITY pOrigin,TW_MEMREF pData)455 TW_UINT16 TWAIN_CloseDSM (pTW_IDENTITY pOrigin, TW_MEMREF pData)
456 {
457 activeDS *currentDS = activeSources, *nextDS;
458
459 TRACE("DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM\n");
460
461 if (DSM_initialized)
462 {
463 DSM_initialized = FALSE;
464
465 /* If there are data sources still open, close them now. */
466 while (currentDS != NULL)
467 {
468 nextDS = currentDS->next;
469 currentDS->dsEntry (pOrigin, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, pData);
470 HeapFree (GetProcessHeap(), 0, currentDS);
471 currentDS = nextDS;
472 }
473 activeSources = NULL;
474 DSM_parent = NULL;
475 DSM_twCC = TWCC_SUCCESS;
476 return TWRC_SUCCESS;
477 } else {
478 DSM_twCC = TWCC_SEQERROR;
479 return TWRC_FAILURE;
480 }
481 }
482
483 /* DG_CONTROL/DAT_PARENT/MSG_OPENDSM */
TWAIN_OpenDSM(pTW_IDENTITY pOrigin,TW_MEMREF pData)484 TW_UINT16 TWAIN_OpenDSM (pTW_IDENTITY pOrigin, TW_MEMREF pData)
485 {
486 TW_UINT16 twRC = TWRC_SUCCESS;
487
488 TRACE("DG_CONTROL/DAT_PARENT/MSG_OPENDSM\n");
489 if (!DSM_initialized) {
490 event_message = RegisterWindowMessageA("WINE TWAIN_32 EVENT");
491 DSM_currentDevice = 0;
492 DSM_initialized = TRUE;
493 DSM_twCC = TWCC_SUCCESS;
494 twRC = TWRC_SUCCESS;
495 } else {
496 /* operation invoked in invalid state */
497 DSM_twCC = TWCC_SEQERROR;
498 twRC = TWRC_FAILURE;
499 }
500 DSM_parent = (HWND)pData;
501 return twRC;
502 }
503
504 /* DG_CONTROL/DAT_STATUS/MSG_GET */
TWAIN_GetDSMStatus(pTW_IDENTITY pOrigin,TW_MEMREF pData)505 TW_UINT16 TWAIN_GetDSMStatus (pTW_IDENTITY pOrigin, TW_MEMREF pData)
506 {
507 pTW_STATUS pSourceStatus = (pTW_STATUS) pData;
508
509 TRACE ("DG_CONTROL/DAT_STATUS/MSG_GET\n");
510
511 pSourceStatus->ConditionCode = DSM_twCC;
512 DSM_twCC = TWCC_SUCCESS; /* clear the condition code */
513 return TWRC_SUCCESS;
514 }
515