xref: /reactos/dll/win32/twain_32/dsm_ctrl.c (revision 0f92924a)
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 }
517