ntdll: Buffer pagemap reads in fill_working_set_info().
[wine.git] / dlls / twain_32 / dsm_ctrl.c
blob13503e6772cc844673cf9a0a90636afc89398cb3
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 struct all_devices *new_devices;
57 TW_UINT16 ret;
58 WCHAR path[MAX_PATH];
60 swprintf( path, MAX_PATH, L"c:\\windows\\twain_%u\\%s", sizeof(void *) * 8, dsname );
61 hmod = LoadLibraryW(path);
62 if (!hmod) {
63 ERR("Failed to load TWAIN Source %s\n", debugstr_w(path));
64 return;
66 dsEntry = (DSENTRYPROC)GetProcAddress(hmod, "DS_Entry");
67 if (!dsEntry) {
68 ERR("Failed to find DS_Entry() in TWAIN DS %s\n", debugstr_w(path));
69 return;
71 /* Loop to do multiple detects, mostly for sane.ds and gphoto2.ds */
72 do {
73 int i;
75 sourceId.Id = DSM_sourceId;
76 sourceId.ProtocolMajor = TWON_PROTOCOLMAJOR;
77 sourceId.ProtocolMinor = TWON_PROTOCOLMINOR;
78 ret = dsEntry (&fakeOrigin, DG_CONTROL, DAT_IDENTITY, MSG_GET, &sourceId);
79 if (ret != TWRC_SUCCESS) {
80 ERR("Source->(DG_CONTROL,DAT_IDENTITY,MSG_GET) failed!\n");
81 break;
83 TRACE("Manufacturer: %s\n", debugstr_a(sourceId.Manufacturer));
84 TRACE("ProductFamily: %s\n", debugstr_a(sourceId.ProductFamily));
85 TRACE("ProductName: %s\n", debugstr_a(sourceId.ProductName));
87 for (i=0;i<nrdevices;i++) {
88 if (!strcmp(sourceId.ProductName,devices[i].identity.ProductName))
89 break;
91 if (i < nrdevices)
92 break;
93 new_devices = realloc( devices, sizeof(devices[0]) * (nrdevices + 1) );
94 if (!new_devices)
95 break;
96 devices = new_devices;
97 devices[nrdevices].modname = wcsdup( path );
98 devices[nrdevices].identity = sourceId;
99 nrdevices++;
100 DSM_sourceId++;
101 } while (1);
102 FreeLibrary (hmod);
105 static BOOL detectionrun = FALSE;
107 static void
108 twain_autodetect(void) {
109 if (detectionrun) return;
110 detectionrun = TRUE;
112 twain_add_onedriver(L"sane.ds");
113 twain_add_onedriver(L"gphoto2.ds");
114 #if 0
115 twain_add_onedriver(L"Largan\\sp503a.ds");
116 twain_add_onedriver(L"vivicam10\\vivicam10.ds");
117 twain_add_onedriver(L"ws30slim\\sp500a.ds");
118 #endif
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)
124 struct pending_message *message;
126 TRACE ("DG_CONTROL/DAT_NULL MSG=%i\n", MSG);
128 if (MSG != MSG_CLOSEDSREQ &&
129 MSG != MSG_DEVICEEVENT &&
130 MSG != MSG_XFERREADY)
132 DSM_twCC = TWCC_BADPROTOCOL;
133 return TWRC_FAILURE;
136 message = HeapAlloc(GetProcessHeap(), 0, sizeof(*message));
137 if (!message)
139 DSM_twCC = TWCC_LOWMEMORY;
140 return TWRC_FAILURE;
143 message->msg = MSG;
144 list_add_tail(&pSource->pending_messages, &message->entry);
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);
160 return TWRC_SUCCESS;
163 /* Filters MSG_PROCESSEVENT messages before reaching the data source */
164 TW_UINT16 TWAIN_ProcessEvent (pTW_IDENTITY pOrigin, activeDS *pSource, TW_MEMREF pData)
166 TW_EVENT *event = (TW_EVENT*)pData;
167 MSG *msg = (MSG*)event->pEvent;
168 TW_UINT16 result = TWRC_NOTDSEVENT;
170 TRACE("%x,%x\n", msg->message, event_message);
172 if (msg->message == event_message)
174 if (!list_empty (&pSource->pending_messages))
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);
182 else
183 event->TWMessage = MSG_NULL;
184 result = TWRC_DSEVENT;
187 if (msg->hwnd)
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))
194 PostMessageW(msg->hwnd, event_message, 0, 0);
198 return result;
201 /* DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS */
202 TW_UINT16 TWAIN_CloseDS (pTW_IDENTITY pOrigin, TW_MEMREF pData)
204 TW_UINT16 twRC = TWRC_SUCCESS;
205 pTW_IDENTITY pIdentity = (pTW_IDENTITY) pData;
206 activeDS *currentDS = NULL, *prevDS = NULL;
208 TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS\n");
210 for (currentDS = activeSources; currentDS; currentDS = currentDS->next) {
211 if (currentDS->identity.Id == pIdentity->Id)
212 break;
213 prevDS = currentDS;
215 if (!currentDS) {
216 DSM_twCC = TWCC_NODS;
217 return TWRC_FAILURE;
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);
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;
235 /* DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT */
236 TW_UINT16 TWAIN_IdentityGetDefault (pTW_IDENTITY pOrigin, TW_MEMREF pData)
238 pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
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;
250 /* DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST */
251 TW_UINT16 TWAIN_IdentityGetFirst (pTW_IDENTITY pOrigin, TW_MEMREF pData)
253 pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
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;
262 DSM_currentDevice = 0;
263 *pSourceIdentity = devices[DSM_currentDevice++].identity;
264 return TWRC_SUCCESS;
267 /* DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT */
268 TW_UINT16 TWAIN_IdentityGetNext (pTW_IDENTITY pOrigin, TW_MEMREF pData)
270 pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
272 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT\n");
273 if (!nrdevices || (DSM_currentDevice == nrdevices)) {
274 DSM_twCC = TWCC_SUCCESS;
275 return TWRC_ENDOFLIST;
277 *pSourceIdentity = devices[DSM_currentDevice++].identity;
278 return TWRC_SUCCESS;
281 /* DG_CONTROL/DAT_IDENTITY/MSG_OPENDS */
282 TW_UINT16 TWAIN_OpenDS (pTW_IDENTITY pOrigin, TW_MEMREF pData)
284 TW_UINT16 i = 0;
285 pTW_IDENTITY pIdentity = (pTW_IDENTITY) pData;
286 activeDS *newSource;
287 HMODULE hmod;
289 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_OPENDS\n");
290 TRACE("pIdentity is %s\n", pIdentity->ProductName);
291 if (!DSM_initialized) {
292 FIXME("seq error\n");
293 DSM_twCC = TWCC_SEQERROR;
294 return TWRC_FAILURE;
296 twain_autodetect();
297 if (!nrdevices) {
298 FIXME("no devs.\n");
299 DSM_twCC = TWCC_NODS;
300 return TWRC_FAILURE;
303 if (pIdentity->ProductName[0] != '\0') {
304 /* Make sure the source to be opened exists in the device list */
305 for (i = 0; i<nrdevices; i++)
306 if (!strcmp (devices[i].identity.ProductName, pIdentity->ProductName))
307 break;
308 if (i == nrdevices)
309 i = 0;
310 } /* else use the first device */
312 /* the source is found in the device list */
313 newSource = HeapAlloc (GetProcessHeap(), 0, sizeof (activeDS));
314 if (!newSource) {
315 DSM_twCC = TWCC_LOWMEMORY;
316 FIXME("Out of memory.\n");
317 return TWRC_FAILURE;
319 hmod = LoadLibraryW(devices[i].modname);
320 if (!hmod) {
321 ERR("Failed to load TWAIN Source %s\n", debugstr_w(devices[i].modname));
322 DSM_twCC = TWCC_OPERATIONERROR;
323 HeapFree(GetProcessHeap(), 0, newSource);
324 return TWRC_FAILURE;
326 newSource->hmod = hmod;
327 newSource->dsEntry = (DSENTRYPROC)GetProcAddress(hmod, "DS_Entry");
328 /* Assign id for the opened data source */
329 pIdentity->Id = DSM_sourceId ++;
330 if (TWRC_SUCCESS != newSource->dsEntry (pOrigin, DG_CONTROL, DAT_IDENTITY, MSG_OPENDS, pIdentity)) {
331 DSM_twCC = TWCC_OPERATIONERROR;
332 HeapFree(GetProcessHeap(), 0, newSource);
333 DSM_sourceId--;
334 return TWRC_FAILURE;
336 /* add the data source to an internal active source list */
337 newSource->next = activeSources;
338 newSource->identity.Id = pIdentity->Id;
339 strcpy (newSource->identity.ProductName, pIdentity->ProductName);
340 list_init(&newSource->pending_messages);
341 newSource->ui_window = NULL;
342 newSource->event_window = NULL;
343 activeSources = newSource;
344 DSM_twCC = TWCC_SUCCESS;
345 return TWRC_SUCCESS;
348 typedef struct {
349 pTW_IDENTITY origin;
350 pTW_IDENTITY result;
351 } userselect_data;
353 static INT_PTR CALLBACK userselect_dlgproc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
355 switch (msg)
357 case WM_INITDIALOG:
359 userselect_data *data = (userselect_data*)lparam;
360 int i;
361 HWND sourcelist;
362 BOOL any_devices = FALSE;
364 SetWindowLongPtrW(hwnd, DWLP_USER, (LONG_PTR)data);
366 sourcelist = GetDlgItem(hwnd, IDC_LISTSOURCE);
368 for (i=0; i<nrdevices; i++)
370 TW_IDENTITY *id = &devices[i].identity;
371 LRESULT index;
373 if ((id->SupportedGroups & data->origin->SupportedGroups) == 0)
374 continue;
376 index = SendMessageA(sourcelist, LB_ADDSTRING, 0, (LPARAM)id->ProductName);
377 SendMessageW(sourcelist, LB_SETITEMDATA, (WPARAM)index, (LPARAM)i);
378 any_devices = TRUE;
381 if (any_devices)
383 EnableWindow(GetDlgItem(hwnd, IDOK), TRUE);
385 /* FIXME: Select the supplied product name or default source. */
386 SendMessageW(sourcelist, LB_SETCURSEL, 0, 0);
389 return TRUE;
391 case WM_CLOSE:
392 EndDialog(hwnd, 0);
393 return TRUE;
394 case WM_COMMAND:
395 if (wparam == MAKEWPARAM(IDCANCEL, BN_CLICKED))
397 EndDialog(hwnd, 0);
398 return TRUE;
400 else if (wparam == MAKEWPARAM(IDOK, BN_CLICKED) ||
401 wparam == MAKEWPARAM(IDC_LISTSOURCE, LBN_DBLCLK))
403 userselect_data *data = (userselect_data*)GetWindowLongPtrW(hwnd, DWLP_USER);
404 HWND sourcelist;
405 LRESULT index;
407 sourcelist = GetDlgItem(hwnd, IDC_LISTSOURCE);
409 index = SendMessageW(sourcelist, LB_GETCURSEL, 0, 0);
411 if (index == LB_ERR)
412 return TRUE;
414 index = SendMessageW(sourcelist, LB_GETITEMDATA, (WPARAM)index, 0);
416 *data->result = devices[index].identity;
418 /* FIXME: Save this as the default source */
420 EndDialog(hwnd, 1);
421 return TRUE;
423 break;
425 return FALSE;
428 /* DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT */
429 TW_UINT16 TWAIN_UserSelect (pTW_IDENTITY pOrigin, TW_MEMREF pData)
431 userselect_data param = {pOrigin, pData};
432 HWND parent = DSM_parent;
434 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT SupportedGroups=0x%lx ProductName=%s\n",
435 pOrigin->SupportedGroups, wine_dbgstr_a(param.result->ProductName));
437 twain_autodetect();
439 if (!IsWindow(parent))
440 parent = NULL;
442 if (DialogBoxParamW(DSM_hinstance, MAKEINTRESOURCEW(DLG_USERSELECT),
443 parent, userselect_dlgproc, (LPARAM)&param) == 0)
445 TRACE("canceled\n");
446 DSM_twCC = TWCC_SUCCESS;
447 return TWRC_CANCEL;
450 TRACE("<-- %s\n", wine_dbgstr_a(param.result->ProductName));
451 DSM_twCC = TWCC_SUCCESS;
452 return TWRC_SUCCESS;
455 /* DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM */
456 TW_UINT16 TWAIN_CloseDSM (pTW_IDENTITY pOrigin, TW_MEMREF pData)
458 activeDS *currentDS = activeSources, *nextDS;
460 TRACE("DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM\n");
462 if (DSM_initialized)
464 DSM_initialized = FALSE;
466 /* If there are data sources still open, close them now. */
467 while (currentDS != NULL)
469 nextDS = currentDS->next;
470 currentDS->dsEntry (pOrigin, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, pData);
471 HeapFree (GetProcessHeap(), 0, currentDS);
472 currentDS = nextDS;
474 activeSources = NULL;
475 DSM_parent = NULL;
476 DSM_twCC = TWCC_SUCCESS;
477 return TWRC_SUCCESS;
478 } else {
479 DSM_twCC = TWCC_SEQERROR;
480 return TWRC_FAILURE;
484 /* DG_CONTROL/DAT_PARENT/MSG_OPENDSM */
485 TW_UINT16 TWAIN_OpenDSM (pTW_IDENTITY pOrigin, TW_MEMREF pData)
487 TW_UINT16 twRC = TWRC_SUCCESS;
489 TRACE("DG_CONTROL/DAT_PARENT/MSG_OPENDSM\n");
490 if (!DSM_initialized) {
491 event_message = RegisterWindowMessageA("WINE TWAIN_32 EVENT");
492 DSM_currentDevice = 0;
493 DSM_initialized = TRUE;
494 DSM_twCC = TWCC_SUCCESS;
495 twRC = TWRC_SUCCESS;
496 } else {
497 /* operation invoked in invalid state */
498 DSM_twCC = TWCC_SEQERROR;
499 twRC = TWRC_FAILURE;
501 DSM_parent = (HWND)pData;
502 return twRC;
505 /* DG_CONTROL/DAT_STATUS/MSG_GET */
506 TW_UINT16 TWAIN_GetDSMStatus (pTW_IDENTITY pOrigin, TW_MEMREF pData)
508 pTW_STATUS pSourceStatus = (pTW_STATUS) pData;
510 TRACE ("DG_CONTROL/DAT_STATUS/MSG_GET\n");
512 pSourceStatus->ConditionCode = DSM_twCC;
513 DSM_twCC = TWCC_SUCCESS; /* clear the condition code */
514 return TWRC_SUCCESS;