imagehlp/tests: Run tests again on Win95.
[wine] / dlls / sane.ds / sane_main.c
1 /*
2  * SANE.DS functions
3  *
4  * Copyright 2000 Shi Quan He <shiquan@cyberdude.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <stdarg.h>
25 #include <stdio.h>
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "twain.h"
30 #include "sane_i.h"
31 #include "wine/debug.h"
32 #include "wine/library.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(twain);
35
36 HINSTANCE SANE_instance;
37
38 #ifdef SONAME_LIBSANE
39
40 static void *libsane_handle;
41
42 static void close_libsane(void *h)
43 {
44     if (h)
45         wine_dlclose(h, NULL, 0);
46 }
47
48 static void *open_libsane(void)
49 {
50     void *h;
51
52     h = wine_dlopen(SONAME_LIBSANE, RTLD_GLOBAL | RTLD_NOW, NULL, 0);
53     if (!h)
54     {
55         WARN("dlopen(%s) failed\n", SONAME_LIBSANE);
56         return NULL;
57     }
58
59 #define LOAD_FUNCPTR(f) \
60     if((p##f = wine_dlsym(h, #f, NULL, 0)) == NULL) { \
61         close_libsane(h); \
62         ERR("Could not dlsym %s\n", #f); \
63         return NULL; \
64     }
65
66     LOAD_FUNCPTR(sane_init)
67     LOAD_FUNCPTR(sane_exit)
68     LOAD_FUNCPTR(sane_get_devices)
69     LOAD_FUNCPTR(sane_open)
70     LOAD_FUNCPTR(sane_close)
71     LOAD_FUNCPTR(sane_get_option_descriptor)
72     LOAD_FUNCPTR(sane_control_option)
73     LOAD_FUNCPTR(sane_get_parameters)
74     LOAD_FUNCPTR(sane_start)
75     LOAD_FUNCPTR(sane_read)
76     LOAD_FUNCPTR(sane_cancel)
77     LOAD_FUNCPTR(sane_set_io_mode)
78     LOAD_FUNCPTR(sane_get_select_fd)
79     LOAD_FUNCPTR(sane_strstatus)
80 #undef LOAD_FUNCPTR
81
82     return h;
83 }
84
85 #endif /* SONAME_LIBSANE */
86
87 BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
88 {
89     TRACE("%p,%x,%p\n", hinstDLL, fdwReason, lpvReserved);
90
91     switch (fdwReason)
92     {
93         case DLL_PROCESS_ATTACH: {
94 #ifdef SONAME_LIBSANE
95             SANE_Status status;
96             SANE_Int version_code;
97
98             libsane_handle = open_libsane();
99             if (! libsane_handle)
100                 return FALSE;
101
102             status = psane_init (&version_code, NULL);
103 #endif
104             SANE_instance = hinstDLL;
105             DisableThreadLibraryCalls(hinstDLL);
106             break;
107         }
108         case DLL_PROCESS_DETACH:
109 #ifdef SONAME_LIBSANE
110             TRACE("calling sane_exit()\n");
111             psane_exit ();
112
113             close_libsane(libsane_handle);
114             libsane_handle = NULL;
115 #endif 
116             SANE_instance = NULL;
117             break;
118     }
119
120     return TRUE;
121 }
122
123 #ifdef SONAME_LIBSANE
124 static TW_UINT16 SANE_GetIdentity( pTW_IDENTITY, pTW_IDENTITY);
125 static TW_UINT16 SANE_OpenDS( pTW_IDENTITY, pTW_IDENTITY);
126 #endif
127
128 static TW_UINT16 SANE_SourceControlHandler (
129            pTW_IDENTITY pOrigin,
130            TW_UINT16    DAT,
131            TW_UINT16    MSG,
132            TW_MEMREF    pData)
133 {
134     TW_UINT16 twRC = TWRC_SUCCESS;
135
136     switch (DAT)
137     {
138         case DAT_IDENTITY:
139             switch (MSG)
140             {
141                 case MSG_CLOSEDS:
142 #ifdef SONAME_LIBSANE
143                      psane_close (activeDS.deviceHandle);
144 #else
145                      twRC = TWRC_FAILURE;
146                      activeDS.twCC = TWCC_CAPUNSUPPORTED;
147 #endif
148                      break;
149                 case MSG_OPENDS:
150 #ifdef SONAME_LIBSANE
151                      twRC = SANE_OpenDS( pOrigin, (pTW_IDENTITY)pData);
152 #else
153                      twRC = TWRC_FAILURE;
154                      activeDS.twCC = TWCC_CAPUNSUPPORTED;
155 #endif
156                      break;
157                 case MSG_GET:
158 #ifdef SONAME_LIBSANE
159                      twRC = SANE_GetIdentity( pOrigin, (pTW_IDENTITY)pData);
160 #else
161                      twRC = TWRC_FAILURE;
162                      activeDS.twCC = TWCC_CAPUNSUPPORTED;
163 #endif
164                      break;
165             }
166             break;
167         case DAT_CAPABILITY:
168             switch (MSG)
169             {
170                 case MSG_GET:
171                     twRC = SANE_CapabilityGet (pOrigin, pData);
172                     break;
173                 case MSG_GETCURRENT:
174                     twRC = SANE_CapabilityGetCurrent (pOrigin, pData);
175                     break;
176                 case MSG_GETDEFAULT:
177                     twRC = SANE_CapabilityGetDefault (pOrigin, pData);
178                     break;
179                 case MSG_QUERYSUPPORT:
180                     twRC = SANE_CapabilityQuerySupport (pOrigin, pData);
181                     break;
182                 case MSG_RESET:
183                     twRC = SANE_CapabilityReset (pOrigin, pData);
184                     break;
185                 case MSG_SET:
186                     twRC = SANE_CapabilitySet (pOrigin, pData);
187                     break;
188                 default:
189                     twRC = TWRC_FAILURE;
190                     activeDS.twCC = TWCC_CAPBADOPERATION;
191                     FIXME("unrecognized opertion triplet\n");
192                     break;
193             }
194             break;
195
196         case DAT_EVENT:
197             if (MSG == MSG_PROCESSEVENT)
198                 twRC = SANE_ProcessEvent (pOrigin, pData);
199             else
200             {
201                 activeDS.twCC = TWCC_CAPBADOPERATION;
202                 twRC = TWRC_FAILURE;
203             }
204             break;
205
206         case DAT_PENDINGXFERS:
207             switch (MSG)
208             {
209                 case MSG_ENDXFER:
210                     twRC = SANE_PendingXfersEndXfer (pOrigin, pData);
211                     break;
212                 case MSG_GET:
213                     twRC = SANE_PendingXfersGet (pOrigin, pData);
214                     break;
215                 case MSG_RESET:
216                     twRC = SANE_PendingXfersReset (pOrigin, pData);
217                     break;
218                 default:
219                     activeDS.twCC = TWCC_CAPBADOPERATION;
220                     twRC = TWRC_FAILURE;
221             }
222             break;
223
224         case DAT_SETUPMEMXFER:
225             if (MSG == MSG_GET)
226                 twRC = SANE_SetupMemXferGet (pOrigin, pData);
227             else
228             {
229                 activeDS.twCC = TWCC_CAPBADOPERATION;
230                 twRC = TWRC_FAILURE;
231             }
232             break;
233
234         case DAT_STATUS:
235             if (MSG == MSG_GET)
236                 twRC = SANE_GetDSStatus (pOrigin, pData);
237             else
238             {
239                 activeDS.twCC = TWCC_CAPBADOPERATION;
240                 twRC = TWRC_FAILURE;
241             }
242             break;
243
244         case DAT_USERINTERFACE:
245             switch (MSG)
246             {
247                 case MSG_DISABLEDS:
248                     twRC = SANE_DisableDSUserInterface (pOrigin, pData);
249                     break;
250                 case MSG_ENABLEDS:
251                     twRC = SANE_EnableDSUserInterface (pOrigin, pData);
252                     break;
253                 case MSG_ENABLEDSUIONLY:
254                     twRC = SANE_EnableDSUIOnly (pOrigin, pData);
255                     break;
256                 default:
257                     activeDS.twCC = TWCC_CAPBADOPERATION;
258                     twRC = TWRC_FAILURE;
259                     break;
260             }
261             break;
262
263         case DAT_XFERGROUP:
264             switch (MSG)
265             {
266                 case MSG_GET:
267                     twRC = SANE_XferGroupGet (pOrigin, pData);
268                     break;
269                 case MSG_SET:
270                     twRC = SANE_XferGroupSet (pOrigin, pData);
271                     break;
272                 default:
273                     activeDS.twCC = TWCC_CAPBADOPERATION;
274                     twRC = TWRC_FAILURE;
275                     break;
276             }
277             break;
278
279         default:
280             WARN("code unsupported: %d\n", DAT);
281             activeDS.twCC = TWCC_CAPUNSUPPORTED;
282             twRC = TWRC_FAILURE;
283             break;
284     }
285
286     return twRC;
287 }
288
289
290 static TW_UINT16 SANE_ImageGroupHandler (
291            pTW_IDENTITY pOrigin,
292            TW_UINT16    DAT,
293            TW_UINT16    MSG,
294            TW_MEMREF    pData)
295 {
296     TW_UINT16 twRC = TWRC_SUCCESS;
297
298     switch (DAT)
299     {
300         case DAT_IMAGEINFO:
301             if (MSG == MSG_GET)
302                 twRC = SANE_ImageInfoGet (pOrigin, pData);
303             else
304             {
305                 activeDS.twCC = TWCC_CAPBADOPERATION;
306                 twRC = TWRC_FAILURE;
307             }
308             break;
309
310         case DAT_IMAGELAYOUT:
311             switch (MSG)
312             {
313                 case MSG_GET:
314                     twRC = SANE_ImageLayoutGet (pOrigin, pData);
315                     break;
316                 case MSG_GETDEFAULT:
317                     twRC = SANE_ImageLayoutGetDefault (pOrigin, pData);
318                     break;
319                 case MSG_RESET:
320                     twRC = SANE_ImageLayoutReset (pOrigin, pData);
321                     break;
322                 case MSG_SET:
323                     twRC = SANE_ImageLayoutSet (pOrigin, pData);
324                     break;
325                 default:
326                     twRC = TWRC_FAILURE;
327                     activeDS.twCC = TWCC_CAPBADOPERATION;
328                     ERR("unrecognized operation triplet\n");
329                     break;
330             }
331             break;
332
333         case DAT_IMAGEMEMXFER:
334             if (MSG == MSG_GET)
335                 twRC = SANE_ImageMemXferGet (pOrigin, pData);
336             else
337             {
338                 activeDS.twCC = TWCC_CAPBADOPERATION;
339                 twRC = TWRC_FAILURE;
340             }
341             break;
342
343         case DAT_IMAGENATIVEXFER:
344             if (MSG == MSG_GET)
345                 twRC = SANE_ImageNativeXferGet (pOrigin, pData);
346             else
347             {
348                 activeDS.twCC = TWCC_CAPBADOPERATION;
349                 twRC = TWRC_FAILURE;
350             }
351             break;
352
353         default:
354             twRC = TWRC_FAILURE;
355             activeDS.twCC = TWCC_CAPUNSUPPORTED;
356             WARN("unsupported DG type %d\n", DAT);
357             break;
358     }
359     return twRC;
360 }
361
362 /* Main entry point for the TWAIN library */
363 TW_UINT16 WINAPI
364 DS_Entry ( pTW_IDENTITY pOrigin,
365            TW_UINT32    DG,
366            TW_UINT16    DAT,
367            TW_UINT16    MSG,
368            TW_MEMREF    pData)
369 {
370     TW_UINT16 twRC = TWRC_SUCCESS;  /* Return Code */
371
372     TRACE("(DG=%d DAT=%d MSG=%d)\n", DG, DAT, MSG);
373
374     switch (DG)
375     {
376         case DG_CONTROL:
377             twRC = SANE_SourceControlHandler (pOrigin,DAT,MSG,pData);
378             break;
379         case DG_IMAGE:
380             twRC = SANE_ImageGroupHandler (pOrigin,DAT,MSG,pData);
381             break;
382         case DG_AUDIO:
383             WARN("Audio group of controls not implemented yet.\n");
384             twRC = TWRC_FAILURE;
385             activeDS.twCC = TWCC_CAPUNSUPPORTED;
386             break;
387         default:
388             activeDS.twCC = TWCC_BADPROTOCOL;
389             twRC = TWRC_FAILURE;
390     }
391
392     return twRC;
393 }
394
395 #ifdef SONAME_LIBSANE
396 /* Sane returns device names that are longer than the 32 bytes allowed
397    by TWAIN.  However, it colon separates them, and the last bit is
398    the most interesting.  So we use the last bit, and add a signature
399    to ensure uniqueness */
400 static void copy_sane_short_name(const char *in, char *out, size_t outsize)
401 {
402     const char *p;
403     int  signature = 0;
404
405     if (strlen(in) <= outsize - 1)
406     {
407         strcpy(out, in);
408         return;
409     }
410
411     for (p = in; *p; p++)
412         signature += *p;
413
414     p = strrchr(in, ':');
415     if (!p)
416         p = in;
417     else
418         p++;
419
420     if (strlen(p) > outsize - 7 - 1)
421         p += strlen(p) - (outsize - 7 - 1);
422
423     strcpy(out, p);
424     sprintf(out + strlen(out), "(%04X)", signature % 0x10000);
425
426 }
427
428 static const SANE_Device **sane_devlist;
429
430 static void
431 detect_sane_devices(void) {
432     if (sane_devlist && sane_devlist[0]) return;
433     TRACE("detecting sane...\n");
434     if (psane_get_devices (&sane_devlist, SANE_FALSE) != SANE_STATUS_GOOD)
435         return;
436 }
437
438 static TW_UINT16
439 SANE_GetIdentity( pTW_IDENTITY pOrigin, pTW_IDENTITY self) {
440     static int cursanedev = 0;
441
442     detect_sane_devices();
443     if (!sane_devlist[cursanedev])
444         return TWRC_FAILURE;
445     self->ProtocolMajor = TWON_PROTOCOLMAJOR;
446     self->ProtocolMinor = TWON_PROTOCOLMINOR;
447     self->SupportedGroups = DG_CONTROL | DG_IMAGE;
448     copy_sane_short_name(sane_devlist[cursanedev]->name, self->ProductName, sizeof(self->ProductName) - 1);
449     lstrcpynA (self->Manufacturer, sane_devlist[cursanedev]->vendor, sizeof(self->Manufacturer) - 1);
450     lstrcpynA (self->ProductFamily, sane_devlist[cursanedev]->model, sizeof(self->ProductFamily) - 1);
451     cursanedev++;
452
453     if (!sane_devlist[cursanedev]               ||
454         !sane_devlist[cursanedev]->model        ||
455         !sane_devlist[cursanedev]->vendor       ||
456         !sane_devlist[cursanedev]->name
457     )
458         cursanedev = 0; /* wrap to begin */
459     return TWRC_SUCCESS;
460 }
461
462 static TW_UINT16 SANE_OpenDS( pTW_IDENTITY pOrigin, pTW_IDENTITY self) {
463     SANE_Status status;
464     int i;
465
466     detect_sane_devices();
467     if (!sane_devlist[0]) {
468         ERR("No scanners? We should not get to OpenDS?\n");
469         return TWRC_FAILURE;
470     }
471
472     for (i=0; sane_devlist[i] && sane_devlist[i]->model; i++) {
473         TW_STR32 name;
474
475         /* To make string as short as above */
476         lstrcpynA(name, sane_devlist[i]->vendor, sizeof(name)-1);
477         if (self->Manufacturer && *self->Manufacturer && strcmp(name, self->Manufacturer))
478             continue;
479         lstrcpynA(name, sane_devlist[i]->model, sizeof(name)-1);
480         if (self->ProductFamily && *self->ProductFamily && strcmp(name, self->ProductFamily))
481             continue;
482         copy_sane_short_name(sane_devlist[i]->name, name, sizeof(name) - 1);
483         if (self->ProductName && *self->ProductName && strcmp(name, self->ProductName))
484             continue;
485         break;
486     }
487     if (!sane_devlist[i]) {
488         WARN("Scanner not found.\n");
489         return TWRC_FAILURE;
490     }
491     status = psane_open(sane_devlist[i]->name,&activeDS.deviceHandle);
492     if (status == SANE_STATUS_GOOD) {
493         activeDS.twCC = SANE_SaneSetDefaults();
494         if (activeDS.twCC == TWCC_SUCCESS) {
495             activeDS.currentState = 4;
496             return TWRC_SUCCESS;
497         }
498         else
499             psane_close(activeDS.deviceHandle);
500     }
501     else
502         ERR("sane_open(%s): %s\n", sane_devlist[i]->name, psane_strstatus (status));
503     return TWRC_FAILURE;
504 }
505 #endif