* Sync to trunk r63845.
[reactos.git] / dll / win32 / 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 "twain_i.h"
23
24 static TW_UINT16 DSM_initialized; /* whether Source Manager is initialized */
25 static TW_UINT32 DSM_sourceId; /* source id generator */
26 static TW_UINT16 DSM_currentDevice; /* keep track of device during enumeration */
27
28 struct all_devices {
29 char *modname;
30 TW_IDENTITY identity;
31 };
32
33 static int nrdevices = 0;
34 static struct all_devices *devices = NULL;
35
36 static void
37 twain_add_onedriver(const char *dsname) {
38 HMODULE hmod;
39 DSENTRYPROC dsEntry;
40 TW_IDENTITY fakeOrigin;
41 TW_IDENTITY sourceId;
42 TW_UINT16 ret;
43
44 hmod = LoadLibraryA(dsname);
45 if (!hmod) {
46 ERR("Failed to load TWAIN Source %s\n", dsname);
47 return;
48 }
49 dsEntry = (DSENTRYPROC)GetProcAddress(hmod, "DS_Entry");
50 if (!dsEntry) {
51 ERR("Failed to find DS_Entry() in TWAIN DS %s\n", dsname);
52 return;
53 }
54 /* Loop to do multiple detects, mostly for sane.ds and gphoto2.ds */
55 do {
56 int i;
57
58 sourceId.Id = DSM_sourceId;
59 sourceId.ProtocolMajor = TWON_PROTOCOLMAJOR;
60 sourceId.ProtocolMinor = TWON_PROTOCOLMINOR;
61 ret = dsEntry (&fakeOrigin, DG_CONTROL, DAT_IDENTITY, MSG_GET, &sourceId);
62 if (ret != TWRC_SUCCESS) {
63 ERR("Source->(DG_CONTROL,DAT_IDENTITY,MSG_GET) failed!\n");
64 break;
65 }
66 TRACE("Manufacturer: %s\n", debugstr_a(sourceId.Manufacturer));
67 TRACE("ProductFamily: %s\n", debugstr_a(sourceId.ProductFamily));
68 TRACE("ProductName: %s\n", debugstr_a(sourceId.ProductName));
69
70 for (i=0;i<nrdevices;i++) {
71 if (!strcmp(sourceId.ProductName,devices[i].identity.ProductName))
72 break;
73 }
74 if (i < nrdevices)
75 break;
76 if (nrdevices)
77 devices = HeapReAlloc(GetProcessHeap(), 0, devices, sizeof(devices[0])*(nrdevices+1));
78 else
79 devices = HeapAlloc(GetProcessHeap(), 0, sizeof(devices[0]));
80 if ((devices[nrdevices].modname = HeapAlloc(GetProcessHeap(), 0, strlen(dsname) + 1)))
81 lstrcpyA(devices[nrdevices].modname, dsname);
82 devices[nrdevices].identity = sourceId;
83 nrdevices++;
84 DSM_sourceId++;
85 } while (1);
86 FreeLibrary (hmod);
87 }
88
89 static BOOL detectionrun = FALSE;
90
91 static void
92 twain_autodetect(void) {
93 if (detectionrun) return;
94 detectionrun = TRUE;
95
96 twain_add_onedriver("sane.ds");
97 twain_add_onedriver("gphoto2.ds");
98 #if 0
99 twain_add_onedriver("c:\\windows\\Twain_32\\Largan\\sp503a.ds");
100 twain_add_onedriver("c:\\windows\\Twain_32\\vivicam10\\vivicam10.ds");
101 twain_add_onedriver("c:\\windows\\Twain_32\\ws30slim\\sp500a.ds");
102 #endif
103 }
104
105 /* DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS */
106 TW_UINT16 TWAIN_CloseDS (pTW_IDENTITY pOrigin, TW_MEMREF pData)
107 {
108 TW_UINT16 twRC = TWRC_SUCCESS;
109 pTW_IDENTITY pIdentity = (pTW_IDENTITY) pData;
110 activeDS *currentDS = NULL, *prevDS = NULL;
111
112 TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS\n");
113
114 for (currentDS = activeSources; currentDS; currentDS = currentDS->next) {
115 if (currentDS->identity.Id == pIdentity->Id)
116 break;
117 prevDS = currentDS;
118 }
119 if (!currentDS) {
120 DSM_twCC = TWCC_NODS;
121 return TWRC_FAILURE;
122 }
123 twRC = currentDS->dsEntry (pOrigin, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, pData);
124 /* This causes crashes due to still open Windows, so leave out for now.
125 * FreeLibrary (currentDS->hmod);
126 */
127 if (prevDS)
128 prevDS->next = currentDS->next;
129 else
130 activeSources = currentDS->next;
131 HeapFree (GetProcessHeap(), 0, currentDS);
132 if (twRC == TWRC_SUCCESS)
133 DSM_twCC = TWCC_SUCCESS;
134 else /* FIXME: unclear how to get TWCC */
135 DSM_twCC = TWCC_SEQERROR;
136 return twRC;
137 }
138
139 /* DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT */
140 TW_UINT16 TWAIN_IdentityGetDefault (pTW_IDENTITY pOrigin, TW_MEMREF pData)
141 {
142 pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
143
144 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT\n");
145 DSM_twCC = TWCC_NODS;
146 twain_autodetect();
147 if (!nrdevices)
148 return TWRC_FAILURE;
149 *pSourceIdentity = devices[0].identity;
150 DSM_twCC = TWCC_SUCCESS;
151 return TWRC_SUCCESS;
152 }
153
154 /* DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST */
155 TW_UINT16 TWAIN_IdentityGetFirst (pTW_IDENTITY pOrigin, TW_MEMREF pData)
156 {
157 pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
158
159 TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST\n");
160 twain_autodetect();
161 if (!nrdevices) {
162 TRACE ("no entries found.\n");
163 DSM_twCC = TWCC_NODS;
164 return TWRC_FAILURE;
165 }
166 DSM_currentDevice = 0;
167 *pSourceIdentity = devices[DSM_currentDevice++].identity;
168 return TWRC_SUCCESS;
169 }
170
171 /* DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT */
172 TW_UINT16 TWAIN_IdentityGetNext (pTW_IDENTITY pOrigin, TW_MEMREF pData)
173 {
174 pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
175
176 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT\n");
177 if (!nrdevices || (DSM_currentDevice == nrdevices)) {
178 DSM_twCC = TWCC_SUCCESS;
179 return TWRC_ENDOFLIST;
180 }
181 *pSourceIdentity = devices[DSM_currentDevice++].identity;
182 return TWRC_SUCCESS;
183 }
184
185 /* DG_CONTROL/DAT_IDENTITY/MSG_OPENDS */
186 TW_UINT16 TWAIN_OpenDS (pTW_IDENTITY pOrigin, TW_MEMREF pData)
187 {
188 TW_UINT16 i = 0;
189 pTW_IDENTITY pIdentity = (pTW_IDENTITY) pData;
190 activeDS *newSource;
191 const char *modname = NULL;
192 HMODULE hmod;
193
194 TRACE("DG_CONTROL/DAT_IDENTITY/MSG_OPENDS\n");
195 TRACE("pIdentity is %s\n", pIdentity->ProductName);
196 if (!DSM_initialized) {
197 FIXME("seq error\n");
198 DSM_twCC = TWCC_SEQERROR;
199 return TWRC_FAILURE;
200 }
201 twain_autodetect();
202 if (!nrdevices) {
203 FIXME("no devs.\n");
204 DSM_twCC = TWCC_NODS;
205 return TWRC_FAILURE;
206 }
207
208 if (pIdentity->ProductName[0] != '\0') {
209 /* Make sure the source to be opened exists in the device list */
210 for (i = 0; i<nrdevices; i++)
211 if (!strcmp (devices[i].identity.ProductName, pIdentity->ProductName))
212 break;
213 if (i == nrdevices)
214 i = 0;
215 } /* else use the first device */
216
217 /* the source is found in the device list */
218 newSource = HeapAlloc (GetProcessHeap(), 0, sizeof (activeDS));
219 if (!newSource) {
220 DSM_twCC = TWCC_LOWMEMORY;
221 FIXME("Out of memory.\n");
222 return TWRC_FAILURE;
223 }
224 hmod = LoadLibraryA(devices[i].modname);
225 if (!hmod) {
226 ERR("Failed to load TWAIN Source %s\n", modname);
227 DSM_twCC = TWCC_OPERATIONERROR;
228 HeapFree(GetProcessHeap(), 0, newSource);
229 return TWRC_FAILURE;
230 }
231 newSource->hmod = hmod;
232 newSource->dsEntry = (DSENTRYPROC)GetProcAddress(hmod, "DS_Entry");
233 if (TWRC_SUCCESS != newSource->dsEntry (pOrigin, DG_CONTROL, DAT_IDENTITY, MSG_OPENDS, pIdentity)) {
234 DSM_twCC = TWCC_OPERATIONERROR;
235 HeapFree(GetProcessHeap(), 0, newSource);
236 return TWRC_FAILURE;
237 }
238 /* Assign name and id for the opened data source */
239 pIdentity->Id = DSM_sourceId ++;
240 /* add the data source to an internal active source list */
241 newSource->next = activeSources;
242 newSource->identity.Id = pIdentity->Id;
243 strcpy (newSource->identity.ProductName, pIdentity->ProductName);
244 activeSources = newSource;
245 DSM_twCC = TWCC_SUCCESS;
246 return TWRC_SUCCESS;
247 }
248
249 /* DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT */
250 TW_UINT16 TWAIN_UserSelect (pTW_IDENTITY pOrigin, TW_MEMREF pData)
251 {
252 pTW_IDENTITY selected = (pTW_IDENTITY)pData;
253
254 if (!nrdevices) {
255 DSM_twCC = TWCC_OPERATIONERROR;
256 return TWRC_FAILURE;
257 }
258 *selected = devices[0].identity;
259 DSM_twCC = TWCC_SUCCESS;
260 return TWRC_SUCCESS;
261 }
262
263 /* DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM */
264 TW_UINT16 TWAIN_CloseDSM (pTW_IDENTITY pOrigin, TW_MEMREF pData)
265 {
266 activeDS *currentDS = activeSources, *nextDS;
267
268 TRACE("DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM\n");
269
270 if (DSM_initialized)
271 {
272 DSM_initialized = FALSE;
273
274 /* If there are data sources still open, close them now. */
275 while (currentDS != NULL)
276 {
277 nextDS = currentDS->next;
278 currentDS->dsEntry (pOrigin, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, pData);
279 HeapFree (GetProcessHeap(), 0, currentDS);
280 currentDS = nextDS;
281 }
282 activeSources = NULL;
283 DSM_twCC = TWCC_SUCCESS;
284 return TWRC_SUCCESS;
285 } else {
286 DSM_twCC = TWCC_SEQERROR;
287 return TWRC_FAILURE;
288 }
289 }
290
291 /* DG_CONTROL/DAT_PARENT/MSG_OPENDSM */
292 TW_UINT16 TWAIN_OpenDSM (pTW_IDENTITY pOrigin, TW_MEMREF pData)
293 {
294 TW_UINT16 twRC = TWRC_SUCCESS;
295
296 TRACE("DG_CONTROL/DAT_PARENT/MSG_OPENDSM\n");
297 if (!DSM_initialized) {
298 DSM_currentDevice = 0;
299 DSM_initialized = TRUE;
300 DSM_twCC = TWCC_SUCCESS;
301 twRC = TWRC_SUCCESS;
302 } else {
303 /* operation invoked in invalid state */
304 DSM_twCC = TWCC_SEQERROR;
305 twRC = TWRC_FAILURE;
306 }
307 return twRC;
308 }
309
310 /* DG_CONTROL/DAT_STATUS/MSG_GET */
311 TW_UINT16 TWAIN_GetDSMStatus (pTW_IDENTITY pOrigin, TW_MEMREF pData)
312 {
313 pTW_STATUS pSourceStatus = (pTW_STATUS) pData;
314
315 TRACE ("DG_CONTROL/DAT_STATUS/MSG_GET\n");
316
317 pSourceStatus->ConditionCode = DSM_twCC;
318 DSM_twCC = TWCC_SUCCESS; /* clear the condition code */
319 return TWRC_SUCCESS;
320 }