[TWAIN_32] Sync with Wine 3.0. CORE-14225
[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 "twain_i.h"
23
24 #include <winuser.h>
25
26 #include "resource.h"
27
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;
33
34 struct all_devices {
35 char *modname;
36 TW_IDENTITY identity;
37 };
38
39 static int nrdevices = 0;
40 static struct all_devices *devices = NULL;
41
42 static void
43 twain_add_onedriver(const char *dsname) {
44 HMODULE hmod;
45 DSENTRYPROC dsEntry;
46 TW_IDENTITY fakeOrigin;
47 TW_IDENTITY sourceId;
48 TW_UINT16 ret;
49
50 hmod = LoadLibraryA(dsname);
51 if (!hmod) {
52 ERR("Failed to load TWAIN Source %s\n", dsname);
53 return;
54 }
55 dsEntry = (DSENTRYPROC)GetProcAddress(hmod, "DS_Entry");
56 if (!dsEntry) {
57 ERR("Failed to find DS_Entry() in TWAIN DS %s\n", dsname);
58 return;
59 }
60 /* Loop to do multiple detects, mostly for sane.ds and gphoto2.ds */
61 do {
62 int i;
63
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");
70 break;
71 }
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));
75
76 for (i=0;i<nrdevices;i++) {
77 if (!strcmp(sourceId.ProductName,devices[i].identity.ProductName))
78 break;
79 }
80 if (i < nrdevices)
81 break;
82 if (nrdevices)
83 devices = HeapReAlloc(GetProcessHeap(), 0, devices, sizeof(devices[0])*(nrdevices+1));
84 else
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;
89 nrdevices++;
90 DSM_sourceId++;
91 } while (1);
92 FreeLibrary (hmod);
93 }
94
95 static BOOL detectionrun = FALSE;
96
97 static void
98 twain_autodetect(void) {
99 if (detectionrun) return;
100 detectionrun = TRUE;
101
102 twain_add_onedriver("sane.ds");
103 twain_add_onedriver("gphoto2.ds");
104 #if 0
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");
108 #endif
109 }
110
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)
113 {
114 struct pending_message *message;
115
116 TRACE ("DG_CONTROL/DAT_NULL MSG=%i\n", MSG);
117
118 if (MSG != MSG_CLOSEDSREQ &&
119 MSG != MSG_DEVICEEVENT &&
120 MSG != MSG_XFERREADY)
121 {
122 DSM_twCC = TWCC_BADPROTOCOL;
123 return TWRC_FAILURE;
124 }
125
126 message = HeapAlloc(GetProcessHeap(), 0, sizeof(*message));
127 if (!message)
128 {
129 DSM_twCC = TWCC_LOWMEMORY;
130 return TWRC_FAILURE;
131 }
132
133 message->msg = MSG;
134 list_add_tail(&pSource->pending_messages, &message->entry);
135
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. */
141 if (DSM_parent)
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);
149
150 return TWRC_SUCCESS;
151 }
152
153 /* Filters MSG_PROCESSEVENT messages before reaching the data source */
154 TW_UINT16 TWAIN_ProcessEvent (pTW_IDENTITY pOrigin, activeDS *pSource, TW_MEMREF pData)
155 {
156 TW_EVENT *event = (TW_EVENT*)pData;
157 MSG *msg = (MSG*)event->pEvent;
158 TW_UINT16 result = TWRC_NOTDSEVENT;
159
160 TRACE("%x,%x\n", msg->message, event_message);
161
162 if (msg->message == event_message)
163 {
164 if (!list_empty (&pSource->pending_messages))
165 {
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;
169 list_remove (entry);
170 TRACE("<-- %x\n", event->TWMessage);
171 }
172 else
173 event->TWMessage = MSG_NULL;
174 result = TWRC_DSEVENT;
175 }
176
177 if (msg->hwnd)
178 {
179 MSG dummy;
180 pSource->event_window = msg->hwnd;
181 if (!list_empty (&pSource->pending_messages) &&
182 !PeekMessageW(&dummy, msg->hwnd, event_message, event_message, PM_NOREMOVE))
183 {
184 PostMessageW(msg->hwnd, event_message, 0, 0);
185 }
186 }
187
188 return result;
189 }
190
191 /* DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS */
192 TW_UINT16 TWAIN_CloseDS (pTW_IDENTITY pOrigin, TW_MEMREF pData)
193 {
194 TW_UINT16 twRC = TWRC_SUCCESS;
195 pTW_IDENTITY pIdentity = (pTW_IDENTITY) pData;
196 activeDS *currentDS = NULL, *prevDS = NULL;
197
198 TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS\n");
199
200 for (currentDS = activeSources; currentDS; currentDS = currentDS->next) {
201 if (currentDS->identity.Id == pIdentity->Id)
202 break;
203 prevDS = currentDS;
204 }
205 if (!currentDS) {
206 DSM_twCC = TWCC_NODS;
207 return TWRC_FAILURE;
208 }
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);
212 */
213 if (prevDS)
214 prevDS->next = currentDS->next;
215 else
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;
222 return twRC;
223 }
224
225 /* DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT */
226 TW_UINT16 TWAIN_IdentityGetDefault (pTW_IDENTITY pOrigin, TW_MEMREF pData)
227 {
228 pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
229
230 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT\n");
231 DSM_twCC = TWCC_NODS;
232 twain_autodetect();
233 if (!nrdevices)
234 return TWRC_FAILURE;
235 *pSourceIdentity = devices[0].identity;
236 DSM_twCC = TWCC_SUCCESS;
237 return TWRC_SUCCESS;
238 }
239
240 /* DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST */
241 TW_UINT16 TWAIN_IdentityGetFirst (pTW_IDENTITY pOrigin, TW_MEMREF pData)
242 {
243 pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
244
245 TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST\n");
246 twain_autodetect();
247 if (!nrdevices) {
248 TRACE ("no entries found.\n");
249 DSM_twCC = TWCC_NODS;
250 return TWRC_FAILURE;
251 }
252 DSM_currentDevice = 0;
253 *pSourceIdentity = devices[DSM_currentDevice++].identity;
254 return TWRC_SUCCESS;
255 }
256
257 /* DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT */
258 TW_UINT16 TWAIN_IdentityGetNext (pTW_IDENTITY pOrigin, TW_MEMREF pData)
259 {
260 pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
261
262 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT\n");
263 if (!nrdevices || (DSM_currentDevice == nrdevices)) {
264 DSM_twCC = TWCC_SUCCESS;
265 return TWRC_ENDOFLIST;
266 }
267 *pSourceIdentity = devices[DSM_currentDevice++].identity;
268 return TWRC_SUCCESS;
269 }
270
271 /* DG_CONTROL/DAT_IDENTITY/MSG_OPENDS */
272 TW_UINT16 TWAIN_OpenDS (pTW_IDENTITY pOrigin, TW_MEMREF pData)
273 {
274 TW_UINT16 i = 0;
275 pTW_IDENTITY pIdentity = (pTW_IDENTITY) pData;
276 activeDS *newSource;
277 const char *modname = NULL;
278 HMODULE hmod;
279
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;
285 return TWRC_FAILURE;
286 }
287 twain_autodetect();
288 if (!nrdevices) {
289 FIXME("no devs.\n");
290 DSM_twCC = TWCC_NODS;
291 return TWRC_FAILURE;
292 }
293
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))
298 break;
299 if (i == nrdevices)
300 i = 0;
301 } /* else use the first device */
302
303 /* the source is found in the device list */
304 newSource = HeapAlloc (GetProcessHeap(), 0, sizeof (activeDS));
305 if (!newSource) {
306 DSM_twCC = TWCC_LOWMEMORY;
307 FIXME("Out of memory.\n");
308 return TWRC_FAILURE;
309 }
310 hmod = LoadLibraryA(devices[i].modname);
311 if (!hmod) {
312 ERR("Failed to load TWAIN Source %s\n", modname);
313 DSM_twCC = TWCC_OPERATIONERROR;
314 HeapFree(GetProcessHeap(), 0, newSource);
315 return TWRC_FAILURE;
316 }
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);
324 DSM_sourceId--;
325 return TWRC_FAILURE;
326 }
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;
336 return TWRC_SUCCESS;
337 }
338
339 typedef struct {
340 pTW_IDENTITY origin;
341 pTW_IDENTITY result;
342 } userselect_data;
343
344 static INT_PTR CALLBACK userselect_dlgproc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
345 {
346 switch (msg)
347 {
348 case WM_INITDIALOG:
349 {
350 userselect_data *data = (userselect_data*)lparam;
351 int i;
352 HWND sourcelist;
353 BOOL any_devices = FALSE;
354
355 SetWindowLongPtrW(hwnd, DWLP_USER, (LONG_PTR)data);
356
357 sourcelist = GetDlgItem(hwnd, IDC_LISTSOURCE);
358
359 for (i=0; i<nrdevices; i++)
360 {
361 TW_IDENTITY *id = &devices[i].identity;
362 LRESULT index;
363
364 if ((id->SupportedGroups & data->origin->SupportedGroups) == 0)
365 continue;
366
367 index = SendMessageA(sourcelist, LB_ADDSTRING, 0, (LPARAM)id->ProductName);
368 SendMessageW(sourcelist, LB_SETITEMDATA, (WPARAM)index, (LPARAM)i);
369 any_devices = TRUE;
370 }
371
372 if (any_devices)
373 {
374 EnableWindow(GetDlgItem(hwnd, IDOK), TRUE);
375
376 /* FIXME: Select the supplied product name or default source. */
377 SendMessageW(sourcelist, LB_SETCURSEL, 0, 0);
378 }
379
380 return TRUE;
381 }
382 case WM_CLOSE:
383 EndDialog(hwnd, 0);
384 return TRUE;
385 case WM_COMMAND:
386 if (wparam == MAKEWPARAM(IDCANCEL, BN_CLICKED))
387 {
388 EndDialog(hwnd, 0);
389 return TRUE;
390 }
391 else if (wparam == MAKEWPARAM(IDOK, BN_CLICKED) ||
392 wparam == MAKEWPARAM(IDC_LISTSOURCE, LBN_DBLCLK))
393 {
394 userselect_data *data = (userselect_data*)GetWindowLongPtrW(hwnd, DWLP_USER);
395 HWND sourcelist;
396 LRESULT index;
397
398 sourcelist = GetDlgItem(hwnd, IDC_LISTSOURCE);
399
400 index = SendMessageW(sourcelist, LB_GETCURSEL, 0, 0);
401
402 if (index == LB_ERR)
403 return TRUE;
404
405 index = SendMessageW(sourcelist, LB_GETITEMDATA, (WPARAM)index, 0);
406
407 *data->result = devices[index].identity;
408
409 /* FIXME: Save this as the default source */
410
411 EndDialog(hwnd, 1);
412 return TRUE;
413 }
414 break;
415 }
416 return FALSE;
417 }
418
419 /* DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT */
420 TW_UINT16 TWAIN_UserSelect (pTW_IDENTITY pOrigin, TW_MEMREF pData)
421 {
422 userselect_data param = {pOrigin, pData};
423 HWND parent = DSM_parent;
424
425 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT SupportedGroups=0x%x ProductName=%s\n",
426 pOrigin->SupportedGroups, wine_dbgstr_a(param.result->ProductName));
427
428 twain_autodetect();
429
430 if (!IsWindow(parent))
431 parent = NULL;
432
433 if (DialogBoxParamW(DSM_hinstance, MAKEINTRESOURCEW(DLG_USERSELECT),
434 parent, userselect_dlgproc, (LPARAM)&param) == 0)
435 {
436 TRACE("canceled\n");
437 DSM_twCC = TWCC_SUCCESS;
438 return TWRC_CANCEL;
439 }
440
441 TRACE("<-- %s\n", wine_dbgstr_a(param.result->ProductName));
442 DSM_twCC = TWCC_SUCCESS;
443 return TWRC_SUCCESS;
444 }
445
446 /* DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM */
447 TW_UINT16 TWAIN_CloseDSM (pTW_IDENTITY pOrigin, TW_MEMREF pData)
448 {
449 activeDS *currentDS = activeSources, *nextDS;
450
451 TRACE("DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM\n");
452
453 if (DSM_initialized)
454 {
455 DSM_initialized = FALSE;
456
457 /* If there are data sources still open, close them now. */
458 while (currentDS != NULL)
459 {
460 nextDS = currentDS->next;
461 currentDS->dsEntry (pOrigin, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, pData);
462 HeapFree (GetProcessHeap(), 0, currentDS);
463 currentDS = nextDS;
464 }
465 activeSources = NULL;
466 DSM_parent = NULL;
467 DSM_twCC = TWCC_SUCCESS;
468 return TWRC_SUCCESS;
469 } else {
470 DSM_twCC = TWCC_SEQERROR;
471 return TWRC_FAILURE;
472 }
473 }
474
475 /* DG_CONTROL/DAT_PARENT/MSG_OPENDSM */
476 TW_UINT16 TWAIN_OpenDSM (pTW_IDENTITY pOrigin, TW_MEMREF pData)
477 {
478 TW_UINT16 twRC = TWRC_SUCCESS;
479
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;
486 twRC = TWRC_SUCCESS;
487 } else {
488 /* operation invoked in invalid state */
489 DSM_twCC = TWCC_SEQERROR;
490 twRC = TWRC_FAILURE;
491 }
492 DSM_parent = (HWND)pData;
493 return twRC;
494 }
495
496 /* DG_CONTROL/DAT_STATUS/MSG_GET */
497 TW_UINT16 TWAIN_GetDSMStatus (pTW_IDENTITY pOrigin, TW_MEMREF pData)
498 {
499 pTW_STATUS pSourceStatus = (pTW_STATUS) pData;
500
501 TRACE ("DG_CONTROL/DAT_STATUS/MSG_GET\n");
502
503 pSourceStatus->ConditionCode = DSM_twCC;
504 DSM_twCC = TWCC_SUCCESS; /* clear the condition code */
505 return TWRC_SUCCESS;
506 }