ntdll: Rename local variables in heap_reallocate.
[wine.git] / dlls / twain_32 / dsm_ctrl.c
blob6854093017ab105c9e614602ff41e9cac4697507
1 /*
2 * TWAIN32 Source Manager
4 * Copyright 2000 Corel Corporation
5 * Copyright 2006 Marcus Meissner
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.
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.
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
22 #include <stdlib.h>
23 #include <stdarg.h>
24 #include <stdio.h>
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"
34 WINE_DEFAULT_DEBUG_CHANNEL(twain);
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;
42 struct all_devices {
43 WCHAR *modname;
44 TW_IDENTITY identity;
47 static int nrdevices = 0;
48 static struct all_devices *devices = NULL;
50 static void
51 twain_add_onedriver(const WCHAR *dsname) {
52 HMODULE hmod;
53 DSENTRYPROC dsEntry;
54 TW_IDENTITY fakeOrigin;
55 TW_IDENTITY sourceId;
56 TW_UINT16 ret;
57 WCHAR path[MAX_PATH];
59 swprintf( path, MAX_PATH, L"c:\\windows\\twain_%u\\%s", sizeof(void *) * 8, dsname );
60 hmod = LoadLibraryW(path);
61 if (!hmod) {
62 ERR("Failed to load TWAIN Source %s\n", debugstr_w(path));
63 return;
65 dsEntry = (DSENTRYPROC)GetProcAddress(hmod, "DS_Entry");
66 if (!dsEntry) {
67 ERR("Failed to find DS_Entry() in TWAIN DS %s\n", debugstr_w(path));
68 return;
70 /* Loop to do multiple detects, mostly for sane.ds and gphoto2.ds */
71 do {
72 int i;
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;
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));
86 for (i=0;i<nrdevices;i++) {
87 if (!strcmp(sourceId.ProductName,devices[i].identity.ProductName))
88 break;
90 if (i < nrdevices)
91 break;
92 devices = realloc( devices, sizeof(devices[0])*(nrdevices+1) );
93 devices[nrdevices].modname = wcsdup( path );
94 devices[nrdevices].identity = sourceId;
95 nrdevices++;
96 DSM_sourceId++;
97 } while (1);
98 FreeLibrary (hmod);
101 static BOOL detectionrun = FALSE;
103 static void
104 twain_autodetect(void) {
105 if (detectionrun) return;
106 detectionrun = TRUE;
108 twain_add_onedriver(L"sane.ds");
109 twain_add_onedriver(L"gphoto2.ds");
110 #if 0
111 twain_add_onedriver(L"Largan\\sp503a.ds");
112 twain_add_onedriver(L"vivicam10\\vivicam10.ds");
113 twain_add_onedriver(L"ws30slim\\sp500a.ds");
114 #endif
117 /* DG_CONTROL/DAT_NULL/MSG_CLOSEDSREQ|MSG_DEVICEEVENT|MSG_XFERREADY */
118 TW_UINT16 TWAIN_ControlNull (pTW_IDENTITY pOrigin, pTW_IDENTITY pDest, activeDS *pSource, TW_UINT16 MSG, TW_MEMREF pData)
120 struct pending_message *message;
122 TRACE ("DG_CONTROL/DAT_NULL MSG=%i\n", MSG);
124 if (MSG != MSG_CLOSEDSREQ &&
125 MSG != MSG_DEVICEEVENT &&
126 MSG != MSG_XFERREADY)
128 DSM_twCC = TWCC_BADPROTOCOL;
129 return TWRC_FAILURE;
132 message = HeapAlloc(GetProcessHeap(), 0, sizeof(*message));
133 if (!message)
135 DSM_twCC = TWCC_LOWMEMORY;
136 return TWRC_FAILURE;
139 message->msg = MSG;
140 list_add_tail(&pSource->pending_messages, &message->entry);
142 /* Delphi twain only sends us messages from one window, and it
143 doesn't even give us the real handle to that window. Other
144 applications might decide to forward messages sent to DSM_parent
145 or to the one supplied to ENABLEDS. So let's try very hard to
146 find a window that will work. */
147 if (DSM_parent)
148 PostMessageW(DSM_parent, event_message, 0, 0);
149 if (pSource->ui_window && pSource->ui_window != DSM_parent)
150 PostMessageW(pSource->ui_window, event_message, 0, 0);
151 if (pSource->event_window && pSource->event_window != pSource->ui_window &&
152 pSource->event_window != DSM_parent)
153 PostMessageW(pSource->event_window, event_message, 0, 0);
154 PostMessageW(0, event_message, 0, 0);
156 return TWRC_SUCCESS;
159 /* Filters MSG_PROCESSEVENT messages before reaching the data source */
160 TW_UINT16 TWAIN_ProcessEvent (pTW_IDENTITY pOrigin, activeDS *pSource, TW_MEMREF pData)
162 TW_EVENT *event = (TW_EVENT*)pData;
163 MSG *msg = (MSG*)event->pEvent;
164 TW_UINT16 result = TWRC_NOTDSEVENT;
166 TRACE("%x,%x\n", msg->message, event_message);
168 if (msg->message == event_message)
170 if (!list_empty (&pSource->pending_messages))
172 struct list *entry = list_head (&pSource->pending_messages);
173 struct pending_message *message = LIST_ENTRY(entry, struct pending_message, entry);
174 event->TWMessage = message->msg;
175 list_remove (entry);
176 TRACE("<-- %x\n", event->TWMessage);
178 else
179 event->TWMessage = MSG_NULL;
180 result = TWRC_DSEVENT;
183 if (msg->hwnd)
185 MSG dummy;
186 pSource->event_window = msg->hwnd;
187 if (!list_empty (&pSource->pending_messages) &&
188 !PeekMessageW(&dummy, msg->hwnd, event_message, event_message, PM_NOREMOVE))
190 PostMessageW(msg->hwnd, event_message, 0, 0);
194 return result;
197 /* DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS */
198 TW_UINT16 TWAIN_CloseDS (pTW_IDENTITY pOrigin, TW_MEMREF pData)
200 TW_UINT16 twRC = TWRC_SUCCESS;
201 pTW_IDENTITY pIdentity = (pTW_IDENTITY) pData;
202 activeDS *currentDS = NULL, *prevDS = NULL;
204 TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS\n");
206 for (currentDS = activeSources; currentDS; currentDS = currentDS->next) {
207 if (currentDS->identity.Id == pIdentity->Id)
208 break;
209 prevDS = currentDS;
211 if (!currentDS) {
212 DSM_twCC = TWCC_NODS;
213 return TWRC_FAILURE;
215 twRC = currentDS->dsEntry (pOrigin, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, pData);
216 /* This causes crashes due to still open Windows, so leave out for now.
217 * FreeLibrary (currentDS->hmod);
219 if (prevDS)
220 prevDS->next = currentDS->next;
221 else
222 activeSources = currentDS->next;
223 HeapFree (GetProcessHeap(), 0, currentDS);
224 if (twRC == TWRC_SUCCESS)
225 DSM_twCC = TWCC_SUCCESS;
226 else /* FIXME: unclear how to get TWCC */
227 DSM_twCC = TWCC_SEQERROR;
228 return twRC;
231 /* DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT */
232 TW_UINT16 TWAIN_IdentityGetDefault (pTW_IDENTITY pOrigin, TW_MEMREF pData)
234 pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
236 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT\n");
237 DSM_twCC = TWCC_NODS;
238 twain_autodetect();
239 if (!nrdevices)
240 return TWRC_FAILURE;
241 *pSourceIdentity = devices[0].identity;
242 DSM_twCC = TWCC_SUCCESS;
243 return TWRC_SUCCESS;
246 /* DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST */
247 TW_UINT16 TWAIN_IdentityGetFirst (pTW_IDENTITY pOrigin, TW_MEMREF pData)
249 pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
251 TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST\n");
252 twain_autodetect();
253 if (!nrdevices) {
254 TRACE ("no entries found.\n");
255 DSM_twCC = TWCC_NODS;
256 return TWRC_FAILURE;
258 DSM_currentDevice = 0;
259 *pSourceIdentity = devices[DSM_currentDevice++].identity;
260 return TWRC_SUCCESS;
263 /* DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT */
264 TW_UINT16 TWAIN_IdentityGetNext (pTW_IDENTITY pOrigin, TW_MEMREF pData)
266 pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
268 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT\n");
269 if (!nrdevices || (DSM_currentDevice == nrdevices)) {
270 DSM_twCC = TWCC_SUCCESS;
271 return TWRC_ENDOFLIST;
273 *pSourceIdentity = devices[DSM_currentDevice++].identity;
274 return TWRC_SUCCESS;
277 /* DG_CONTROL/DAT_IDENTITY/MSG_OPENDS */
278 TW_UINT16 TWAIN_OpenDS (pTW_IDENTITY pOrigin, TW_MEMREF pData)
280 TW_UINT16 i = 0;
281 pTW_IDENTITY pIdentity = (pTW_IDENTITY) pData;
282 activeDS *newSource;
283 HMODULE hmod;
285 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_OPENDS\n");
286 TRACE("pIdentity is %s\n", pIdentity->ProductName);
287 if (!DSM_initialized) {
288 FIXME("seq error\n");
289 DSM_twCC = TWCC_SEQERROR;
290 return TWRC_FAILURE;
292 twain_autodetect();
293 if (!nrdevices) {
294 FIXME("no devs.\n");
295 DSM_twCC = TWCC_NODS;
296 return TWRC_FAILURE;
299 if (pIdentity->ProductName[0] != '\0') {
300 /* Make sure the source to be opened exists in the device list */
301 for (i = 0; i<nrdevices; i++)
302 if (!strcmp (devices[i].identity.ProductName, pIdentity->ProductName))
303 break;
304 if (i == nrdevices)
305 i = 0;
306 } /* else use the first device */
308 /* the source is found in the device list */
309 newSource = HeapAlloc (GetProcessHeap(), 0, sizeof (activeDS));
310 if (!newSource) {
311 DSM_twCC = TWCC_LOWMEMORY;
312 FIXME("Out of memory.\n");
313 return TWRC_FAILURE;
315 hmod = LoadLibraryW(devices[i].modname);
316 if (!hmod) {
317 ERR("Failed to load TWAIN Source %s\n", debugstr_w(devices[i].modname));
318 DSM_twCC = TWCC_OPERATIONERROR;
319 HeapFree(GetProcessHeap(), 0, newSource);
320 return TWRC_FAILURE;
322 newSource->hmod = hmod;
323 newSource->dsEntry = (DSENTRYPROC)GetProcAddress(hmod, "DS_Entry");
324 /* Assign id for the opened data source */
325 pIdentity->Id = DSM_sourceId ++;
326 if (TWRC_SUCCESS != newSource->dsEntry (pOrigin, DG_CONTROL, DAT_IDENTITY, MSG_OPENDS, pIdentity)) {
327 DSM_twCC = TWCC_OPERATIONERROR;
328 HeapFree(GetProcessHeap(), 0, newSource);
329 DSM_sourceId--;
330 return TWRC_FAILURE;
332 /* add the data source to an internal active source list */
333 newSource->next = activeSources;
334 newSource->identity.Id = pIdentity->Id;
335 strcpy (newSource->identity.ProductName, pIdentity->ProductName);
336 list_init(&newSource->pending_messages);
337 newSource->ui_window = NULL;
338 newSource->event_window = NULL;
339 activeSources = newSource;
340 DSM_twCC = TWCC_SUCCESS;
341 return TWRC_SUCCESS;
344 typedef struct {
345 pTW_IDENTITY origin;
346 pTW_IDENTITY result;
347 } userselect_data;
349 static INT_PTR CALLBACK userselect_dlgproc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
351 switch (msg)
353 case WM_INITDIALOG:
355 userselect_data *data = (userselect_data*)lparam;
356 int i;
357 HWND sourcelist;
358 BOOL any_devices = FALSE;
360 SetWindowLongPtrW(hwnd, DWLP_USER, (LONG_PTR)data);
362 sourcelist = GetDlgItem(hwnd, IDC_LISTSOURCE);
364 for (i=0; i<nrdevices; i++)
366 TW_IDENTITY *id = &devices[i].identity;
367 LRESULT index;
369 if ((id->SupportedGroups & data->origin->SupportedGroups) == 0)
370 continue;
372 index = SendMessageA(sourcelist, LB_ADDSTRING, 0, (LPARAM)id->ProductName);
373 SendMessageW(sourcelist, LB_SETITEMDATA, (WPARAM)index, (LPARAM)i);
374 any_devices = TRUE;
377 if (any_devices)
379 EnableWindow(GetDlgItem(hwnd, IDOK), TRUE);
381 /* FIXME: Select the supplied product name or default source. */
382 SendMessageW(sourcelist, LB_SETCURSEL, 0, 0);
385 return TRUE;
387 case WM_CLOSE:
388 EndDialog(hwnd, 0);
389 return TRUE;
390 case WM_COMMAND:
391 if (wparam == MAKEWPARAM(IDCANCEL, BN_CLICKED))
393 EndDialog(hwnd, 0);
394 return TRUE;
396 else if (wparam == MAKEWPARAM(IDOK, BN_CLICKED) ||
397 wparam == MAKEWPARAM(IDC_LISTSOURCE, LBN_DBLCLK))
399 userselect_data *data = (userselect_data*)GetWindowLongPtrW(hwnd, DWLP_USER);
400 HWND sourcelist;
401 LRESULT index;
403 sourcelist = GetDlgItem(hwnd, IDC_LISTSOURCE);
405 index = SendMessageW(sourcelist, LB_GETCURSEL, 0, 0);
407 if (index == LB_ERR)
408 return TRUE;
410 index = SendMessageW(sourcelist, LB_GETITEMDATA, (WPARAM)index, 0);
412 *data->result = devices[index].identity;
414 /* FIXME: Save this as the default source */
416 EndDialog(hwnd, 1);
417 return TRUE;
419 break;
421 return FALSE;
424 /* DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT */
425 TW_UINT16 TWAIN_UserSelect (pTW_IDENTITY pOrigin, TW_MEMREF pData)
427 userselect_data param = {pOrigin, pData};
428 HWND parent = DSM_parent;
430 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT SupportedGroups=0x%lx ProductName=%s\n",
431 pOrigin->SupportedGroups, wine_dbgstr_a(param.result->ProductName));
433 twain_autodetect();
435 if (!IsWindow(parent))
436 parent = NULL;
438 if (DialogBoxParamW(DSM_hinstance, MAKEINTRESOURCEW(DLG_USERSELECT),
439 parent, userselect_dlgproc, (LPARAM)&param) == 0)
441 TRACE("canceled\n");
442 DSM_twCC = TWCC_SUCCESS;
443 return TWRC_CANCEL;
446 TRACE("<-- %s\n", wine_dbgstr_a(param.result->ProductName));
447 DSM_twCC = TWCC_SUCCESS;
448 return TWRC_SUCCESS;
451 /* DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM */
452 TW_UINT16 TWAIN_CloseDSM (pTW_IDENTITY pOrigin, TW_MEMREF pData)
454 activeDS *currentDS = activeSources, *nextDS;
456 TRACE("DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM\n");
458 if (DSM_initialized)
460 DSM_initialized = FALSE;
462 /* If there are data sources still open, close them now. */
463 while (currentDS != NULL)
465 nextDS = currentDS->next;
466 currentDS->dsEntry (pOrigin, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, pData);
467 HeapFree (GetProcessHeap(), 0, currentDS);
468 currentDS = nextDS;
470 activeSources = NULL;
471 DSM_parent = NULL;
472 DSM_twCC = TWCC_SUCCESS;
473 return TWRC_SUCCESS;
474 } else {
475 DSM_twCC = TWCC_SEQERROR;
476 return TWRC_FAILURE;
480 /* DG_CONTROL/DAT_PARENT/MSG_OPENDSM */
481 TW_UINT16 TWAIN_OpenDSM (pTW_IDENTITY pOrigin, TW_MEMREF pData)
483 TW_UINT16 twRC = TWRC_SUCCESS;
485 TRACE("DG_CONTROL/DAT_PARENT/MSG_OPENDSM\n");
486 if (!DSM_initialized) {
487 event_message = RegisterWindowMessageA("WINE TWAIN_32 EVENT");
488 DSM_currentDevice = 0;
489 DSM_initialized = TRUE;
490 DSM_twCC = TWCC_SUCCESS;
491 twRC = TWRC_SUCCESS;
492 } else {
493 /* operation invoked in invalid state */
494 DSM_twCC = TWCC_SEQERROR;
495 twRC = TWRC_FAILURE;
497 DSM_parent = (HWND)pData;
498 return twRC;
501 /* DG_CONTROL/DAT_STATUS/MSG_GET */
502 TW_UINT16 TWAIN_GetDSMStatus (pTW_IDENTITY pOrigin, TW_MEMREF pData)
504 pTW_STATUS pSourceStatus = (pTW_STATUS) pData;
506 TRACE ("DG_CONTROL/DAT_STATUS/MSG_GET\n");
508 pSourceStatus->ConditionCode = DSM_twCC;
509 DSM_twCC = TWCC_SUCCESS; /* clear the condition code */
510 return TWRC_SUCCESS;