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