Release 1.5.29.
[wine] / dlls / twain_32 / dsm_ctrl.c
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 #define NONAMELESSUNION
29 #define NONAMELESSSTRUCT
30 #include "windef.h"
31 #include "winbase.h"
32 #include "twain.h"
33 #include "twain_i.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
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
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", 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", 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                         return;
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 int detectionrun = 0;
104
105 static void
106 twain_autodetect(void) {
107         if (detectionrun) return;
108         detectionrun = 1;
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_IDENTITY/MSG_CLOSEDS */
120 TW_UINT16 TWAIN_CloseDS (pTW_IDENTITY pOrigin, TW_MEMREF pData)
121 {
122         TW_UINT16 twRC = TWRC_SUCCESS;
123         pTW_IDENTITY pIdentity = (pTW_IDENTITY) pData;
124         activeDS *currentDS = NULL, *prevDS = NULL;
125
126         TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS\n");
127
128         for (currentDS = activeSources; currentDS; currentDS = currentDS->next) {
129                 if (currentDS->identity.Id == pIdentity->Id)
130                         break;
131                 prevDS = currentDS;
132         }
133         if (!currentDS) {
134                 DSM_twCC = TWCC_NODS;
135                 return TWRC_FAILURE;
136         }
137         twRC = currentDS->dsEntry (pOrigin, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, pData);
138         /* This causes crashes due to still open Windows, so leave out for now.
139          * FreeLibrary (currentDS->hmod);
140          */
141         if (prevDS)
142                 prevDS->next = currentDS->next;
143         else
144                 activeSources = currentDS->next;
145         HeapFree (GetProcessHeap(), 0, currentDS);
146         if (twRC == TWRC_SUCCESS)
147                 DSM_twCC = TWCC_SUCCESS;
148         else /* FIXME: unclear how to get TWCC */
149                 DSM_twCC = TWCC_SEQERROR;
150         return twRC;
151 }
152
153 /* DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT */
154 TW_UINT16 TWAIN_IdentityGetDefault (pTW_IDENTITY pOrigin, TW_MEMREF pData)
155 {
156         pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
157
158         TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT\n");
159         DSM_twCC = TWCC_NODS;
160         twain_autodetect();
161         if (!nrdevices)
162                 return TWRC_FAILURE;
163         *pSourceIdentity = devices[0].identity;
164         DSM_twCC = TWCC_SUCCESS;
165         return TWRC_SUCCESS;
166 }
167
168 /* DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST */
169 TW_UINT16 TWAIN_IdentityGetFirst (pTW_IDENTITY pOrigin, TW_MEMREF pData)
170 {
171         pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
172
173         TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST\n");
174         twain_autodetect();
175         if (!nrdevices) {
176                 TRACE ("no entries found.\n");
177                 DSM_twCC = TWCC_NODS;
178                 return TWRC_FAILURE;
179         }
180         DSM_currentDevice = 0;
181         *pSourceIdentity = devices[DSM_currentDevice++].identity;
182         return TWRC_SUCCESS;
183 }
184
185 /* DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT */
186 TW_UINT16 TWAIN_IdentityGetNext (pTW_IDENTITY pOrigin, TW_MEMREF pData)
187 {
188         pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
189
190         TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT\n");
191         if (!nrdevices || (DSM_currentDevice == nrdevices)) {
192                 DSM_twCC = TWCC_SUCCESS;
193                 return TWRC_ENDOFLIST;
194         }
195         *pSourceIdentity = devices[DSM_currentDevice++].identity;
196         return TWRC_SUCCESS;
197 }
198
199 /* DG_CONTROL/DAT_IDENTITY/MSG_OPENDS */
200 TW_UINT16 TWAIN_OpenDS (pTW_IDENTITY pOrigin, TW_MEMREF pData)
201 {
202         TW_UINT16 i = 0;
203         pTW_IDENTITY pIdentity = (pTW_IDENTITY) pData;
204         activeDS *newSource;
205         const char *modname = NULL;
206         HMODULE hmod;
207
208         TRACE("DG_CONTROL/DAT_IDENTITY/MSG_OPENDS\n");
209         TRACE("pIdentity is %s\n", pIdentity->ProductName);
210         if (!DSM_initialized) {
211                 FIXME("seq error\n");
212                 DSM_twCC = TWCC_SEQERROR;
213                 return TWRC_FAILURE;
214         }
215         twain_autodetect();
216         if (!nrdevices) {
217                 FIXME("no devs.\n");
218                 DSM_twCC = TWCC_NODS;
219                 return TWRC_FAILURE;
220         }
221
222         if (pIdentity->ProductName[0] != '\0') {
223                 /* Make sure the source to be opened exists in the device list */
224                 for (i = 0; i<nrdevices; i++)
225                         if (!strcmp (devices[i].identity.ProductName, pIdentity->ProductName))
226                                 break;
227                 if (i == nrdevices)
228                         i = 0;
229         } /* else use the first device */
230
231         /* the source is found in the device list */
232         newSource = HeapAlloc (GetProcessHeap(), 0, sizeof (activeDS));
233         if (!newSource) {
234                 DSM_twCC = TWCC_LOWMEMORY;
235                 FIXME("Out of memory.\n");
236                 return TWRC_FAILURE;
237         }
238         hmod = LoadLibraryA(devices[i].modname);
239         if (!hmod) {
240                 ERR("Failed to load TWAIN Source %s\n", modname);
241                 DSM_twCC = TWCC_OPERATIONERROR;
242                 HeapFree(GetProcessHeap(), 0, newSource);
243                 return TWRC_FAILURE;
244         }
245         newSource->hmod = hmod; 
246         newSource->dsEntry = (DSENTRYPROC)GetProcAddress(hmod, "DS_Entry"); 
247         if (TWRC_SUCCESS != newSource->dsEntry (pOrigin, DG_CONTROL, DAT_IDENTITY, MSG_OPENDS, pIdentity)) {
248                 DSM_twCC = TWCC_OPERATIONERROR;
249                 HeapFree(GetProcessHeap(), 0, newSource);
250                 return TWRC_FAILURE;
251         }
252         /* Assign name and id for the opened data source */
253         pIdentity->Id = DSM_sourceId ++;
254         /* add the data source to an internal active source list */
255         newSource->next = activeSources;
256         newSource->identity.Id = pIdentity->Id;
257         strcpy (newSource->identity.ProductName, pIdentity->ProductName);
258         activeSources = newSource;
259         DSM_twCC = TWCC_SUCCESS;
260         return TWRC_SUCCESS;
261 }
262
263 /* DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT */
264 TW_UINT16 TWAIN_UserSelect (pTW_IDENTITY pOrigin, TW_MEMREF pData)
265 {
266         pTW_IDENTITY    selected = (pTW_IDENTITY)pData;
267
268         if (!nrdevices) {
269                 DSM_twCC = TWCC_OPERATIONERROR;
270                 return TWRC_FAILURE;
271         }
272         *selected = devices[0].identity;
273         DSM_twCC = TWCC_SUCCESS;
274         return TWRC_SUCCESS;
275 }
276
277 /* DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM */
278 TW_UINT16 TWAIN_CloseDSM (pTW_IDENTITY pOrigin, TW_MEMREF pData)
279 {
280     activeDS *currentDS = activeSources, *nextDS;
281
282     TRACE("DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM\n");
283
284     if (DSM_initialized)
285     {
286         DSM_initialized = FALSE;
287
288         /* If there are data sources still open, close them now. */
289         while (currentDS != NULL)
290         {
291             nextDS = currentDS->next;
292             currentDS->dsEntry (pOrigin, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, pData);
293             HeapFree (GetProcessHeap(), 0, currentDS);
294             currentDS = nextDS;
295         }
296         activeSources = NULL;
297         DSM_twCC = TWCC_SUCCESS;
298         return TWRC_SUCCESS;
299     } else {
300         DSM_twCC = TWCC_SEQERROR;
301         return TWRC_FAILURE;
302     }
303 }
304
305 /* DG_CONTROL/DAT_PARENT/MSG_OPENDSM */
306 TW_UINT16 TWAIN_OpenDSM (pTW_IDENTITY pOrigin, TW_MEMREF pData)
307 {
308         TW_UINT16 twRC = TWRC_SUCCESS;
309
310         TRACE("DG_CONTROL/DAT_PARENT/MSG_OPENDSM\n");
311         if (!DSM_initialized) {
312                 DSM_currentDevice = 0;
313                 DSM_initialized = TRUE;
314                 DSM_twCC = TWCC_SUCCESS;
315                 twRC = TWRC_SUCCESS;
316         } else {
317                 /* operation invoked in invalid state */
318                 DSM_twCC = TWCC_SEQERROR;
319                 twRC = TWRC_FAILURE;
320         }
321         return twRC;
322 }
323
324 /* DG_CONTROL/DAT_STATUS/MSG_GET */
325 TW_UINT16 TWAIN_GetDSMStatus (pTW_IDENTITY pOrigin, TW_MEMREF pData)
326 {
327         pTW_STATUS pSourceStatus = (pTW_STATUS) pData;
328
329         TRACE ("DG_CONTROL/DAT_STATUS/MSG_GET\n");
330
331         pSourceStatus->ConditionCode = DSM_twCC;
332         DSM_twCC = TWCC_SUCCESS;  /* clear the condition code */
333         return TWRC_SUCCESS;
334 }