0b2098654f84bb622d9d64921e3080cab5b20551
[reactos.git] / dll / win32 / twain_32 / dsm_ctrl.c
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 "config.h"
23
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <stdio.h>
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winuser.h"
31 #include "twain.h"
32 #include "twain_i.h"
33 #include "resource.h"
34 #include "wine/debug.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(twain);
37
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;
43
44 struct all_devices {
45 char *modname;
46 TW_IDENTITY identity;
47 };
48
49 static int nrdevices = 0;
50 static struct all_devices *devices = NULL;
51
52 static void
53 twain_add_onedriver(const char *dsname) {
54 HMODULE hmod;
55 DSENTRYPROC dsEntry;
56 TW_IDENTITY fakeOrigin;
57 TW_IDENTITY sourceId;
58 TW_UINT16 ret;
59
60 hmod = LoadLibraryA(dsname);
61 if (!hmod) {
62 ERR("Failed to load TWAIN Source %s\n", dsname);
63 return;
64 }
65 dsEntry = (DSENTRYPROC)GetProcAddress(hmod, "DS_Entry");
66 if (!dsEntry) {
67 ERR("Failed to find DS_Entry() in TWAIN DS %s\n", dsname);
68 return;
69 }
70 /* Loop to do multiple detects, mostly for sane.ds and gphoto2.ds */
71 do {
72 int i;
73
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");
80 break;
81 }
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));
85
86 for (i=0;i<nrdevices;i++) {
87 if (!strcmp(sourceId.ProductName,devices[i].identity.ProductName))
88 break;
89 }
90 if (i < nrdevices)
91 break;
92 if (nrdevices)
93 devices = HeapReAlloc(GetProcessHeap(), 0, devices, sizeof(devices[0])*(nrdevices+1));
94 else
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;
99 nrdevices++;
100 DSM_sourceId++;
101 } while (1);
102 FreeLibrary (hmod);
103 }
104
105 static BOOL detectionrun = FALSE;
106
107 static void
108 twain_autodetect(void) {
109 if (detectionrun) return;
110 detectionrun = TRUE;
111
112 twain_add_onedriver("sane.ds");
113 twain_add_onedriver("gphoto2.ds");
114 #if 0
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");
118 #endif
119 }
120
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)
123 {
124 struct pending_message *message;
125
126 TRACE ("DG_CONTROL/DAT_NULL MSG=%i\n", MSG);
127
128 if (MSG != MSG_CLOSEDSREQ &&
129 MSG != MSG_DEVICEEVENT &&
130 MSG != MSG_XFERREADY)
131 {
132 DSM_twCC = TWCC_BADPROTOCOL;
133 return TWRC_FAILURE;
134 }
135
136 message = HeapAlloc(GetProcessHeap(), 0, sizeof(*message));
137 if (!message)
138 {
139 DSM_twCC = TWCC_LOWMEMORY;
140 return TWRC_FAILURE;
141 }
142
143 message->msg = MSG;
144 list_add_tail(&pSource->pending_messages, &message->entry);
145
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. */
151 if (DSM_parent)
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);
159
160 return TWRC_SUCCESS;
161 }
162
163 /* Filters MSG_PROCESSEVENT messages before reaching the data source */
164 TW_UINT16 TWAIN_ProcessEvent (pTW_IDENTITY pOrigin, activeDS *pSource, TW_MEMREF pData)
165 {
166 TW_EVENT *event = (TW_EVENT*)pData;
167 MSG *msg = (MSG*)event->pEvent;
168 TW_UINT16 result = TWRC_NOTDSEVENT;
169
170 TRACE("%x,%x\n", msg->message, event_message);
171
172 if (msg->message == event_message)
173 {
174 if (!list_empty (&pSource->pending_messages))
175 {
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;
179 list_remove (entry);
180 TRACE("<-- %x\n", event->TWMessage);
181 }
182 else
183 event->TWMessage = MSG_NULL;
184 result = TWRC_DSEVENT;
185 }
186
187 if (msg->hwnd)
188 {
189 MSG dummy;
190 pSource->event_window = msg->hwnd;
191 if (!list_empty (&pSource->pending_messages) &&
192 !PeekMessageW(&dummy, msg->hwnd, event_message, event_message, PM_NOREMOVE))
193 {
194 PostMessageW(msg->hwnd, event_message, 0, 0);
195 }
196 }
197
198 return result;
199 }
200
201 /* DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS */
202 TW_UINT16 TWAIN_CloseDS (pTW_IDENTITY pOrigin, TW_MEMREF pData)
203 {
204 TW_UINT16 twRC = TWRC_SUCCESS;
205 pTW_IDENTITY pIdentity = (pTW_IDENTITY) pData;
206 activeDS *currentDS = NULL, *prevDS = NULL;
207
208 TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS\n");
209
210 for (currentDS = activeSources; currentDS; currentDS = currentDS->next) {
211 if (currentDS->identity.Id == pIdentity->Id)
212 break;
213 prevDS = currentDS;
214 }
215 if (!currentDS) {
216 DSM_twCC = TWCC_NODS;
217 return TWRC_FAILURE;
218 }
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);
222 */
223 if (prevDS)
224 prevDS->next = currentDS->next;
225 else
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;
232 return twRC;
233 }
234
235 /* DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT */
236 TW_UINT16 TWAIN_IdentityGetDefault (pTW_IDENTITY pOrigin, TW_MEMREF pData)
237 {
238 pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
239
240 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT\n");
241 DSM_twCC = TWCC_NODS;
242 twain_autodetect();
243 if (!nrdevices)
244 return TWRC_FAILURE;
245 *pSourceIdentity = devices[0].identity;
246 DSM_twCC = TWCC_SUCCESS;
247 return TWRC_SUCCESS;
248 }
249
250 /* DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST */
251 TW_UINT16 TWAIN_IdentityGetFirst (pTW_IDENTITY pOrigin, TW_MEMREF pData)
252 {
253 pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
254
255 TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST\n");
256 twain_autodetect();
257 if (!nrdevices) {
258 TRACE ("no entries found.\n");
259 DSM_twCC = TWCC_NODS;
260 return TWRC_FAILURE;
261 }
262 DSM_currentDevice = 0;
263 *pSourceIdentity = devices[DSM_currentDevice++].identity;
264 return TWRC_SUCCESS;
265 }
266
267 /* DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT */
268 TW_UINT16 TWAIN_IdentityGetNext (pTW_IDENTITY pOrigin, TW_MEMREF pData)
269 {
270 pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
271
272 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT\n");
273 if (!nrdevices || (DSM_currentDevice == nrdevices)) {
274 DSM_twCC = TWCC_SUCCESS;
275 return TWRC_ENDOFLIST;
276 }
277 *pSourceIdentity = devices[DSM_currentDevice++].identity;
278 return TWRC_SUCCESS;
279 }
280
281 /* DG_CONTROL/DAT_IDENTITY/MSG_OPENDS */
282 TW_UINT16 TWAIN_OpenDS (pTW_IDENTITY pOrigin, TW_MEMREF pData)
283 {
284 TW_UINT16 i = 0;
285 pTW_IDENTITY pIdentity = (pTW_IDENTITY) pData;
286 activeDS *newSource;
287 const char *modname = NULL;
288 HMODULE hmod;
289
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;
295 return TWRC_FAILURE;
296 }
297 twain_autodetect();
298 if (!nrdevices) {
299 FIXME("no devs.\n");
300 DSM_twCC = TWCC_NODS;
301 return TWRC_FAILURE;
302 }
303
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))
308 break;
309 if (i == nrdevices)
310 i = 0;
311 } /* else use the first device */
312
313 /* the source is found in the device list */
314 newSource = HeapAlloc (GetProcessHeap(), 0, sizeof (activeDS));
315 if (!newSource) {
316 DSM_twCC = TWCC_LOWMEMORY;
317 FIXME("Out of memory.\n");
318 return TWRC_FAILURE;
319 }
320 hmod = LoadLibraryA(devices[i].modname);
321 if (!hmod) {
322 ERR("Failed to load TWAIN Source %s\n", modname);
323 DSM_twCC = TWCC_OPERATIONERROR;
324 HeapFree(GetProcessHeap(), 0, newSource);
325 return TWRC_FAILURE;
326 }
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);
334 DSM_sourceId--;
335 return TWRC_FAILURE;
336 }
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;
346 return TWRC_SUCCESS;
347 }
348
349 typedef struct {
350 pTW_IDENTITY origin;
351 pTW_IDENTITY result;
352 } userselect_data;
353
354 static INT_PTR CALLBACK userselect_dlgproc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
355 {
356 switch (msg)
357 {
358 case WM_INITDIALOG:
359 {
360 userselect_data *data = (userselect_data*)lparam;
361 int i;
362 HWND sourcelist;
363 BOOL any_devices = FALSE;
364
365 SetWindowLongPtrW(hwnd, DWLP_USER, (LONG_PTR)data);
366
367 sourcelist = GetDlgItem(hwnd, IDC_LISTSOURCE);
368
369 for (i=0; i<nrdevices; i++)
370 {
371 TW_IDENTITY *id = &devices[i].identity;
372 LRESULT index;
373
374 if ((id->SupportedGroups & data->origin->SupportedGroups) == 0)
375 continue;
376
377 index = SendMessageA(sourcelist, LB_ADDSTRING, 0, (LPARAM)id->ProductName);
378 SendMessageW(sourcelist, LB_SETITEMDATA, (WPARAM)index, (LPARAM)i);
379 any_devices = TRUE;
380 }
381
382 if (any_devices)
383 {
384 EnableWindow(GetDlgItem(hwnd, IDOK), TRUE);
385
386 /* FIXME: Select the supplied product name or default source. */
387 SendMessageW(sourcelist, LB_SETCURSEL, 0, 0);
388 }
389
390 return TRUE;
391 }
392 case WM_CLOSE:
393 EndDialog(hwnd, 0);
394 return TRUE;
395 case WM_COMMAND:
396 if (wparam == MAKEWPARAM(IDCANCEL, BN_CLICKED))
397 {
398 EndDialog(hwnd, 0);
399 return TRUE;
400 }
401 else if (wparam == MAKEWPARAM(IDOK, BN_CLICKED) ||
402 wparam == MAKEWPARAM(IDC_LISTSOURCE, LBN_DBLCLK))
403 {
404 userselect_data *data = (userselect_data*)GetWindowLongPtrW(hwnd, DWLP_USER);
405 HWND sourcelist;
406 LRESULT index;
407
408 sourcelist = GetDlgItem(hwnd, IDC_LISTSOURCE);
409
410 index = SendMessageW(sourcelist, LB_GETCURSEL, 0, 0);
411
412 if (index == LB_ERR)
413 return TRUE;
414
415 index = SendMessageW(sourcelist, LB_GETITEMDATA, (WPARAM)index, 0);
416
417 *data->result = devices[index].identity;
418
419 /* FIXME: Save this as the default source */
420
421 EndDialog(hwnd, 1);
422 return TRUE;
423 }
424 break;
425 }
426 return FALSE;
427 }
428
429 /* DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT */
430 TW_UINT16 TWAIN_UserSelect (pTW_IDENTITY pOrigin, TW_MEMREF pData)
431 {
432 userselect_data param = {pOrigin, pData};
433 HWND parent = DSM_parent;
434
435 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT SupportedGroups=0x%x ProductName=%s\n",
436 pOrigin->SupportedGroups, wine_dbgstr_a(param.result->ProductName));
437
438 twain_autodetect();
439
440 if (!IsWindow(parent))
441 parent = NULL;
442
443 if (DialogBoxParamW(DSM_hinstance, MAKEINTRESOURCEW(DLG_USERSELECT),
444 parent, userselect_dlgproc, (LPARAM)&param) == 0)
445 {
446 TRACE("canceled\n");
447 DSM_twCC = TWCC_SUCCESS;
448 return TWRC_CANCEL;
449 }
450
451 TRACE("<-- %s\n", wine_dbgstr_a(param.result->ProductName));
452 DSM_twCC = TWCC_SUCCESS;
453 return TWRC_SUCCESS;
454 }
455
456 /* DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM */
457 TW_UINT16 TWAIN_CloseDSM (pTW_IDENTITY pOrigin, TW_MEMREF pData)
458 {
459 activeDS *currentDS = activeSources, *nextDS;
460
461 TRACE("DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM\n");
462
463 if (DSM_initialized)
464 {
465 DSM_initialized = FALSE;
466
467 /* If there are data sources still open, close them now. */
468 while (currentDS != NULL)
469 {
470 nextDS = currentDS->next;
471 currentDS->dsEntry (pOrigin, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, pData);
472 HeapFree (GetProcessHeap(), 0, currentDS);
473 currentDS = nextDS;
474 }
475 activeSources = NULL;
476 DSM_parent = NULL;
477 DSM_twCC = TWCC_SUCCESS;
478 return TWRC_SUCCESS;
479 } else {
480 DSM_twCC = TWCC_SEQERROR;
481 return TWRC_FAILURE;
482 }
483 }
484
485 /* DG_CONTROL/DAT_PARENT/MSG_OPENDSM */
486 TW_UINT16 TWAIN_OpenDSM (pTW_IDENTITY pOrigin, TW_MEMREF pData)
487 {
488 TW_UINT16 twRC = TWRC_SUCCESS;
489
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;
496 twRC = TWRC_SUCCESS;
497 } else {
498 /* operation invoked in invalid state */
499 DSM_twCC = TWCC_SEQERROR;
500 twRC = TWRC_FAILURE;
501 }
502 DSM_parent = (HWND)pData;
503 return twRC;
504 }
505
506 /* DG_CONTROL/DAT_STATUS/MSG_GET */
507 TW_UINT16 TWAIN_GetDSMStatus (pTW_IDENTITY pOrigin, TW_MEMREF pData)
508 {
509 pTW_STATUS pSourceStatus = (pTW_STATUS) pData;
510
511 TRACE ("DG_CONTROL/DAT_STATUS/MSG_GET\n");
512
513 pSourceStatus->ConditionCode = DSM_twCC;
514 DSM_twCC = TWCC_SUCCESS; /* clear the condition code */
515 return TWRC_SUCCESS;
516 }