xref: /reactos/dll/win32/twain_32/dsm_ctrl.c (revision 17427193)
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)&param) == 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