summaryrefslogtreecommitdiffstats
path: root/plug-ins/twain/tw_func.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:23:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:23:22 +0000
commite42129241681dde7adae7d20697e7b421682fbb4 (patch)
treeaf1fe815a5e639e68e59fabd8395ec69458b3e5e /plug-ins/twain/tw_func.c
parentInitial commit. (diff)
downloadgimp-e42129241681dde7adae7d20697e7b421682fbb4.tar.xz
gimp-e42129241681dde7adae7d20697e7b421682fbb4.zip
Adding upstream version 2.10.22.upstream/2.10.22upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plug-ins/twain/tw_func.c')
-rw-r--r--plug-ins/twain/tw_func.c874
1 files changed, 874 insertions, 0 deletions
diff --git a/plug-ins/twain/tw_func.c b/plug-ins/twain/tw_func.c
new file mode 100644
index 0000000..8baa7b3
--- /dev/null
+++ b/plug-ins/twain/tw_func.c
@@ -0,0 +1,874 @@
+/*
+ * TWAIN Plug-in
+ * Copyright (C) 1999 Craig Setera
+ * Craig Setera <setera@home.com>
+ * 03/31/1999
+ *
+ * Updated for Mac OS X support
+ * Brion Vibber <brion@pobox.com>
+ * 07/22/2004
+ *
+ * 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 3 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, see <https://www.gnu.org/licenses/>.
+ *
+ *
+ * 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:
+ * Should compile and run on both Win32 and Mac OS X 10.3 (possibly
+ * also on 10.2).
+ *
+ * Known problems:
+ * - Multiple image transfers will hang the plug-in. The current
+ * configuration compiles with a maximum of single image transfers.
+ * - On Mac OS X, canceling doesn't always close things out fully.
+ * - Epson TWAIN driver on Mac OS X crashes the plugin when scanning.
+ */
+
+/*
+ * 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.
+ * (07/23/04) v0.6 Added Mac OS X support.
+ */
+
+#include "config.h"
+
+#include <glib.h> /* Needed when compiling with gcc */
+
+#include "tw_func.h"
+#include "tw_util.h"
+#include "tw_local.h"
+
+/*
+ * 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
+};
+
+/*
+ * 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;
+}
+
+/*
+ * 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);
+}
+
+
+/*
+ * 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 = twainAllocHandle(sizeof(TW_ONEVALUE));
+
+ pvalOneValue = (pTW_ONEVALUE) twainLockHandle(bufXfer.hContainer);
+ pvalOneValue->ItemType = TWTY_UINT16;
+ pvalOneValue->Item = TWSX_MEMORY;
+ twainUnlockHandle(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 */
+ twainFreeHandle(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, gboolean showUI)
+{
+ /* Make sure in the correct state */
+ if (DS_IS_CLOSED(twSession)) {
+ LogMessage("Can't acquire image with closed datasource\n");
+ return FALSE;
+ }
+
+ twainSetupCallback(twSession);
+
+ /* Set the transfer mode */
+ if (setBufferedXfer(twSession)) {
+ TW_USERINTERFACE ui;
+
+ /* Set the UI information */
+ ui.ShowUI = TRUE;
+ ui.ModalUI = TRUE;
+ /* In Windows, the callbacks are sent to the window message handler */
+ 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,
+ (TW_MEMREF)&(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);
+}
+
+
+/*
+ * 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 = g_new (char, 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 */
+ g_free (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)
+{
+ gboolean 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);
+}
+
+void
+processTwainMessage(TW_UINT16 message, pTW_SESSION twSession)
+{
+ switch (message) {
+ 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;
+ }
+}
+
+/**********************************************************************
+ * Session related functions
+ **********************************************************************/
+
+/*
+ * newSession
+ *
+ * Create a new TWAIN session.
+ */
+pTW_SESSION
+newSession(pTW_IDENTITY appIdentity) {
+ /* Create the structure */
+ pTW_SESSION session = g_new (TW_SESSION, 1);
+
+ /* Set the structure fields */
+ session->hwnd = 0;
+ session->twRC = TWRC_SUCCESS;
+ session->appIdentity = appIdentity;
+ session->dsIdentity = g_new (TW_IDENTITY, 1);
+ 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, TW_HANDLE 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;
+}