mirror of https://github.com/GNOME/gimp.git
1000 lines
25 KiB
C
1000 lines
25 KiB
C
|
/*
|
||
|
* TWAIN Plug-in
|
||
|
* Copyright (C) 1999 Craig Setera
|
||
|
* Craig Setera, setera@infonet.isl.net
|
||
|
* 03/31/1999
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This program 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 General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program; if not, write to the Free Software
|
||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||
|
*
|
||
|
*
|
||
|
* Based on (at least) the following plug-ins:
|
||
|
* Screenshot
|
||
|
* GIF
|
||
|
* Randomize
|
||
|
*
|
||
|
* Any suggestions, bug-reports or patches are welcome.
|
||
|
*
|
||
|
* This plug-in interfaces to the TWAIN support library in order
|
||
|
* to capture images from TWAIN devices directly into GIMP images.
|
||
|
* The plug-in is capable of acquiring the following type of
|
||
|
* images:
|
||
|
* - B/W (1 bit images translated to grayscale B/W)
|
||
|
* - Grayscale up to 16 bits per pixel
|
||
|
* - RGB up to 16 bits per sample (24, 30, 36, etc.)
|
||
|
* - Paletted images (both Gray and RGB)
|
||
|
*
|
||
|
* Prerequisites:
|
||
|
* This plug-in will not compile on anything other than a Win32
|
||
|
* platform. Although the TWAIN documentation implies that there
|
||
|
* is TWAIN support available on Macintosh, I neither have a
|
||
|
* Macintosh nor the interest in porting this. If anyone else
|
||
|
* has an interest, consult www.twain.org for more information on
|
||
|
* interfacing to TWAIN.
|
||
|
*
|
||
|
* Known problems:
|
||
|
* - Multiple image transfers will hang the plug-in. The current
|
||
|
* configuration compiles with a maximum of single image transfers.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* Revision history
|
||
|
* (02/07/99) v0.1 First working version (internal)
|
||
|
* (02/09/99) v0.2 First release to anyone other than myself
|
||
|
* (02/15/99) v0.3 Added image dump and read support for debugging
|
||
|
* (03/31/99) v0.5 Added support for multi-byte samples and paletted
|
||
|
* images.
|
||
|
*/
|
||
|
|
||
|
#include <glib.h> /* Needed when compiling with gcc */
|
||
|
|
||
|
#include <malloc.h>
|
||
|
#include <windows.h>
|
||
|
#include "twain.h"
|
||
|
#include "tw_func.h"
|
||
|
#include "tw_util.h"
|
||
|
|
||
|
/* The DLL to be loaded for TWAIN support */
|
||
|
#define TWAIN_DLL_NAME "TWAIN_32.DLL"
|
||
|
|
||
|
/*
|
||
|
* Twain error code to string mappings
|
||
|
*/
|
||
|
static int twainErrorCount = 0;
|
||
|
static char *twainErrors[] = {
|
||
|
"No error",
|
||
|
"Failure due to unknown causes",
|
||
|
"Not enough memory to perform operation",
|
||
|
"No Data Source",
|
||
|
"DS is connected to max possible apps",
|
||
|
"DS or DSM reported error, application shouldn't",
|
||
|
"Unknown capability",
|
||
|
"Unrecognized MSG DG DAT combination",
|
||
|
"Data parameter out of range",
|
||
|
"DG DAT MSG out of expected sequence",
|
||
|
"Unknown destination App/Src in DSM_Entry",
|
||
|
"Capability not supported by source",
|
||
|
"Operation not supported by capability",
|
||
|
"Capability has dependency on other capability",
|
||
|
"File System operation is denied (file is protected)",
|
||
|
"Operation failed because file already exists.",
|
||
|
"File not found",
|
||
|
"Operation failed because directory is not empty",
|
||
|
"The feeder is jammed",
|
||
|
"The feeder detected multiple pages",
|
||
|
"Error writing the file (disk full?)",
|
||
|
"The device went offline prior to or during this operation",
|
||
|
NULL
|
||
|
};
|
||
|
|
||
|
/* Storage for the DLL handle */
|
||
|
static HINSTANCE hDLL = NULL;
|
||
|
|
||
|
/* Storage for the entry point into the DSM */
|
||
|
static DSMENTRYPROC dsmEntryPoint = NULL;
|
||
|
|
||
|
/*
|
||
|
* FloatToFix32
|
||
|
*
|
||
|
* Convert a floating point value into a FIX32.
|
||
|
*/
|
||
|
TW_FIX32 FloatToFIX32(float floater)
|
||
|
{
|
||
|
TW_FIX32 Fix32_value;
|
||
|
TW_INT32 value = (TW_INT32) (floater * 65536.0 + 0.5);
|
||
|
Fix32_value.Whole = value >> 16;
|
||
|
Fix32_value.Frac = value & 0x0000ffffL;
|
||
|
return (Fix32_value);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Fix32ToFloat
|
||
|
*
|
||
|
* Convert a FIX32 value into a floating point value.
|
||
|
*/
|
||
|
float FIX32ToFloat(TW_FIX32 fix32)
|
||
|
{
|
||
|
float floater;
|
||
|
floater = (float) fix32.Whole + (float) fix32.Frac / 65536.0;
|
||
|
return floater;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* callDSM
|
||
|
*
|
||
|
* Call the specified function on the data source manager.
|
||
|
*/
|
||
|
TW_UINT16
|
||
|
callDSM(pTW_IDENTITY pOrigin,
|
||
|
pTW_IDENTITY pDest,
|
||
|
TW_UINT32 DG,
|
||
|
TW_UINT16 DAT,
|
||
|
TW_UINT16 MSG,
|
||
|
TW_MEMREF pData)
|
||
|
{
|
||
|
/* Call the function */
|
||
|
return (*dsmEntryPoint) (pOrigin, pDest, DG, DAT, MSG, pData);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* twainError
|
||
|
*
|
||
|
* Return the TWAIN error message associated
|
||
|
* with the specified error code.
|
||
|
*/
|
||
|
char *
|
||
|
twainError(int errorCode)
|
||
|
{
|
||
|
/* Check whether we've counted */
|
||
|
if (twainErrorCount == 0)
|
||
|
while (twainErrors[twainErrorCount++]) {}
|
||
|
|
||
|
/* Check out of bounds */
|
||
|
if (errorCode >= twainErrorCount)
|
||
|
return "Unknown TWAIN Error Code";
|
||
|
else
|
||
|
return twainErrors[errorCode];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* currentTwainError
|
||
|
*
|
||
|
* Return the current TWAIN error message.
|
||
|
*/
|
||
|
char *
|
||
|
currentTwainError(pTW_SESSION twSession)
|
||
|
{
|
||
|
TW_STATUS twStatus;
|
||
|
|
||
|
/* Get the current status code from the DSM */
|
||
|
twSession->twRC = callDSM(APP_IDENTITY(twSession), DS_IDENTITY(twSession),
|
||
|
DG_CONTROL, DAT_STATUS, MSG_GET,
|
||
|
(TW_MEMREF) &twStatus);
|
||
|
|
||
|
/* Return the mapped error code */
|
||
|
return twainError(twStatus.ConditionCode);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* twainIsAvailable
|
||
|
*
|
||
|
* Return boolean indicating whether TWAIN is available
|
||
|
*/
|
||
|
int
|
||
|
twainIsAvailable(void)
|
||
|
{
|
||
|
/* Already loaded? */
|
||
|
if (dsmEntryPoint) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/* Attempt to load the library */
|
||
|
hDLL = LoadLibrary(TWAIN_DLL_NAME);
|
||
|
if (hDLL == NULL)
|
||
|
return FALSE;
|
||
|
|
||
|
/* Look up the entry point for use */
|
||
|
dsmEntryPoint = (DSMENTRYPROC) GetProcAddress(hDLL, "DSM_Entry");
|
||
|
if (dsmEntryPoint == NULL)
|
||
|
return FALSE;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* getImage
|
||
|
*
|
||
|
* This is a "high-level" function that can be called in order
|
||
|
* to take all of the steps necessary to kick off an image-transfer
|
||
|
* from a user-specified TWAIN datasource. The data will be passed
|
||
|
* back to the callback function specified in the session structure.
|
||
|
*/
|
||
|
int
|
||
|
getImage(pTW_SESSION twSession)
|
||
|
{
|
||
|
/* Do some sanity checking first and bail
|
||
|
* if necessary.
|
||
|
*/
|
||
|
if (!twainIsAvailable()) {
|
||
|
LogMessage("TWAIN is not available for image capture\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
/* One step at a time */
|
||
|
if (!openDSM(twSession)) {
|
||
|
LogMessage("Unable to open data source manager\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (!selectDS(twSession)) {
|
||
|
LogMessage("Data source not selected\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (!openDS(twSession)) {
|
||
|
LogMessage("Unable to open datasource\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
requestImageAcquire(twSession, TRUE);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* openDSM
|
||
|
*
|
||
|
* Open the data source manager
|
||
|
*/
|
||
|
int
|
||
|
openDSM(pTW_SESSION twSession)
|
||
|
{
|
||
|
/* Make sure that we aren't already open */
|
||
|
if (DSM_IS_OPEN(twSession))
|
||
|
return TRUE;
|
||
|
|
||
|
/* Open the data source manager */
|
||
|
twSession->twRC = callDSM(APP_IDENTITY(twSession), NULL,
|
||
|
DG_CONTROL, DAT_PARENT, MSG_OPENDSM,
|
||
|
(TW_MEMREF) &(twSession->hwnd));
|
||
|
|
||
|
/* Check the return code */
|
||
|
switch (twSession->twRC) {
|
||
|
case TWRC_SUCCESS:
|
||
|
/* We are now at state 3 */
|
||
|
twSession->twainState = 3;
|
||
|
return TRUE;
|
||
|
break;
|
||
|
|
||
|
case TWRC_FAILURE:
|
||
|
default:
|
||
|
LogMessage("OpenDSM failure\n");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* selectDS
|
||
|
*
|
||
|
* Select a datasource using the TWAIN user
|
||
|
* interface.
|
||
|
*/
|
||
|
int
|
||
|
selectDS(pTW_SESSION twSession)
|
||
|
{
|
||
|
/* The datasource manager must be open */
|
||
|
if (DSM_IS_CLOSED(twSession)) {
|
||
|
LogMessage("Can't select data source with closed source manager\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
/* Ask TWAIN to present the source select dialog */
|
||
|
twSession->twRC = callDSM(APP_IDENTITY(twSession), NULL,
|
||
|
DG_CONTROL, DAT_IDENTITY, MSG_USERSELECT,
|
||
|
(TW_MEMREF) DS_IDENTITY(twSession));
|
||
|
|
||
|
/* Check the return to determine what the user decided
|
||
|
* to do.
|
||
|
*/
|
||
|
switch (twSession->twRC) {
|
||
|
case TWRC_SUCCESS:
|
||
|
LogMessage("Data source %s selected\n", DS_IDENTITY(twSession)->ProductName);
|
||
|
return TRUE;
|
||
|
break;
|
||
|
|
||
|
case TWRC_CANCEL:
|
||
|
LogMessage("User cancelled TWAIN source selection\n");
|
||
|
break;
|
||
|
|
||
|
case TWRC_FAILURE:
|
||
|
default:
|
||
|
LogMessage("Error \"%s\" during TWAIN source selection\n",
|
||
|
currentTwainError(twSession));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* selectDefaultDS
|
||
|
*
|
||
|
* Select the default datasource.
|
||
|
*/
|
||
|
int
|
||
|
selectDefaultDS(pTW_SESSION twSession)
|
||
|
{
|
||
|
/* The datasource manager must be open */
|
||
|
if (DSM_IS_CLOSED(twSession)) {
|
||
|
LogMessage("Can't select data source with closed source manager\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
/* Ask TWAIN to present the source select dialog */
|
||
|
twSession->twRC = callDSM(APP_IDENTITY(twSession), NULL,
|
||
|
DG_CONTROL, DAT_IDENTITY, MSG_GETDEFAULT,
|
||
|
(TW_MEMREF) DS_IDENTITY(twSession));
|
||
|
|
||
|
/* Check the return code */
|
||
|
return (twSession->twRC == TWRC_SUCCESS);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* openDS
|
||
|
*
|
||
|
* Open a data source using the TWAIN user interface.
|
||
|
*/
|
||
|
int
|
||
|
openDS(pTW_SESSION twSession)
|
||
|
{
|
||
|
TW_IDENTITY *dsIdentity;
|
||
|
|
||
|
/* The datasource manager must be open */
|
||
|
if (DSM_IS_CLOSED(twSession)) {
|
||
|
LogMessage("openDS: Cannot open data source... manager closed\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
/* Is the data source already open? */
|
||
|
if (DS_IS_OPEN(twSession)) {
|
||
|
LogMessage("openDS: Data source already open\n");
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/* Open the TWAIN datasource */
|
||
|
dsIdentity = DS_IDENTITY(twSession);
|
||
|
twSession->twRC = callDSM(APP_IDENTITY(twSession), NULL,
|
||
|
DG_CONTROL, DAT_IDENTITY, MSG_OPENDS,
|
||
|
(TW_MEMREF) dsIdentity);
|
||
|
|
||
|
/* Check the return to determine what the user decided
|
||
|
* to do.
|
||
|
*/
|
||
|
switch (twSession->twRC) {
|
||
|
case TWRC_SUCCESS:
|
||
|
/* We are now in TWAIN state 4 */
|
||
|
twSession->twainState = 4;
|
||
|
LogMessage("Data source %s opened\n", DS_IDENTITY(twSession)->ProductName);
|
||
|
LogMessage("\tVersion.MajorNum = %d\n", dsIdentity->Version.MajorNum);
|
||
|
LogMessage("\tVersion.MinorNum = %d\n", dsIdentity->Version.MinorNum);
|
||
|
LogMessage("\tVersion.Info = %s\n", dsIdentity->Version.Info);
|
||
|
LogMessage("\tProtocolMajor = %d\n", dsIdentity->ProtocolMajor);
|
||
|
LogMessage("\tProtocolMinor = %d\n", dsIdentity->ProtocolMinor);
|
||
|
LogMessage("\tManufacturer = %s\n", dsIdentity->Manufacturer);
|
||
|
LogMessage("\tProductFamily = %s\n", dsIdentity->ProductFamily);
|
||
|
return TRUE;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
LogMessage("Error \"%s\" opening data source\n", currentTwainError(twSession));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* setBufferedXfer
|
||
|
*/
|
||
|
static int
|
||
|
setBufferedXfer(pTW_SESSION twSession)
|
||
|
{
|
||
|
TW_CAPABILITY bufXfer;
|
||
|
pTW_ONEVALUE pvalOneValue;
|
||
|
|
||
|
/* Make sure the data source is open first */
|
||
|
if (DS_IS_CLOSED(twSession))
|
||
|
return FALSE;
|
||
|
|
||
|
/* Create the capability information */
|
||
|
bufXfer.Cap = ICAP_XFERMECH;
|
||
|
bufXfer.ConType = TWON_ONEVALUE;
|
||
|
bufXfer.hContainer = GlobalAlloc(GHND, sizeof(TW_ONEVALUE));
|
||
|
pvalOneValue = (pTW_ONEVALUE) GlobalLock(bufXfer.hContainer);
|
||
|
pvalOneValue->ItemType = TWTY_UINT16;
|
||
|
pvalOneValue->Item = TWSX_MEMORY;
|
||
|
GlobalUnlock(bufXfer.hContainer);
|
||
|
|
||
|
/* Make the call to the source manager */
|
||
|
twSession->twRC = callDSM(APP_IDENTITY(twSession), DS_IDENTITY(twSession),
|
||
|
DG_CONTROL, DAT_CAPABILITY, MSG_SET,
|
||
|
(TW_MEMREF) &bufXfer);
|
||
|
|
||
|
/* Free the container */
|
||
|
GlobalFree(bufXfer.hContainer);
|
||
|
|
||
|
/* Let the caller know what happened */
|
||
|
return (twSession->twRC==TWRC_SUCCESS);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* requestImageAcquire
|
||
|
*
|
||
|
* Request that the acquire user interface
|
||
|
* be displayed. This may or may not cause
|
||
|
* an image to actually be transferred.
|
||
|
*/
|
||
|
int
|
||
|
requestImageAcquire(pTW_SESSION twSession, BOOL showUI)
|
||
|
{
|
||
|
/* Make sure in the correct state */
|
||
|
if (DS_IS_CLOSED(twSession)) {
|
||
|
LogMessage("Can't acquire image with closed datasource\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
/* Set the transfer mode */
|
||
|
if (setBufferedXfer(twSession)) {
|
||
|
TW_USERINTERFACE ui;
|
||
|
|
||
|
/* Set the UI information */
|
||
|
ui.ShowUI = TRUE;
|
||
|
ui.ModalUI = TRUE;
|
||
|
ui.hParent = twSession->hwnd;
|
||
|
|
||
|
/* Make the call to the source manager */
|
||
|
twSession->twRC = callDSM(APP_IDENTITY(twSession), DS_IDENTITY(twSession),
|
||
|
DG_CONTROL, DAT_USERINTERFACE, MSG_ENABLEDS,
|
||
|
(TW_MEMREF) &ui);
|
||
|
|
||
|
if (twSession->twRC == TWRC_SUCCESS) {
|
||
|
/* We are now at a new twain state */
|
||
|
twSession->twainState = 5;
|
||
|
|
||
|
return TRUE;
|
||
|
} else {
|
||
|
LogMessage("Error during data source enable\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
} else {
|
||
|
LogMessage("Unable to set buffered transfer mode: %s\n",
|
||
|
currentTwainError(twSession));
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* disableDS
|
||
|
*
|
||
|
* Disable the datasource associated with twSession.
|
||
|
*/
|
||
|
int
|
||
|
disableDS(pTW_SESSION twSession)
|
||
|
{
|
||
|
TW_USERINTERFACE ui;
|
||
|
|
||
|
/* Verify the datasource is enabled */
|
||
|
if (DS_IS_DISABLED(twSession)) {
|
||
|
LogMessage("disableDS: Data source not enabled\n");
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/* Set the UI information */
|
||
|
ui.ShowUI = TRUE;
|
||
|
ui.ModalUI = TRUE;
|
||
|
ui.hParent = twSession->hwnd;
|
||
|
|
||
|
/* Make the call to the source manager */
|
||
|
twSession->twRC = callDSM(APP_IDENTITY(twSession), DS_IDENTITY(twSession),
|
||
|
DG_CONTROL, DAT_USERINTERFACE, MSG_DISABLEDS,
|
||
|
(TW_MEMREF) &ui);
|
||
|
|
||
|
if (twSession->twRC == TWRC_SUCCESS) {
|
||
|
/* We are now at a new twain state */
|
||
|
twSession->twainState = 4;
|
||
|
|
||
|
return TRUE;
|
||
|
} else {
|
||
|
LogMessage("Error during data source disable\n");
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* closeDS
|
||
|
*
|
||
|
* Close the datasource associated with the
|
||
|
* specified session.
|
||
|
*/
|
||
|
int
|
||
|
closeDS(pTW_SESSION twSession)
|
||
|
{
|
||
|
/* Can't close a closed data source */
|
||
|
if (DS_IS_CLOSED(twSession)) {
|
||
|
LogMessage("closeDS: Data source already closed\n");
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/* Open the TWAIN datasource */
|
||
|
twSession->twRC = callDSM(APP_IDENTITY(twSession), NULL,
|
||
|
DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS,
|
||
|
(TW_MEMREF) DS_IDENTITY(twSession));
|
||
|
|
||
|
/* Check the return to determine what the user decided
|
||
|
* to do.
|
||
|
*/
|
||
|
switch (twSession->twRC) {
|
||
|
case TWRC_SUCCESS:
|
||
|
/* We are now in TWAIN state 3 */
|
||
|
twSession->twainState = 3;
|
||
|
LogMessage("Data source %s closed\n", DS_IDENTITY(twSession)->ProductName);
|
||
|
return TRUE;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
LogMessage("Error \"%s\" closing data source\n", currentTwainError(twSession));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* closeDSM
|
||
|
*
|
||
|
* Close the data source manager
|
||
|
*/
|
||
|
int
|
||
|
closeDSM(pTW_SESSION twSession)
|
||
|
{
|
||
|
if (DSM_IS_CLOSED(twSession)) {
|
||
|
LogMessage("closeDSM: Source Manager not open\n");
|
||
|
return FALSE;
|
||
|
} else {
|
||
|
if (DS_IS_OPEN(twSession)) {
|
||
|
LogMessage("closeDSM: Can't close source manager with open source\n");
|
||
|
return FALSE;
|
||
|
} else {
|
||
|
twSession->twRC = callDSM(APP_IDENTITY(twSession), NULL,
|
||
|
DG_CONTROL, DAT_PARENT, MSG_CLOSEDSM,
|
||
|
&(twSession->hwnd));
|
||
|
|
||
|
if (twSession->twRC != TWRC_SUCCESS) {
|
||
|
LogMessage("CloseDSM failure -- %s\n", currentTwainError(twSession));
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
/* We are now in state 2 */
|
||
|
twSession->twainState = 2;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Let the caller know what happened */
|
||
|
return (twSession->twRC==TWRC_SUCCESS);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* unloadTwainLibrary
|
||
|
*
|
||
|
* Unload the TWAIN dynamic link library
|
||
|
*/
|
||
|
int
|
||
|
unloadTwainLibrary(pTW_SESSION twSession)
|
||
|
{
|
||
|
/* Explicitly free the SM library */
|
||
|
if (hDLL) {
|
||
|
FreeLibrary(hDLL);
|
||
|
hDLL=NULL;
|
||
|
}
|
||
|
|
||
|
/* the data source id will no longer be valid after
|
||
|
* twain is killed. If the id is left around the
|
||
|
* data source can not be found or opened
|
||
|
*/
|
||
|
DS_IDENTITY(twSession)->Id = 0;
|
||
|
|
||
|
/* We are now back at state 1 */
|
||
|
twSession->twainState = 1;
|
||
|
LogMessage("Source Manager successfully closed\n");
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* beginImageTransfer
|
||
|
*
|
||
|
* Begin an image transfer.
|
||
|
*/
|
||
|
static int
|
||
|
beginImageTransfer(pTW_SESSION twSession, pTW_IMAGEINFO imageInfo)
|
||
|
{
|
||
|
/* Clear our structures */
|
||
|
memset(imageInfo, 0, sizeof(TW_IMAGEINFO));
|
||
|
|
||
|
/* Query the image information */
|
||
|
twSession->twRC = callDSM(
|
||
|
APP_IDENTITY(twSession), DS_IDENTITY(twSession),
|
||
|
DG_IMAGE, DAT_IMAGEINFO, MSG_GET,
|
||
|
(TW_MEMREF) imageInfo);
|
||
|
|
||
|
/* Check the return code */
|
||
|
if (twSession->twRC != TWRC_SUCCESS) {
|
||
|
LogMessage("Get Image Info failure - %s\n", currentTwainError(twSession));
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
/* Call the begin transfer callback if registered */
|
||
|
if (twSession->transferFunctions->txfrBeginCb)
|
||
|
if (!(*twSession->transferFunctions->txfrBeginCb)(imageInfo, twSession->clientData))
|
||
|
return FALSE;
|
||
|
|
||
|
/* We should continue */
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* transferImage
|
||
|
*
|
||
|
* The Source indicated it is ready to transfer data. It is
|
||
|
* waiting for the application to inquire about the image details,
|
||
|
* initiate the actual transfer, and, hence, transition the session
|
||
|
* from State 6 to 7. Return the reason for exiting the transfer.
|
||
|
*/
|
||
|
static void
|
||
|
transferImage(pTW_SESSION twSession, pTW_IMAGEINFO imageInfo)
|
||
|
{
|
||
|
TW_SETUPMEMXFER setupMemXfer;
|
||
|
TW_IMAGEMEMXFER imageMemXfer;
|
||
|
char *buffer;
|
||
|
|
||
|
/* Clear our structures */
|
||
|
memset(&setupMemXfer, 0, sizeof(TW_SETUPMEMXFER));
|
||
|
memset(&imageMemXfer, 0, sizeof(TW_IMAGEMEMXFER));
|
||
|
|
||
|
/* Find out how the source would like to transfer... */
|
||
|
twSession->twRC = callDSM(APP_IDENTITY(twSession), DS_IDENTITY(twSession),
|
||
|
DG_CONTROL, DAT_SETUPMEMXFER, MSG_GET,
|
||
|
(TW_MEMREF) &setupMemXfer);
|
||
|
|
||
|
/* Allocate the buffer for the transfer */
|
||
|
buffer = (char *) malloc (setupMemXfer.Preferred);
|
||
|
imageMemXfer.Memory.Flags = TWMF_APPOWNS | TWMF_POINTER;
|
||
|
imageMemXfer.Memory.Length = setupMemXfer.Preferred;
|
||
|
imageMemXfer.Memory.TheMem = (TW_MEMREF) buffer;
|
||
|
|
||
|
/* Get the data */
|
||
|
do {
|
||
|
/* Setup for the memory transfer */
|
||
|
imageMemXfer.Compression = TWON_DONTCARE16;
|
||
|
imageMemXfer.BytesPerRow = TWON_DONTCARE32;
|
||
|
imageMemXfer.Columns = TWON_DONTCARE32;
|
||
|
imageMemXfer.Rows = TWON_DONTCARE32;
|
||
|
imageMemXfer.XOffset = TWON_DONTCARE32;
|
||
|
imageMemXfer.YOffset = TWON_DONTCARE32;
|
||
|
imageMemXfer.BytesWritten = TWON_DONTCARE32;
|
||
|
|
||
|
/* Get the next block of memory */
|
||
|
twSession->twRC = callDSM(APP_IDENTITY(twSession), DS_IDENTITY(twSession),
|
||
|
DG_IMAGE, DAT_IMAGEMEMXFER, MSG_GET,
|
||
|
(TW_MEMREF) &imageMemXfer);
|
||
|
|
||
|
if ((twSession->twRC == TWRC_SUCCESS) ||
|
||
|
(twSession->twRC == TWRC_XFERDONE)) {
|
||
|
/* Call the callback function */
|
||
|
if (!(*twSession->transferFunctions->txfrDataCb) (
|
||
|
imageInfo,
|
||
|
&imageMemXfer,
|
||
|
twSession->clientData)) {
|
||
|
/* Callback function requested to cancel */
|
||
|
twSession->twRC = TWRC_CANCEL;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
} while (twSession->twRC == TWRC_SUCCESS);
|
||
|
|
||
|
/* Free the memory buffer */
|
||
|
free((void *) imageMemXfer.Memory.TheMem);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* endPendingTransfer
|
||
|
*
|
||
|
* Cancel the currently pending transfer.
|
||
|
* Return the count of pending transfers.
|
||
|
*/
|
||
|
static int
|
||
|
endPendingTransfer(pTW_SESSION twSession)
|
||
|
{
|
||
|
TW_PENDINGXFERS pendingXfers;
|
||
|
|
||
|
twSession->twRC = callDSM(APP_IDENTITY(twSession), DS_IDENTITY(twSession),
|
||
|
DG_CONTROL, DAT_PENDINGXFERS, MSG_ENDXFER,
|
||
|
(TW_MEMREF) &pendingXfers);
|
||
|
|
||
|
if (!pendingXfers.Count)
|
||
|
twSession->twainState = 5;
|
||
|
|
||
|
return pendingXfers.Count;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* cancelPendingTransfers
|
||
|
*
|
||
|
* Cancel all pending image transfers.
|
||
|
*/
|
||
|
void
|
||
|
cancelPendingTransfers(pTW_SESSION twSession)
|
||
|
{
|
||
|
TW_PENDINGXFERS pendingXfers;
|
||
|
|
||
|
twSession->twRC = callDSM(APP_IDENTITY(twSession), DS_IDENTITY(twSession),
|
||
|
DG_CONTROL, DAT_PENDINGXFERS, MSG_RESET,
|
||
|
(TW_MEMREF) &pendingXfers);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* endImageTransfer
|
||
|
*
|
||
|
* Finish transferring an image. Return the count
|
||
|
* of pending images.
|
||
|
*/
|
||
|
static int
|
||
|
endImageTransfer(pTW_SESSION twSession, int *pendingCount)
|
||
|
{
|
||
|
BOOL continueTransfers;
|
||
|
int exitCode = twSession->twRC;
|
||
|
|
||
|
/* Have now exited the transfer for some reason... Figure out
|
||
|
* why and what to do about it
|
||
|
*/
|
||
|
switch (twSession->twRC) {
|
||
|
case TWRC_XFERDONE:
|
||
|
case TWRC_CANCEL:
|
||
|
LogMessage("Xfer done received\n");
|
||
|
*pendingCount = endPendingTransfer(twSession);
|
||
|
break;
|
||
|
|
||
|
case TWRC_FAILURE:
|
||
|
LogMessage("Failure received\n");
|
||
|
*pendingCount = endPendingTransfer(twSession);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Call the end transfer callback */
|
||
|
if (twSession->transferFunctions->txfrEndCb)
|
||
|
continueTransfers =
|
||
|
(*twSession->transferFunctions->txfrEndCb)(exitCode,
|
||
|
*pendingCount,
|
||
|
twSession->clientData);
|
||
|
|
||
|
return (*pendingCount && continueTransfers);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* transferImages
|
||
|
*
|
||
|
* Transfer all of the images that are available from the
|
||
|
* datasource.
|
||
|
*/
|
||
|
static void
|
||
|
transferImages(pTW_SESSION twSession)
|
||
|
{
|
||
|
TW_IMAGEINFO imageInfo;
|
||
|
int pendingCount;
|
||
|
|
||
|
/* Check the image transfer callback function
|
||
|
* before even attempting to do the transfer
|
||
|
*/
|
||
|
if (!twSession->transferFunctions || !twSession->transferFunctions->txfrDataCb) {
|
||
|
LogMessage("Attempting image transfer without callback function\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Inform our application that we are getting ready
|
||
|
* to transfer images.
|
||
|
*/
|
||
|
if (twSession->transferFunctions->preTxfrCb)
|
||
|
(*twSession->transferFunctions->preTxfrCb)(twSession->clientData);
|
||
|
|
||
|
/* Loop through the available images */
|
||
|
do {
|
||
|
/* Move to the new state */
|
||
|
twSession->twainState = 6;
|
||
|
|
||
|
/* Begin the image transfer */
|
||
|
if (!beginImageTransfer(twSession, &imageInfo))
|
||
|
continue;
|
||
|
|
||
|
/* Call the image transfer function */
|
||
|
transferImage(twSession, &imageInfo);
|
||
|
|
||
|
} while (endImageTransfer(twSession, &pendingCount));
|
||
|
|
||
|
/*
|
||
|
* Inform our application that we are done
|
||
|
* transferring images.
|
||
|
*/
|
||
|
if (twSession->transferFunctions->postTxfrCb)
|
||
|
(*twSession->transferFunctions->postTxfrCb)(pendingCount,
|
||
|
twSession->clientData);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* TwainProcessMessage
|
||
|
*
|
||
|
* Returns TRUE if the application should process message as usual.
|
||
|
* Returns FALSE if the application should skip processing of this message
|
||
|
*/
|
||
|
int
|
||
|
TwainProcessMessage(LPMSG lpMsg, pTW_SESSION twSession)
|
||
|
{
|
||
|
TW_EVENT twEvent;
|
||
|
|
||
|
twSession->twRC = TWRC_NOTDSEVENT;
|
||
|
|
||
|
/* Only ask Source Manager to process event if there is a Source connected. */
|
||
|
if (DSM_IS_OPEN(twSession) && DS_IS_OPEN(twSession)) {
|
||
|
/*
|
||
|
* A Source provides a modeless dialog box as its user interface.
|
||
|
* The following call relays Windows messages down to the Source's
|
||
|
* UI that were intended for its dialog box. It also retrieves TWAIN
|
||
|
* messages sent from the Source to our Application.
|
||
|
*/
|
||
|
twEvent.pEvent = (TW_MEMREF) lpMsg;
|
||
|
twSession->twRC = callDSM(APP_IDENTITY(twSession), DS_IDENTITY(twSession),
|
||
|
DG_CONTROL, DAT_EVENT, MSG_PROCESSEVENT,
|
||
|
(TW_MEMREF) &twEvent);
|
||
|
|
||
|
/* Check the return code */
|
||
|
if (twSession->twRC == TWRC_NOTDSEVENT) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
/* Process the message as necessary */
|
||
|
switch (twEvent.TWMessage) {
|
||
|
case MSG_XFERREADY:
|
||
|
LogMessage("Source says that data is ready\n");
|
||
|
transferImages(twSession);
|
||
|
break;
|
||
|
|
||
|
case MSG_CLOSEDSREQ:
|
||
|
/* Disable the datasource, Close the Data source
|
||
|
* and close the data source manager
|
||
|
*/
|
||
|
LogMessage("CloseDSReq\n");
|
||
|
disableDS(twSession);
|
||
|
closeDS(twSession);
|
||
|
closeDSM(twSession);
|
||
|
break;
|
||
|
|
||
|
/* No message from the Source to the App break;
|
||
|
* possible new message
|
||
|
*/
|
||
|
case MSG_NULL:
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* tell the caller what happened */
|
||
|
return (twSession->twRC == TWRC_DSEVENT);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* twainMessageLoop
|
||
|
*
|
||
|
* Process Win32 window messages and provide special handling
|
||
|
* of TWAIN specific messages. This loop will not exit until
|
||
|
* the application exits.
|
||
|
*/
|
||
|
int
|
||
|
twainMessageLoop(pTW_SESSION twSession)
|
||
|
{
|
||
|
MSG msg;
|
||
|
|
||
|
while (GetMessage(&msg, NULL, 0, 0)) {
|
||
|
if (DS_IS_CLOSED(twSession) || !TwainProcessMessage(&msg, twSession)) {
|
||
|
TranslateMessage((LPMSG)&msg);
|
||
|
DispatchMessage(&msg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return msg.wParam;
|
||
|
}
|
||
|
|
||
|
/**********************************************************************
|
||
|
* Session related functions
|
||
|
**********************************************************************/
|
||
|
|
||
|
/*
|
||
|
* newSession
|
||
|
*
|
||
|
* Create a new TWAIN session.
|
||
|
*/
|
||
|
pTW_SESSION
|
||
|
newSession(pTW_IDENTITY appIdentity) {
|
||
|
/* Create the structure */
|
||
|
pTW_SESSION session = (pTW_SESSION) malloc (sizeof(TW_SESSION));
|
||
|
|
||
|
/* Set the structure fields */
|
||
|
session->hwnd = 0;
|
||
|
session->twRC = TWRC_SUCCESS;
|
||
|
session->appIdentity = appIdentity;
|
||
|
session->dsIdentity = (pTW_IDENTITY) malloc (sizeof(TW_IDENTITY));
|
||
|
session->dsIdentity->Id = 0;
|
||
|
session->dsIdentity->ProductName[0] = '\0';
|
||
|
session->transferFunctions = NULL;
|
||
|
|
||
|
if (twainIsAvailable())
|
||
|
session->twainState = 2;
|
||
|
else
|
||
|
session->twainState = 0;
|
||
|
|
||
|
return session;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* registerWindowHandle
|
||
|
*
|
||
|
* Register the window handle to be used for this
|
||
|
* session.
|
||
|
*/
|
||
|
void
|
||
|
registerWindowHandle(pTW_SESSION session, HWND hwnd)
|
||
|
{
|
||
|
session->hwnd = hwnd;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* registerTransferCallback
|
||
|
*
|
||
|
* Register the callback to use when transferring
|
||
|
* image data.
|
||
|
*/
|
||
|
void
|
||
|
registerTransferCallbacks(pTW_SESSION session,
|
||
|
pTXFR_CB_FUNCS txfrFuncs,
|
||
|
void *clientData)
|
||
|
{
|
||
|
session->transferFunctions = txfrFuncs;
|
||
|
session->clientData = clientData;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* setClientData
|
||
|
*
|
||
|
* Set the client data associated with the specified
|
||
|
* TWAIN session.
|
||
|
*/
|
||
|
void
|
||
|
setClientData(pTW_SESSION session, void *clientData)
|
||
|
{
|
||
|
session->clientData = clientData;
|
||
|
}
|