sane.ds: Revise to match identity structures with partial information.
[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 #endif
145                      break;
146                 case MSG_OPENDS:
147 #ifdef SONAME_LIBSANE
148                      twRC = SANE_OpenDS( pOrigin, (pTW_IDENTITY)pData);
149 #else
150                      twRC = TWRC_FAILURE;
151 #endif
152                      break;
153                 case MSG_GET:
154 #ifdef SONAME_LIBSANE
155                      twRC = SANE_GetIdentity( pOrigin, (pTW_IDENTITY)pData);
156 #else
157                      twRC = TWRC_FAILURE;
158 #endif
159                      break;
160             }
161             break;
162         case DAT_CAPABILITY:
163             switch (MSG)
164             {
165                 case MSG_GET:
166                     twRC = SANE_CapabilityGet (pOrigin, pData);
167                     break;
168                 case MSG_GETCURRENT:
169                     twRC = SANE_CapabilityGetCurrent (pOrigin, pData);
170                     break;
171                 case MSG_GETDEFAULT:
172                     twRC = SANE_CapabilityGetDefault (pOrigin, pData);
173                     break;
174                 case MSG_QUERYSUPPORT:
175                     twRC = SANE_CapabilityQuerySupport (pOrigin, pData);
176                     break;
177                 case MSG_RESET:
178                     twRC = SANE_CapabilityReset (pOrigin, pData);
179                     break;
180                 case MSG_SET:
181                     twRC = SANE_CapabilitySet (pOrigin, pData);
182                     break;
183                 default:
184                     twRC = TWRC_FAILURE;
185                     FIXME("unrecognized opertion triplet\n");
186             }
187             break;
188
189         case DAT_CUSTOMDSDATA:
190             switch (MSG)
191             {
192                 case MSG_GET:
193                     twRC = SANE_CustomDSDataGet (pOrigin, pData);
194                     break;
195                 case MSG_SET:
196                     twRC = SANE_CustomDSDataSet (pOrigin, pData);
197                     break;
198                 default:
199                     break;
200             }
201             break;
202
203         case DAT_FILESYSTEM:
204             switch (MSG)
205             {
206                 /*case MSG_AUTOMATICCAPTUREDIRECTORY:
207                     twRC = SANE_AutomaticCaptureDirectory
208                                (pOrigin, pData);
209                     break;*/
210                 case MSG_CHANGEDIRECTORY:
211                     twRC = SANE_ChangeDirectory (pOrigin, pData);
212                     break;
213                 /*case MSG_COPY:
214                     twRC = SANE_FileSystemCopy (pOrigin, pData);
215                     break;*/
216                 case MSG_CREATEDIRECTORY:
217                     twRC = SANE_CreateDirectory (pOrigin, pData);
218                     break;
219                 case MSG_DELETE:
220                     twRC = SANE_FileSystemDelete (pOrigin, pData);
221                     break;
222                 case MSG_FORMATMEDIA:
223                     twRC = SANE_FormatMedia (pOrigin, pData);
224                     break;
225                 case MSG_GETCLOSE:
226                     twRC = SANE_FileSystemGetClose (pOrigin, pData);
227                     break;
228                 case MSG_GETFIRSTFILE:
229                     twRC = SANE_FileSystemGetFirstFile (pOrigin, pData);
230                     break;
231                 case MSG_GETINFO:
232                     twRC = SANE_FileSystemGetInfo (pOrigin, pData);
233                     break;
234                 case MSG_GETNEXTFILE:
235                     twRC = SANE_FileSystemGetNextFile (pOrigin, pData);
236                     break;
237                 case MSG_RENAME:
238                     twRC = SANE_FileSystemRename (pOrigin, pData);
239                     break;
240                 default:
241                     twRC = TWRC_FAILURE;
242                     break;
243             }
244             break;
245
246         case DAT_EVENT:
247             if (MSG == MSG_PROCESSEVENT)
248                 twRC = SANE_ProcessEvent (pOrigin, pData);
249             else
250                 twRC = TWRC_FAILURE;
251             break;
252
253         case DAT_PASSTHRU:
254             if (MSG == MSG_PASSTHRU)
255                 twRC = SANE_PassThrough (pOrigin, pData);
256             else
257                 twRC = TWRC_FAILURE;
258             break;
259
260         case DAT_PENDINGXFERS:
261             switch (MSG)
262             {
263                 case MSG_ENDXFER:
264                     twRC = SANE_PendingXfersEndXfer (pOrigin, pData);
265                     break;
266                 case MSG_GET:
267                     twRC = SANE_PendingXfersGet (pOrigin, pData);
268                     break;
269                 case MSG_RESET:
270                     twRC = SANE_PendingXfersReset (pOrigin, pData);
271                     break;
272                 /*case MSG_STOPFEEDER:
273                     twRC = SANE_PendingXfersStopFeeder (pOrigin, pData);
274                     break;*/
275                 default:
276                     twRC = TWRC_FAILURE;
277             }
278             break;
279
280         case DAT_SETUPFILEXFER:
281             switch (MSG)
282             {
283                 case MSG_GET:
284                     twRC = SANE_SetupFileXferGet (pOrigin, pData);
285                     break;
286                 case MSG_GETDEFAULT:
287                     twRC = SANE_SetupFileXferGetDefault (pOrigin, pData);
288                     break;
289                 case MSG_RESET:
290                     twRC = SANE_SetupFileXferReset (pOrigin, pData);
291                     break;
292                 case MSG_SET:
293                     twRC = SANE_SetupFileXferSet (pOrigin, pData);
294                     break;
295                 default:
296                     twRC = TWRC_FAILURE;
297                     break;
298             }
299             break;
300
301         /*case DAT_SETUPFILEXFER2:
302             switch (MSG)
303             {
304                 case MSG_GET:
305                     twRC = SANE_SetupFileXfer2Get (pOrigin, pData);
306                     break;
307                 case MSG_GETDEFAULT:
308                     twRC = SANE_SetupFileXfer2GetDefault (pOrigin, pData);
309                     break;
310                 case MSG_RESET:
311                     twRC = SANE_SetupFileXfer2Reset (pOrigin, pData);
312                     break;
313                 case MSG_SET:
314                     twRC = SANE_SetupFileXfer2Set (pOrigin, pData);
315                     break;
316             }
317             break;*/
318         case DAT_SETUPMEMXFER:
319             if (MSG == MSG_GET)
320                 twRC = SANE_SetupMemXferGet (pOrigin, pData);
321             else
322                 twRC = TWRC_FAILURE;
323             break;
324
325         case DAT_STATUS:
326             if (MSG == MSG_GET)
327                 twRC = SANE_GetDSStatus (pOrigin, pData);
328             else
329                 twRC = TWRC_FAILURE;
330             break;
331
332         case DAT_USERINTERFACE:
333             switch (MSG)
334             {
335                 case MSG_DISABLEDS:
336                     twRC = SANE_DisableDSUserInterface (pOrigin, pData);
337                     break;
338                 case MSG_ENABLEDS:
339                     twRC = SANE_EnableDSUserInterface (pOrigin, pData);
340                     break;
341                 case MSG_ENABLEDSUIONLY:
342                     twRC = SANE_EnableDSUIOnly (pOrigin, pData);
343                     break;
344                 default:
345                     twRC = TWRC_FAILURE;
346                     break;
347             }
348             break;
349
350         case DAT_XFERGROUP:
351             switch (MSG)
352             {
353                 case MSG_GET:
354                     twRC = SANE_XferGroupGet (pOrigin, pData);
355                     break;
356                 case MSG_SET:
357                     twRC = SANE_XferGroupSet (pOrigin, pData);
358                     break;
359                 default:
360                     twRC = TWRC_FAILURE;
361                     break;
362             }
363             break;
364
365         default:
366             FIXME("code unknown: %d\n", DAT);
367             twRC = TWRC_FAILURE;
368             break;
369     }
370
371     return twRC;
372 }
373
374
375 static TW_UINT16 SANE_ImageGroupHandler (
376            pTW_IDENTITY pOrigin,
377            TW_UINT16    DAT,
378            TW_UINT16    MSG,
379            TW_MEMREF    pData)
380 {
381     TW_UINT16 twRC = TWRC_SUCCESS;
382
383     switch (DAT)
384     {
385         case DAT_CIECOLOR:
386             if (MSG == MSG_GET)
387                 twRC = SANE_CIEColorGet (pOrigin, pData);
388             else
389                 twRC = TWRC_FAILURE;
390             break;
391
392         case DAT_EXTIMAGEINFO:
393             if (MSG == MSG_GET)
394                 twRC = SANE_ExtImageInfoGet (pOrigin, pData);
395             else
396                 twRC = TWRC_FAILURE;
397             break;
398
399         case DAT_GRAYRESPONSE:
400             switch (MSG)
401             {
402                 case MSG_RESET:
403                     twRC = SANE_GrayResponseReset (pOrigin, pData);
404                     break;
405                 case MSG_SET:
406                     twRC = SANE_GrayResponseSet (pOrigin, pData);
407                     break;
408                 default:
409                     twRC = TWRC_FAILURE;
410                     activeDS.twCC = TWCC_BADPROTOCOL;
411                     FIXME("unrecognized operation triplet\n");
412                     break;
413             }
414             break;
415         case DAT_IMAGEFILEXFER:
416             if (MSG == MSG_GET)
417                 twRC = SANE_ImageFileXferGet (pOrigin, pData);
418             else
419                 twRC = TWRC_FAILURE;
420             break;
421
422         case DAT_IMAGEINFO:
423             if (MSG == MSG_GET)
424                 twRC = SANE_ImageInfoGet (pOrigin, pData);
425             else
426                 twRC = TWRC_FAILURE;
427             break;
428
429         case DAT_IMAGELAYOUT:
430             switch (MSG)
431             {
432                 case MSG_GET:
433                     twRC = SANE_ImageLayoutGet (pOrigin, pData);
434                     break;
435                 case MSG_GETDEFAULT:
436                     twRC = SANE_ImageLayoutGetDefault (pOrigin, pData);
437                     break;
438                 case MSG_RESET:
439                     twRC = SANE_ImageLayoutReset (pOrigin, pData);
440                     break;
441                 case MSG_SET:
442                     twRC = SANE_ImageLayoutSet (pOrigin, pData);
443                     break;
444                 default:
445                     twRC = TWRC_FAILURE;
446                     activeDS.twCC = TWCC_BADPROTOCOL;
447                     ERR("unrecognized operation triplet\n");
448                     break;
449             }
450             break;
451
452         case DAT_IMAGEMEMXFER:
453             if (MSG == MSG_GET)
454                 twRC = SANE_ImageMemXferGet (pOrigin, pData);
455             else
456                 twRC = TWRC_FAILURE;
457             break;
458
459         case DAT_IMAGENATIVEXFER:
460             if (MSG == MSG_GET)
461                 twRC = SANE_ImageNativeXferGet (pOrigin, pData);
462             else
463                 twRC = TWRC_FAILURE;
464             break;
465
466         case DAT_JPEGCOMPRESSION:
467             switch (MSG)
468             {
469                 case MSG_GET:
470                     twRC = SANE_JPEGCompressionGet (pOrigin, pData);
471                     break;
472                 case MSG_GETDEFAULT:
473                     twRC = SANE_JPEGCompressionGetDefault (pOrigin, pData);
474                     break;
475                 case MSG_RESET:
476                     twRC = SANE_JPEGCompressionReset (pOrigin, pData);
477                     break;
478                 case MSG_SET:
479                     twRC = SANE_JPEGCompressionSet (pOrigin, pData);
480                     break;
481                 default:
482                     twRC = TWRC_FAILURE;
483                     activeDS.twCC = TWCC_BADPROTOCOL;
484                     WARN("unrecognized operation triplet\n");
485                     break;
486             }
487             break;
488
489         case DAT_PALETTE8:
490             switch (MSG)
491             {
492                 case MSG_GET:
493                     twRC = SANE_Palette8Get (pOrigin, pData);
494                     break;
495                 case MSG_GETDEFAULT:
496                     twRC = SANE_Palette8GetDefault (pOrigin, pData);
497                     break;
498                 case MSG_RESET:
499                     twRC = SANE_Palette8Reset (pOrigin, pData);
500                     break;
501                 case MSG_SET:
502                     twRC = SANE_Palette8Set (pOrigin, pData);
503                     break;
504                 default:
505                     twRC = TWRC_FAILURE;
506                     activeDS.twCC = TWCC_BADPROTOCOL;
507                     WARN("unrecognized operation triplet\n");
508             }
509             break;
510
511         case DAT_RGBRESPONSE:
512             switch (MSG)
513             {
514                 case MSG_RESET:
515                     twRC = SANE_RGBResponseReset (pOrigin, pData);
516                     break;
517                 case MSG_SET:
518                     twRC = SANE_RGBResponseSet (pOrigin, pData);
519                     break;
520                 default:
521                     twRC = TWRC_FAILURE;
522                     activeDS.twCC = TWCC_BADPROTOCOL;
523                     WARN("unrecognized operation triplet\n");
524                     break;
525             }
526             break;
527
528         default:
529             twRC = TWRC_FAILURE;
530             activeDS.twCC = TWCC_BADPROTOCOL;
531             FIXME("unrecognized DG type %d\n", DAT);
532     }
533     return twRC;
534 }
535
536 /* Main entry point for the TWAIN library */
537 TW_UINT16 WINAPI
538 DS_Entry ( pTW_IDENTITY pOrigin,
539            TW_UINT32    DG,
540            TW_UINT16    DAT,
541            TW_UINT16    MSG,
542            TW_MEMREF    pData)
543 {
544     TW_UINT16 twRC = TWRC_SUCCESS;  /* Return Code */
545
546     TRACE("(DG=%d DAT=%d MSG=%d)\n", DG, DAT, MSG);
547
548     switch (DG)
549     {
550         case DG_CONTROL:
551             twRC = SANE_SourceControlHandler (pOrigin,DAT,MSG,pData);
552             break;
553         case DG_IMAGE:
554             twRC = SANE_ImageGroupHandler (pOrigin,DAT,MSG,pData);
555             break;
556         case DG_AUDIO:
557             FIXME("Audio group of controls not implemented yet.\n");
558         default:
559             activeDS.twCC = TWCC_BADPROTOCOL;
560             twRC = TWRC_FAILURE;
561     }
562
563     return twRC;
564 }
565
566 #ifdef SONAME_LIBSANE
567 /* Sane returns device names that are longer than the 32 bytes allowed
568    by TWAIN.  However, it colon separates them, and the last bit is
569    the most interesting.  So we use the last bit, and add a signature
570    to ensure uniqueness */
571 static void copy_sane_short_name(const char *in, char *out, size_t outsize)
572 {
573     const char *p;
574     int  signature = 0;
575
576     if (strlen(in) <= outsize - 1)
577     {
578         strcpy(out, in);
579         return;
580     }
581
582     for (p = in; *p; p++)
583         signature += *p;
584
585     p = strrchr(in, ':');
586     if (!p)
587         p = in;
588     else
589         p++;
590
591     if (strlen(p) > outsize - 7 - 1)
592         p += strlen(p) - (outsize - 7 - 1);
593
594     strcpy(out, p);
595     sprintf(out + strlen(out), "(%04X)", signature % 0x10000);
596
597 }
598
599 static const SANE_Device **sane_devlist;
600
601 static void
602 detect_sane_devices(void) {
603     if (sane_devlist && sane_devlist[0]) return;
604     TRACE("detecting sane...\n");
605     if (psane_get_devices (&sane_devlist, SANE_FALSE) != SANE_STATUS_GOOD)
606         return;
607 }
608
609 static TW_UINT16
610 SANE_GetIdentity( pTW_IDENTITY pOrigin, pTW_IDENTITY self) {
611     static int cursanedev = 0;
612
613     detect_sane_devices();
614     if (!sane_devlist[cursanedev])
615         return TWRC_FAILURE;
616     self->ProtocolMajor = TWON_PROTOCOLMAJOR;
617     self->ProtocolMinor = TWON_PROTOCOLMINOR;
618     self->SupportedGroups = DG_CONTROL | DG_IMAGE;
619     copy_sane_short_name(sane_devlist[cursanedev]->name, self->ProductName, sizeof(self->ProductName) - 1);
620     lstrcpynA (self->Manufacturer, sane_devlist[cursanedev]->vendor, sizeof(self->Manufacturer) - 1);
621     lstrcpynA (self->ProductFamily, sane_devlist[cursanedev]->model, sizeof(self->ProductFamily) - 1);
622     cursanedev++;
623
624     if (!sane_devlist[cursanedev]               ||
625         !sane_devlist[cursanedev]->model        ||
626         !sane_devlist[cursanedev]->vendor       ||
627         !sane_devlist[cursanedev]->name
628     )
629         cursanedev = 0; /* wrap to begin */
630     return TWRC_SUCCESS;
631 }
632
633 static TW_UINT16 SANE_OpenDS( pTW_IDENTITY pOrigin, pTW_IDENTITY self) {
634     SANE_Status status;
635     int i;
636
637     detect_sane_devices();
638     if (!sane_devlist[0]) {
639         ERR("No scanners? We should not get to OpenDS?\n");
640         return TWRC_FAILURE;
641     }
642
643     for (i=0; sane_devlist[i] && sane_devlist[i]->model; i++) {
644         TW_STR32 name;
645
646         /* To make string as short as above */
647         lstrcpynA(name, sane_devlist[i]->vendor, sizeof(name)-1);
648         if (self->Manufacturer && *self->Manufacturer && strcmp(name, self->Manufacturer))
649             continue;
650         lstrcpynA(name, sane_devlist[i]->model, sizeof(name)-1);
651         if (self->ProductFamily && *self->ProductFamily && strcmp(name, self->ProductFamily))
652             continue;
653         copy_sane_short_name(sane_devlist[i]->name, name, sizeof(name) - 1);
654         if (self->ProductName && *self->ProductName && strcmp(name, self->ProductName))
655             continue;
656         break;
657     }
658     if (!sane_devlist[i]) {
659         FIXME("Scanner not found.\n");
660         return TWRC_FAILURE;
661     }
662     status = psane_open(sane_devlist[i]->name,&activeDS.deviceHandle);
663     if (status == SANE_STATUS_GOOD) {
664         activeDS.currentState = 4;
665         activeDS.twCC = TWRC_SUCCESS;
666         return TWRC_SUCCESS;
667     }
668     FIXME("sane_open(%s): %s\n", sane_devlist[i]->name, psane_strstatus (status));
669     return TWRC_FAILURE;
670 }
671 #endif