dsm_ctrl.c 9.24 KB
Newer Older
1 2 3 4
/*
 * TWAIN32 Source Manager
 *
 * Copyright 2000 Corel Corporation
5
 * Copyright 2006 Marcus Meissner
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * 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
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 21 22 23 24
 */

#include "config.h"

#include <stdlib.h>
25
#include <stdarg.h>
26
#include <stdio.h>
27 28 29

#define NONAMELESSUNION
#define NONAMELESSSTRUCT
30
#include "windef.h"
31 32 33 34 35 36 37
#include "winbase.h"
#include "twain.h"
#include "twain_i.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(twain);

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
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)
87
			devices = HeapReAlloc(GetProcessHeap(), 0, devices, sizeof(devices[0])*(nrdevices+1));
88
		else
89 90 91
			devices = HeapAlloc(GetProcessHeap(), 0, sizeof(devices[0]));
		if ((devices[nrdevices].modname = HeapAlloc(GetProcessHeap(), 0, strlen(dsname) + 1)))
			lstrcpyA(devices[nrdevices].modname, dsname);
92
		devices[nrdevices].identity = sourceId;
93 94 95 96 97
		nrdevices++;
		DSM_sourceId++;
	} while (1);
	FreeLibrary (hmod);
}
98

99
static int detectionrun = 0;
100

101
static void
102
twain_autodetect(void) {
103 104
	if (detectionrun) return;
	detectionrun = 1;
105

106
	twain_add_onedriver("sane.ds");
107
	twain_add_onedriver("gphoto2.ds");
108 109 110 111
#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");
112 113 114
#endif
}

115 116
/* DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS */
TW_UINT16 TWAIN_CloseDS (pTW_IDENTITY pOrigin, TW_MEMREF pData)
117
{
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
	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;
147 148
}

149 150 151
/* DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT */
TW_UINT16 TWAIN_IdentityGetDefault (pTW_IDENTITY pOrigin, TW_MEMREF pData)
{
152 153 154 155 156 157 158
	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;
159
	*pSourceIdentity = devices[0].identity;
160
	DSM_twCC = TWCC_SUCCESS;
161
	return TWRC_SUCCESS;
162 163 164 165 166
}

/* DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST */
TW_UINT16 TWAIN_IdentityGetFirst (pTW_IDENTITY pOrigin, TW_MEMREF pData)
{
167 168 169 170 171 172
	pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;

	TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST\n");
	twain_autodetect();
	if (!nrdevices) {
		TRACE ("no entries found.\n");
173 174
		DSM_twCC = TWCC_NODS;
		return TWRC_FAILURE;
175 176
	}
	DSM_currentDevice = 0;
177
	*pSourceIdentity = devices[DSM_currentDevice++].identity;
178
	return TWRC_SUCCESS;
179 180 181 182 183
}

/* DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT */
TW_UINT16 TWAIN_IdentityGetNext (pTW_IDENTITY pOrigin, TW_MEMREF pData)
{
184 185 186 187 188 189 190
	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;
	}
191
	*pSourceIdentity = devices[DSM_currentDevice++].identity;
192
	return TWRC_SUCCESS;
193 194 195 196 197
}

/* DG_CONTROL/DAT_IDENTITY/MSG_OPENDS */
TW_UINT16 TWAIN_OpenDS (pTW_IDENTITY pOrigin, TW_MEMREF pData)
{
198 199 200 201 202 203 204 205 206
	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_currentState != 3) {
207
		FIXME("seq error\n");
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
		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;
238
                HeapFree(GetProcessHeap(), 0, newSource);
239 240 241 242 243 244
		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;
245
                HeapFree(GetProcessHeap(), 0, newSource);
246 247 248 249 250 251 252 253 254 255 256
		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;
257 258 259 260 261
}

/* DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT */
TW_UINT16 TWAIN_UserSelect (pTW_IDENTITY pOrigin, TW_MEMREF pData)
{
262 263 264 265 266 267
	pTW_IDENTITY	selected = (pTW_IDENTITY)pData;

	if (!nrdevices) {
		DSM_twCC = TWCC_OPERATIONERROR;
		return TWRC_FAILURE;
	}
268
	*selected = devices[0].identity;
269 270
	DSM_twCC = TWCC_SUCCESS;
	return TWRC_SUCCESS;
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
}

/* 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_currentState == 3)
    {
        DSM_initialized = FALSE;
        DSM_currentState = 2;

        /* If there are data sources still open, close them now. */
        while (currentDS != NULL)
        {
            nextDS = currentDS->next;
289
	    currentDS->dsEntry (pOrigin, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, pData);
290 291 292 293 294
            HeapFree (GetProcessHeap(), 0, currentDS);
            currentDS = nextDS;
        }
        activeSources = NULL;
        DSM_twCC = TWCC_SUCCESS;
295 296
        return TWRC_SUCCESS;
    } else {
297
        DSM_twCC = TWCC_SEQERROR;
298
        return TWRC_FAILURE;
299 300 301 302 303 304
    }
}

/* DG_CONTROL/DAT_PARENT/MSG_OPENDSM */
TW_UINT16 TWAIN_OpenDSM (pTW_IDENTITY pOrigin, TW_MEMREF pData)
{
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
	TW_UINT16 twRC = TWRC_SUCCESS;

	TRACE("DG_CONTROL/DAT_PARENT/MSG_OPENDSM\n");
	if (DSM_currentState == 2) {
		if (!DSM_initialized) {
			DSM_currentDevice = 0;
			DSM_initialized = TRUE;
		}
        	DSM_currentState = 3;
		DSM_twCC = TWCC_SUCCESS;
		twRC = TWRC_SUCCESS;
	} else {
		/* operation invoked in invalid state */
		DSM_twCC = TWCC_SEQERROR;
		twRC = TWRC_FAILURE;
	}
	return twRC;
322 323 324 325 326
}

/* DG_CONTROL/DAT_STATUS/MSG_GET */
TW_UINT16 TWAIN_GetDSMStatus (pTW_IDENTITY pOrigin, TW_MEMREF pData)
{
327
	pTW_STATUS pSourceStatus = (pTW_STATUS) pData;
328

329
	TRACE ("DG_CONTROL/DAT_STATUS/MSG_GET\n");
330

331 332 333
	pSourceStatus->ConditionCode = DSM_twCC;
	DSM_twCC = TWCC_SUCCESS;  /* clear the condition code */
	return TWRC_SUCCESS;
334
}