335 lines
9.4 KiB
C
335 lines
9.4 KiB
C
/*
|
|
* TWAIN32 Source Manager
|
|
*
|
|
* Copyright 2000 Corel Corporation
|
|
* Copyright 2006 Marcus Meissner
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
|
|
#define NONAMELESSUNION
|
|
#define NONAMELESSSTRUCT
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "twain.h"
|
|
#include "twain_i.h"
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(twain);
|
|
|
|
static TW_UINT16 DSM_initialized; /* whether Source Manager is initialized */
|
|
static TW_UINT32 DSM_sourceId; /* source id generator */
|
|
static TW_UINT16 DSM_currentDevice; /* keep track of device during enumeration */
|
|
|
|
struct all_devices {
|
|
char *modname;
|
|
TW_IDENTITY identity;
|
|
};
|
|
|
|
static int nrdevices = 0;
|
|
static struct all_devices *devices = NULL;
|
|
|
|
static void
|
|
twain_add_onedriver(const char *dsname) {
|
|
HMODULE hmod;
|
|
DSENTRYPROC dsEntry;
|
|
TW_IDENTITY fakeOrigin;
|
|
TW_IDENTITY sourceId;
|
|
TW_UINT16 ret;
|
|
|
|
hmod = LoadLibraryA(dsname);
|
|
if (!hmod) {
|
|
ERR("Failed to load TWAIN Source %s\n", dsname);
|
|
return;
|
|
}
|
|
dsEntry = (DSENTRYPROC)GetProcAddress(hmod, "DS_Entry");
|
|
if (!dsEntry) {
|
|
ERR("Failed to find DS_Entry() in TWAIN DS %s\n", dsname);
|
|
return;
|
|
}
|
|
/* Loop to do multiple detects, mostly for sane.ds and gphoto2.ds */
|
|
do {
|
|
int i;
|
|
|
|
sourceId.Id = DSM_sourceId;
|
|
sourceId.ProtocolMajor = TWON_PROTOCOLMAJOR;
|
|
sourceId.ProtocolMinor = TWON_PROTOCOLMINOR;
|
|
ret = dsEntry (&fakeOrigin, DG_CONTROL, DAT_IDENTITY, MSG_GET, &sourceId);
|
|
if (ret != TWRC_SUCCESS) {
|
|
ERR("Source->(DG_CONTROL,DAT_IDENTITY,MSG_GET) failed!\n");
|
|
return;
|
|
}
|
|
TRACE("Manufacturer: %s\n", debugstr_a(sourceId.Manufacturer));
|
|
TRACE("ProductFamily: %s\n", debugstr_a(sourceId.ProductFamily));
|
|
TRACE("ProductName: %s\n", debugstr_a(sourceId.ProductName));
|
|
|
|
for (i=0;i<nrdevices;i++) {
|
|
if (!strcmp(sourceId.ProductName,devices[i].identity.ProductName))
|
|
break;
|
|
}
|
|
if (i < nrdevices)
|
|
break;
|
|
if (nrdevices)
|
|
devices = HeapReAlloc(GetProcessHeap(), 0, devices, sizeof(devices[0])*(nrdevices+1));
|
|
else
|
|
devices = HeapAlloc(GetProcessHeap(), 0, sizeof(devices[0]));
|
|
if ((devices[nrdevices].modname = HeapAlloc(GetProcessHeap(), 0, strlen(dsname) + 1)))
|
|
lstrcpyA(devices[nrdevices].modname, dsname);
|
|
devices[nrdevices].identity = sourceId;
|
|
nrdevices++;
|
|
DSM_sourceId++;
|
|
} while (1);
|
|
FreeLibrary (hmod);
|
|
}
|
|
|
|
static BOOL detectionrun = FALSE;
|
|
|
|
static void
|
|
twain_autodetect(void) {
|
|
if (detectionrun) return;
|
|
detectionrun = TRUE;
|
|
|
|
twain_add_onedriver("sane.ds");
|
|
twain_add_onedriver("gphoto2.ds");
|
|
#if 0
|
|
twain_add_onedriver("c:\\windows\\Twain_32\\Largan\\sp503a.ds");
|
|
twain_add_onedriver("c:\\windows\\Twain_32\\vivicam10\\vivicam10.ds");
|
|
twain_add_onedriver("c:\\windows\\Twain_32\\ws30slim\\sp500a.ds");
|
|
#endif
|
|
}
|
|
|
|
/* DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS */
|
|
TW_UINT16 TWAIN_CloseDS (pTW_IDENTITY pOrigin, TW_MEMREF pData)
|
|
{
|
|
TW_UINT16 twRC = TWRC_SUCCESS;
|
|
pTW_IDENTITY pIdentity = (pTW_IDENTITY) pData;
|
|
activeDS *currentDS = NULL, *prevDS = NULL;
|
|
|
|
TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS\n");
|
|
|
|
for (currentDS = activeSources; currentDS; currentDS = currentDS->next) {
|
|
if (currentDS->identity.Id == pIdentity->Id)
|
|
break;
|
|
prevDS = currentDS;
|
|
}
|
|
if (!currentDS) {
|
|
DSM_twCC = TWCC_NODS;
|
|
return TWRC_FAILURE;
|
|
}
|
|
twRC = currentDS->dsEntry (pOrigin, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, pData);
|
|
/* This causes crashes due to still open Windows, so leave out for now.
|
|
* FreeLibrary (currentDS->hmod);
|
|
*/
|
|
if (prevDS)
|
|
prevDS->next = currentDS->next;
|
|
else
|
|
activeSources = currentDS->next;
|
|
HeapFree (GetProcessHeap(), 0, currentDS);
|
|
if (twRC == TWRC_SUCCESS)
|
|
DSM_twCC = TWCC_SUCCESS;
|
|
else /* FIXME: unclear how to get TWCC */
|
|
DSM_twCC = TWCC_SEQERROR;
|
|
return twRC;
|
|
}
|
|
|
|
/* DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT */
|
|
TW_UINT16 TWAIN_IdentityGetDefault (pTW_IDENTITY pOrigin, TW_MEMREF pData)
|
|
{
|
|
pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
|
|
|
|
TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT\n");
|
|
DSM_twCC = TWCC_NODS;
|
|
twain_autodetect();
|
|
if (!nrdevices)
|
|
return TWRC_FAILURE;
|
|
*pSourceIdentity = devices[0].identity;
|
|
DSM_twCC = TWCC_SUCCESS;
|
|
return TWRC_SUCCESS;
|
|
}
|
|
|
|
/* DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST */
|
|
TW_UINT16 TWAIN_IdentityGetFirst (pTW_IDENTITY pOrigin, TW_MEMREF pData)
|
|
{
|
|
pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
|
|
|
|
TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST\n");
|
|
twain_autodetect();
|
|
if (!nrdevices) {
|
|
TRACE ("no entries found.\n");
|
|
DSM_twCC = TWCC_NODS;
|
|
return TWRC_FAILURE;
|
|
}
|
|
DSM_currentDevice = 0;
|
|
*pSourceIdentity = devices[DSM_currentDevice++].identity;
|
|
return TWRC_SUCCESS;
|
|
}
|
|
|
|
/* DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT */
|
|
TW_UINT16 TWAIN_IdentityGetNext (pTW_IDENTITY pOrigin, TW_MEMREF pData)
|
|
{
|
|
pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
|
|
|
|
TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT\n");
|
|
if (!nrdevices || (DSM_currentDevice == nrdevices)) {
|
|
DSM_twCC = TWCC_SUCCESS;
|
|
return TWRC_ENDOFLIST;
|
|
}
|
|
*pSourceIdentity = devices[DSM_currentDevice++].identity;
|
|
return TWRC_SUCCESS;
|
|
}
|
|
|
|
/* DG_CONTROL/DAT_IDENTITY/MSG_OPENDS */
|
|
TW_UINT16 TWAIN_OpenDS (pTW_IDENTITY pOrigin, TW_MEMREF pData)
|
|
{
|
|
TW_UINT16 i = 0;
|
|
pTW_IDENTITY pIdentity = (pTW_IDENTITY) pData;
|
|
activeDS *newSource;
|
|
const char *modname = NULL;
|
|
HMODULE hmod;
|
|
|
|
TRACE("DG_CONTROL/DAT_IDENTITY/MSG_OPENDS\n");
|
|
TRACE("pIdentity is %s\n", pIdentity->ProductName);
|
|
if (!DSM_initialized) {
|
|
FIXME("seq error\n");
|
|
DSM_twCC = TWCC_SEQERROR;
|
|
return TWRC_FAILURE;
|
|
}
|
|
twain_autodetect();
|
|
if (!nrdevices) {
|
|
FIXME("no devs.\n");
|
|
DSM_twCC = TWCC_NODS;
|
|
return TWRC_FAILURE;
|
|
}
|
|
|
|
if (pIdentity->ProductName[0] != '\0') {
|
|
/* Make sure the source to be opened exists in the device list */
|
|
for (i = 0; i<nrdevices; i++)
|
|
if (!strcmp (devices[i].identity.ProductName, pIdentity->ProductName))
|
|
break;
|
|
if (i == nrdevices)
|
|
i = 0;
|
|
} /* else use the first device */
|
|
|
|
/* the source is found in the device list */
|
|
newSource = HeapAlloc (GetProcessHeap(), 0, sizeof (activeDS));
|
|
if (!newSource) {
|
|
DSM_twCC = TWCC_LOWMEMORY;
|
|
FIXME("Out of memory.\n");
|
|
return TWRC_FAILURE;
|
|
}
|
|
hmod = LoadLibraryA(devices[i].modname);
|
|
if (!hmod) {
|
|
ERR("Failed to load TWAIN Source %s\n", modname);
|
|
DSM_twCC = TWCC_OPERATIONERROR;
|
|
HeapFree(GetProcessHeap(), 0, newSource);
|
|
return TWRC_FAILURE;
|
|
}
|
|
newSource->hmod = hmod;
|
|
newSource->dsEntry = (DSENTRYPROC)GetProcAddress(hmod, "DS_Entry");
|
|
if (TWRC_SUCCESS != newSource->dsEntry (pOrigin, DG_CONTROL, DAT_IDENTITY, MSG_OPENDS, pIdentity)) {
|
|
DSM_twCC = TWCC_OPERATIONERROR;
|
|
HeapFree(GetProcessHeap(), 0, newSource);
|
|
return TWRC_FAILURE;
|
|
}
|
|
/* Assign name and id for the opened data source */
|
|
pIdentity->Id = DSM_sourceId ++;
|
|
/* add the data source to an internal active source list */
|
|
newSource->next = activeSources;
|
|
newSource->identity.Id = pIdentity->Id;
|
|
strcpy (newSource->identity.ProductName, pIdentity->ProductName);
|
|
activeSources = newSource;
|
|
DSM_twCC = TWCC_SUCCESS;
|
|
return TWRC_SUCCESS;
|
|
}
|
|
|
|
/* DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT */
|
|
TW_UINT16 TWAIN_UserSelect (pTW_IDENTITY pOrigin, TW_MEMREF pData)
|
|
{
|
|
pTW_IDENTITY selected = (pTW_IDENTITY)pData;
|
|
|
|
if (!nrdevices) {
|
|
DSM_twCC = TWCC_OPERATIONERROR;
|
|
return TWRC_FAILURE;
|
|
}
|
|
*selected = devices[0].identity;
|
|
DSM_twCC = TWCC_SUCCESS;
|
|
return TWRC_SUCCESS;
|
|
}
|
|
|
|
/* DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM */
|
|
TW_UINT16 TWAIN_CloseDSM (pTW_IDENTITY pOrigin, TW_MEMREF pData)
|
|
{
|
|
activeDS *currentDS = activeSources, *nextDS;
|
|
|
|
TRACE("DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM\n");
|
|
|
|
if (DSM_initialized)
|
|
{
|
|
DSM_initialized = FALSE;
|
|
|
|
/* If there are data sources still open, close them now. */
|
|
while (currentDS != NULL)
|
|
{
|
|
nextDS = currentDS->next;
|
|
currentDS->dsEntry (pOrigin, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, pData);
|
|
HeapFree (GetProcessHeap(), 0, currentDS);
|
|
currentDS = nextDS;
|
|
}
|
|
activeSources = NULL;
|
|
DSM_twCC = TWCC_SUCCESS;
|
|
return TWRC_SUCCESS;
|
|
} else {
|
|
DSM_twCC = TWCC_SEQERROR;
|
|
return TWRC_FAILURE;
|
|
}
|
|
}
|
|
|
|
/* DG_CONTROL/DAT_PARENT/MSG_OPENDSM */
|
|
TW_UINT16 TWAIN_OpenDSM (pTW_IDENTITY pOrigin, TW_MEMREF pData)
|
|
{
|
|
TW_UINT16 twRC = TWRC_SUCCESS;
|
|
|
|
TRACE("DG_CONTROL/DAT_PARENT/MSG_OPENDSM\n");
|
|
if (!DSM_initialized) {
|
|
DSM_currentDevice = 0;
|
|
DSM_initialized = TRUE;
|
|
DSM_twCC = TWCC_SUCCESS;
|
|
twRC = TWRC_SUCCESS;
|
|
} else {
|
|
/* operation invoked in invalid state */
|
|
DSM_twCC = TWCC_SEQERROR;
|
|
twRC = TWRC_FAILURE;
|
|
}
|
|
return twRC;
|
|
}
|
|
|
|
/* DG_CONTROL/DAT_STATUS/MSG_GET */
|
|
TW_UINT16 TWAIN_GetDSMStatus (pTW_IDENTITY pOrigin, TW_MEMREF pData)
|
|
{
|
|
pTW_STATUS pSourceStatus = (pTW_STATUS) pData;
|
|
|
|
TRACE ("DG_CONTROL/DAT_STATUS/MSG_GET\n");
|
|
|
|
pSourceStatus->ConditionCode = DSM_twCC;
|
|
DSM_twCC = TWCC_SUCCESS; /* clear the condition code */
|
|
return TWRC_SUCCESS;
|
|
}
|