[TWAIN_32] Sync with Wine Staging 4.18. CORE-16441
[reactos.git] / dll / win32 / twain_32 / dsm_ctrl.c
index 7ece48a..45f227e 100644 (file)
@@ -2,6 +2,7 @@
  * 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
  *
  * 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 Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 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 "winuser.h"
 #include "twain.h"
 #include "twain_i.h"
+#include "resource.h"
 #include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(twain);
 
-/* DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS */
-TW_UINT16 TWAIN_CloseDS (pTW_IDENTITY pOrigin, TW_MEMREF pData)
-{
-#ifndef HAVE_SANE
-    DSM_twCC = TWCC_NODS;
-    return TWRC_FAILURE;
-#else
-    TW_UINT16 twRC = TWRC_SUCCESS;
-    pTW_IDENTITY pIdentity = (pTW_IDENTITY) pData;
-    activeDS *currentDS = NULL, *prevDS = NULL;
+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 */
+static HWND DSM_parent;
+static UINT event_message;
+
+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", debugstr_a(dsname));
+               return;
+       }
+       dsEntry = (DSENTRYPROC)GetProcAddress(hmod, "DS_Entry"); 
+       if (!dsEntry) {
+               ERR("Failed to find DS_Entry() in TWAIN DS %s\n", debugstr_a(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");
+                        break;
+               }
+               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);
+}
 
-    TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS\n");
+static BOOL detectionrun = FALSE;
 
-    for (currentDS = activeSources; currentDS; currentDS = currentDS->next)
-    {
-        if (currentDS->identity.Id == pIdentity->Id)
-            break;
-        prevDS = currentDS;
-    }
-    if (currentDS)
-    {
-        /* Only valid to close a data source if it is in state 4 */
-        if (currentDS->currentState == 4)
-        {
-            sane_close (currentDS->deviceHandle);
-            /* remove the data source from active data source list */
-            if (prevDS)
-                prevDS->next = currentDS->next;
-            else
-                activeSources = currentDS->next;
-            HeapFree (GetProcessHeap(), 0, currentDS);
-            twRC = TWRC_SUCCESS;
-            DSM_twCC = TWCC_SUCCESS;
-        }
-        else
-        {
-            twRC = TWRC_FAILURE;
-            DSM_twCC = TWCC_SEQERROR;
-        }
-    }
-    else
-    {
-        twRC = TWRC_FAILURE;
-        DSM_twCC = TWCC_NODS;
-    }
+static void
+twain_autodetect(void) {
+       if (detectionrun) return;
+        detectionrun = TRUE;
 
-    return twRC;
+       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_GETDEFAULT */
-TW_UINT16 TWAIN_IdentityGetDefault (pTW_IDENTITY pOrigin, TW_MEMREF pData)
+/* DG_CONTROL/DAT_NULL/MSG_CLOSEDSREQ|MSG_DEVICEEVENT|MSG_XFERREADY */
+TW_UINT16 TWAIN_ControlNull (pTW_IDENTITY pOrigin, pTW_IDENTITY pDest, activeDS *pSource, TW_UINT16 MSG, TW_MEMREF pData)
 {
-#ifndef HAVE_SANE
-    DSM_twCC = TWCC_NODS;
-    return TWRC_FAILURE;
-#else
-    TW_UINT16 twRC = TWRC_SUCCESS;
-    pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) pData;
+    struct pending_message *message;
 
-    TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETDEFAULT\n");
+    TRACE ("DG_CONTROL/DAT_NULL MSG=%i\n", MSG);
 
-    if (!device_list)
+    if (MSG != MSG_CLOSEDSREQ &&
+        MSG != MSG_DEVICEEVENT &&
+        MSG != MSG_XFERREADY)
     {
-        if ((sane_get_devices (&device_list, SANE_FALSE) != SANE_STATUS_GOOD))
-        {
-            DSM_twCC = TWCC_NODS;
-            return TWRC_FAILURE;
-        }
+        DSM_twCC = TWCC_BADPROTOCOL;
+        return TWRC_FAILURE;
     }
 
-    /* FIXME: the default device is not necessarily the first device.  *
-     * Users should be able to choose the default device               */
-    if (device_list && device_list[0])
-    {
-        pSourceIdentity->Id = DSM_sourceId ++;
-        strcpy (pSourceIdentity->ProductName, device_list[0]->name);
-        strcpy (pSourceIdentity->Manufacturer, device_list[0]->vendor);
-        strcpy (pSourceIdentity->ProductFamily, device_list[0]->model);
-        pSourceIdentity->ProtocolMajor = TWON_PROTOCOLMAJOR;
-        pSourceIdentity->ProtocolMinor = TWON_PROTOCOLMINOR;
-
-        twRC = TWRC_SUCCESS;
-        DSM_twCC = TWCC_SUCCESS;
-    }
-    else
+    message = HeapAlloc(GetProcessHeap(), 0, sizeof(*message));
+    if (!message)
     {
-        twRC = TWRC_FAILURE;
-        DSM_twCC = TWCC_NODS;
+        DSM_twCC = TWCC_LOWMEMORY;
+        return TWRC_FAILURE;
     }
 
-    return twRC;
-#endif
+    message->msg = MSG;
+    list_add_tail(&pSource->pending_messages, &message->entry);
+
+    /* Delphi twain only sends us messages from one window, and it
+       doesn't even give us the real handle to that window. Other
+       applications might decide to forward messages sent to DSM_parent
+       or to the one supplied to ENABLEDS. So let's try very hard to
+       find a window that will work. */
+    if (DSM_parent)
+        PostMessageW(DSM_parent, event_message, 0, 0);
+    if (pSource->ui_window && pSource->ui_window != DSM_parent)
+        PostMessageW(pSource->ui_window, event_message, 0, 0);
+    if (pSource->event_window && pSource->event_window != pSource->ui_window &&
+        pSource->event_window != DSM_parent)
+        PostMessageW(pSource->event_window, event_message, 0, 0);
+    PostMessageW(0, event_message, 0, 0);
+
+    return TWRC_SUCCESS;
 }
 
-/* DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST */
-TW_UINT16 TWAIN_IdentityGetFirst (pTW_IDENTITY pOrigin, TW_MEMREF pData)
+/* Filters MSG_PROCESSEVENT messages before reaching the data source */
+TW_UINT16 TWAIN_ProcessEvent (pTW_IDENTITY pOrigin, activeDS *pSource, TW_MEMREF pData)
 {
-#ifndef HAVE_SANE
-    DSM_twCC = TWCC_NODS;
-    return TWRC_FAILURE;
-#else
-    TW_UINT16 twRC = TWRC_SUCCESS;
-    pTW_IDENTITY pSourceIdentity;/* = (pTW_IDENTITY) pData;*/
-    SANE_Status status;
-
-    TRACE ("DG_CONTROL/DAT_IDENTITY/MSG_GETFIRST\n");
-
-    status = sane_get_devices (&device_list, SANE_FALSE);
-    if (status == SANE_STATUS_GOOD)
+    TW_EVENT *event = (TW_EVENT*)pData;
+    MSG *msg = (MSG*)event->pEvent;
+    TW_UINT16 result = TWRC_NOTDSEVENT;
+
+    TRACE("%x,%x\n", msg->message, event_message);
+
+    if (msg->message == event_message)
     {
-        if (device_list[0])
+        if (!list_empty (&pSource->pending_messages))
         {
-            pSourceIdentity->Id = DSM_sourceId ++;
-            strcpy (pSourceIdentity->ProductName, device_list[0]->name);
-            strcpy (pSourceIdentity->Manufacturer, device_list[0]->vendor);
-            strcpy (pSourceIdentity->ProductFamily, device_list[0]->model);
-            pSourceIdentity->ProtocolMajor = TWON_PROTOCOLMAJOR;
-            pSourceIdentity->ProtocolMinor = TWON_PROTOCOLMINOR;
+            struct list *entry = list_head (&pSource->pending_messages);
+            struct pending_message *message = LIST_ENTRY(entry, struct pending_message, entry);
+            event->TWMessage = message->msg;
+            list_remove (entry);
+            TRACE("<-- %x\n", event->TWMessage);
         }
-        DSM_currentDevice = 1;
-        twRC = TWRC_SUCCESS;
-        DSM_twCC = TWCC_SUCCESS;
-    }
-    else if (status == SANE_STATUS_NO_MEM)
-    {
-        twRC = TWRC_FAILURE;
-        DSM_twCC = TWCC_LOWMEMORY;
+        else
+            event->TWMessage = MSG_NULL;
+        result = TWRC_DSEVENT;
     }
-    else
+
+    if (msg->hwnd)
     {
-        WARN("sane_get_devices() failed: %s\n", sane_strstatus (status));
-        twRC = TWRC_FAILURE;
-        DSM_twCC = TWCC_NODS;
+        MSG dummy;
+        pSource->event_window = msg->hwnd;
+        if (!list_empty (&pSource->pending_messages) &&
+            !PeekMessageW(&dummy, msg->hwnd, event_message, event_message, PM_NOREMOVE))
+        {
+            PostMessageW(msg->hwnd, event_message, 0, 0);
+        }
     }
 
-    return twRC;
-#endif
+    return result;
 }
 
-/* DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT */
-TW_UINT16 TWAIN_IdentityGetNext (pTW_IDENTITY pOrigin, TW_MEMREF pData)
+/* DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS */
+TW_UINT16 TWAIN_CloseDS (pTW_IDENTITY pOrigin, TW_MEMREF pData)
 {
-#ifndef HAVE_SANE
-    DSM_twCC = TWCC_SUCCESS;
-    return TWRC_ENDOFLIST;
-#else
-    TW_UINT16 twRC = TWRC_SUCCESS;
-    pTW_IDENTITY pSourceIdentity = (pTW_IDENTITY) 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;
+}
 
-    TRACE("DG_CONTROL/DAT_IDENTITY/MSG_GETNEXT\n");
+/* 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;
+}
 
-    if (device_list && device_list[DSM_currentDevice])
-    {
-        pSourceIdentity->Id = DSM_sourceId ++;
-        strcpy (pSourceIdentity->ProductName, device_list[DSM_currentDevice]->name);
-        strcpy (pSourceIdentity->Manufacturer, device_list[DSM_currentDevice]->vendor);
-        strcpy (pSourceIdentity->ProductFamily, device_list[DSM_currentDevice]->model);
-        pSourceIdentity->ProtocolMajor = TWON_PROTOCOLMAJOR;
-        pSourceIdentity->ProtocolMinor = TWON_PROTOCOLMINOR;
-        DSM_currentDevice ++;
-
-        twRC = TWRC_SUCCESS;
-        DSM_twCC = TWCC_SUCCESS;
-    }
-    else
-    {
-        DSM_twCC = TWCC_SUCCESS;
-        twRC = TWRC_ENDOFLIST;
-    }
+/* 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;
+}
 
-    return twRC;
-#endif
+/* 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)
 {
-#ifndef HAVE_SANE
-    DSM_twCC = TWCC_NODS;
-    return TWRC_FAILURE;
-#else
-    TW_UINT16 twRC = TWRC_SUCCESS, i = 0;
-    pTW_IDENTITY pIdentity = (pTW_IDENTITY) pData;
-    activeDS *newSource;
-    SANE_Status status;
-
-    TRACE("DG_CONTROL/DAT_IDENTITY/MSG_OPENDS\n");
-
-    if (DSM_currentState != 3)
-    {
-        DSM_twCC = TWCC_SEQERROR;
-        return TWRC_FAILURE;
-    }
+       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", debugstr_a(modname));
+               DSM_twCC = TWCC_OPERATIONERROR;
+                HeapFree(GetProcessHeap(), 0, newSource);
+               return TWRC_FAILURE;
+       }
+       newSource->hmod = hmod; 
+       newSource->dsEntry = (DSENTRYPROC)GetProcAddress(hmod, "DS_Entry"); 
+       /* Assign id for the opened data source */
+       pIdentity->Id = DSM_sourceId ++;
+       if (TWRC_SUCCESS != newSource->dsEntry (pOrigin, DG_CONTROL, DAT_IDENTITY, MSG_OPENDS, pIdentity)) {
+               DSM_twCC = TWCC_OPERATIONERROR;
+                HeapFree(GetProcessHeap(), 0, newSource);
+                DSM_sourceId--;
+               return TWRC_FAILURE;
+       }
+       /* add the data source to an internal active source list */
+       newSource->next = activeSources;
+       newSource->identity.Id = pIdentity->Id;
+       strcpy (newSource->identity.ProductName, pIdentity->ProductName);
+        list_init(&newSource->pending_messages);
+        newSource->ui_window = NULL;
+        newSource->event_window = NULL;
+       activeSources = newSource;
+       DSM_twCC = TWCC_SUCCESS;
+       return TWRC_SUCCESS;
+}
 
-    if (!device_list &&
-       (sane_get_devices (&device_list, SANE_FALSE) != SANE_STATUS_GOOD))
-    {
-        DSM_twCC = TWCC_NODS;
-        return TWRC_FAILURE;
-    }
+typedef struct {
+    pTW_IDENTITY origin;
+    pTW_IDENTITY result;
+} userselect_data;
 
-    if (pIdentity->ProductName[0] != '\0')
+static INT_PTR CALLBACK userselect_dlgproc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    switch (msg)
     {
-        /* Make sure the source to be open exists in the device list */
-        for (i = 0; device_list[i]; i ++)
+    case WM_INITDIALOG:
+    {
+        userselect_data *data = (userselect_data*)lparam;
+        int i;
+        HWND sourcelist;
+        BOOL any_devices = FALSE;
+
+        SetWindowLongPtrW(hwnd, DWLP_USER, (LONG_PTR)data);
+
+        sourcelist = GetDlgItem(hwnd, IDC_LISTSOURCE);
+
+        for (i=0; i<nrdevices; i++)
         {
-            if (strcmp (device_list[i]->name, pIdentity->ProductName) == 0)
-                break;
+            TW_IDENTITY *id = &devices[i].identity;
+            LRESULT index;
+
+            if ((id->SupportedGroups & data->origin->SupportedGroups) == 0)
+                continue;
+
+            index = SendMessageA(sourcelist, LB_ADDSTRING, 0, (LPARAM)id->ProductName);
+            SendMessageW(sourcelist, LB_SETITEMDATA, (WPARAM)index, (LPARAM)i);
+            any_devices = TRUE;
         }
-    }
 
-    if (device_list[i])
-    {
-        /* the source is found in the device list */
-        newSource = HeapAlloc (GetProcessHeap(), 0, sizeof (activeDS));
-        if (newSource)
+        if (any_devices)
         {
-            status = sane_open(device_list[i]->name,&newSource->deviceHandle);
-            if (status == SANE_STATUS_GOOD)
-            {
-                /* Assign name and id for the opened data source */
-                strcpy (pIdentity->ProductName, device_list[i]->name);
-                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);
-                newSource->currentState = 4; /*transition into state 4*/
-                newSource->twCC = TWCC_SUCCESS;
-                activeSources = newSource;
-                twRC = TWRC_SUCCESS;
-                DSM_twCC = TWCC_SUCCESS;
-            }
-            else
-            {
-                twRC = TWRC_FAILURE;
-                DSM_twCC = TWCC_OPERATIONERROR;
-            }
+            EnableWindow(GetDlgItem(hwnd, IDOK), TRUE);
+
+            /* FIXME: Select the supplied product name or default source. */
+            SendMessageW(sourcelist, LB_SETCURSEL, 0, 0);
         }
-        else
+
+        return TRUE;
+    }
+    case WM_CLOSE:
+        EndDialog(hwnd, 0);
+        return TRUE;
+    case WM_COMMAND:
+        if (wparam == MAKEWPARAM(IDCANCEL, BN_CLICKED))
         {
-            twRC = TWRC_FAILURE;
-            DSM_twCC = TWCC_LOWMEMORY;
+            EndDialog(hwnd, 0);
+            return TRUE;
         }
-    }
-    else
-    {
-        twRC = TWRC_FAILURE;
-        DSM_twCC = TWCC_NODS;
-    }
+        else if (wparam == MAKEWPARAM(IDOK, BN_CLICKED) ||
+                 wparam == MAKEWPARAM(IDC_LISTSOURCE, LBN_DBLCLK))
+        {
+            userselect_data *data = (userselect_data*)GetWindowLongPtrW(hwnd, DWLP_USER);
+            HWND sourcelist;
+            LRESULT index;
 
-    return twRC;
-#endif
+            sourcelist = GetDlgItem(hwnd, IDC_LISTSOURCE);
+
+            index = SendMessageW(sourcelist, LB_GETCURSEL, 0, 0);
+
+            if (index == LB_ERR)
+                return TRUE;
+
+            index = SendMessageW(sourcelist, LB_GETITEMDATA, (WPARAM)index, 0);
+
+            *data->result = devices[index].identity;
+
+            /* FIXME: Save this as the default source */
+
+            EndDialog(hwnd, 1);
+            return TRUE;
+        }
+        break;
+    }
+    return FALSE;
 }
 
 /* DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT */
 TW_UINT16 TWAIN_UserSelect (pTW_IDENTITY pOrigin, TW_MEMREF pData)
 {
-#ifndef HAVE_SANE
-    return TWRC_SUCCESS;
-#else
-    TW_UINT16 twRC = TWRC_SUCCESS;
+    userselect_data param = {pOrigin, pData};
+    HWND parent = DSM_parent;
+
+    TRACE("DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT SupportedGroups=0x%x ProductName=%s\n",
+        pOrigin->SupportedGroups, wine_dbgstr_a(param.result->ProductName));
+
+    twain_autodetect();
 
-    TRACE("DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT\n");
+    if (!IsWindow(parent))
+        parent = NULL;
 
-    /* FIXME: we should replace xscanimage with our own  User Select UI */
-    system("xscanimage");
+    if (DialogBoxParamW(DSM_hinstance, MAKEINTRESOURCEW(DLG_USERSELECT),
+        parent, userselect_dlgproc, (LPARAM)&param) == 0)
+    {
+        TRACE("canceled\n");
+        DSM_twCC = TWCC_SUCCESS;
+        return TWRC_CANCEL;
+    }
 
+    TRACE("<-- %s\n", wine_dbgstr_a(param.result->ProductName));
     DSM_twCC = TWCC_SUCCESS;
-    return twRC;
-#endif
+    return TWRC_SUCCESS;
 }
 
 /* DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM */
 TW_UINT16 TWAIN_CloseDSM (pTW_IDENTITY pOrigin, TW_MEMREF pData)
 {
-#ifndef HAVE_SANE
-    return TWRC_FAILURE;
-#else
-    TW_UINT16 twRC = TWRC_SUCCESS;
     activeDS *currentDS = activeSources, *nextDS;
 
     TRACE("DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM\n");
 
-    if (DSM_currentState == 3)
+    if (DSM_initialized)
     {
-        sane_exit ();
         DSM_initialized = FALSE;
-        DSM_parentHWND = 0;
-        DSM_currentState = 2;
 
         /* If there are data sources still open, close them now. */
         while (currentDS != NULL)
         {
             nextDS = currentDS->next;
-            sane_close (currentDS->deviceHandle);
+           currentDS->dsEntry (pOrigin, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, pData);
             HeapFree (GetProcessHeap(), 0, currentDS);
             currentDS = nextDS;
         }
         activeSources = NULL;
+        DSM_parent = NULL;
         DSM_twCC = TWCC_SUCCESS;
-        twRC = TWRC_SUCCESS;
-    }
-    else
-    {
+        return TWRC_SUCCESS;
+    } else {
         DSM_twCC = TWCC_SEQERROR;
-        twRC = TWRC_FAILURE;
+        return TWRC_FAILURE;
     }
-
-    return twRC;
-#endif
 }
 
 /* DG_CONTROL/DAT_PARENT/MSG_OPENDSM */
 TW_UINT16 TWAIN_OpenDSM (pTW_IDENTITY pOrigin, TW_MEMREF pData)
 {
-#ifndef HAVE_SANE
-    return TWRC_FAILURE;
-#else
-    TW_UINT16 twRC = TWRC_SUCCESS;
-    SANE_Status status;
-    SANE_Int version_code;
-
-    TRACE("DG_CONTROL/DAT_PARENT/MSG_OPENDSM\n");
-
-    if (DSM_currentState == 2)
-    {
-        if (!DSM_initialized)
-        {
-            DSM_initialized = TRUE;
-            status = sane_init (&version_code, NULL);
-            device_list = NULL;
-            DSM_currentDevice = 0;
-            DSM_sourceId = 0;
-        }
-        DSM_parentHWND = *(TW_HANDLE*)pData;
-        DSM_currentState = 3; /* transition to state 3 */
-        DSM_twCC = TWCC_SUCCESS;
-        twRC = TWRC_SUCCESS;
-    }
-    else
-    {
-        /* operation invoked in invalid state */
-        DSM_twCC = TWCC_SEQERROR;
-        twRC = TWRC_FAILURE;
-    }
-
-    return twRC;
-#endif
+       TW_UINT16 twRC = TWRC_SUCCESS;
+
+       TRACE("DG_CONTROL/DAT_PARENT/MSG_OPENDSM\n");
+        if (!DSM_initialized) {
+                event_message = RegisterWindowMessageA("WINE TWAIN_32 EVENT");
+               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;
+       }
+        DSM_parent = (HWND)pData;
+       return twRC;
 }
 
 /* DG_CONTROL/DAT_STATUS/MSG_GET */
 TW_UINT16 TWAIN_GetDSMStatus (pTW_IDENTITY pOrigin, TW_MEMREF pData)
 {
-    pTW_STATUS pSourceStatus = (pTW_STATUS) pData;
+       pTW_STATUS pSourceStatus = (pTW_STATUS) pData;
 
-    TRACE ("DG_CONTROL/DAT_STATUS/MSG_GET\n");
+       TRACE ("DG_CONTROL/DAT_STATUS/MSG_GET\n");
 
-    pSourceStatus->ConditionCode = DSM_twCC;
-    DSM_twCC = TWCC_SUCCESS;  /* clear the condition code */
-
-    return TWRC_SUCCESS;
+       pSourceStatus->ConditionCode = DSM_twCC;
+       DSM_twCC = TWCC_SUCCESS;  /* clear the condition code */
+       return TWRC_SUCCESS;
 }