[TWAIN_32] Sync with Wine 3.0. CORE-14225
authorAmine Khaldi <amine.khaldi@reactos.org>
Sat, 20 Jan 2018 12:17:45 +0000 (13:17 +0100)
committerAmine Khaldi <amine.khaldi@reactos.org>
Sat, 20 Jan 2018 12:17:45 +0000 (13:17 +0100)
dll/win32/twain_32/CMakeLists.txt
dll/win32/twain_32/dsm_ctrl.c
dll/win32/twain_32/resource.h [new file with mode: 0644]
dll/win32/twain_32/twain.rc [new file with mode: 0644]
dll/win32/twain_32/twain32_main.c
dll/win32/twain_32/twain_32.rc
dll/win32/twain_32/twain_i.h
media/doc/README.WINE

index d25fde9..ac028ed 100644 (file)
@@ -15,6 +15,6 @@ add_library(twain_32 SHARED
 
 set_module_type(twain_32 win32dll)
 target_link_libraries(twain_32 wine)
-add_importlibs(twain_32 msvcrt kernel32 ntdll)
+add_importlibs(twain_32 user32 msvcrt kernel32 ntdll)
 add_pch(twain_32 twain_i.h SOURCE)
 add_cd_file(TARGET twain_32 DESTINATION reactos FOR all)
index aa2a4c1..ce845c9 100644 (file)
 
 #include "twain_i.h"
 
+#include <winuser.h>
+
+#include "resource.h"
+
 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;
@@ -102,6 +108,86 @@ twain_autodetect(void) {
 #endif
 }
 
+/* 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)
+{
+    struct pending_message *message;
+
+    TRACE ("DG_CONTROL/DAT_NULL MSG=%i\n", MSG);
+
+    if (MSG != MSG_CLOSEDSREQ &&
+        MSG != MSG_DEVICEEVENT &&
+        MSG != MSG_XFERREADY)
+    {
+        DSM_twCC = TWCC_BADPROTOCOL;
+        return TWRC_FAILURE;
+    }
+
+    message = HeapAlloc(GetProcessHeap(), 0, sizeof(*message));
+    if (!message)
+    {
+        DSM_twCC = TWCC_LOWMEMORY;
+        return TWRC_FAILURE;
+    }
+
+    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;
+}
+
+/* Filters MSG_PROCESSEVENT messages before reaching the data source */
+TW_UINT16 TWAIN_ProcessEvent (pTW_IDENTITY pOrigin, activeDS *pSource, TW_MEMREF pData)
+{
+    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 (!list_empty (&pSource->pending_messages))
+        {
+            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);
+        }
+        else
+            event->TWMessage = MSG_NULL;
+        result = TWRC_DSEVENT;
+    }
+
+    if (msg->hwnd)
+    {
+        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 result;
+}
+
 /* DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS */
 TW_UINT16 TWAIN_CloseDS (pTW_IDENTITY pOrigin, TW_MEMREF pData)
 {
@@ -230,34 +316,131 @@ TW_UINT16 TWAIN_OpenDS (pTW_IDENTITY pOrigin, TW_MEMREF pData)
        }
        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;
        }
-       /* 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);
+        list_init(&newSource->pending_messages);
+        newSource->ui_window = NULL;
+        newSource->event_window = NULL;
        activeSources = newSource;
        DSM_twCC = TWCC_SUCCESS;
        return TWRC_SUCCESS;
 }
 
+typedef struct {
+    pTW_IDENTITY origin;
+    pTW_IDENTITY result;
+} userselect_data;
+
+static INT_PTR CALLBACK userselect_dlgproc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    switch (msg)
+    {
+    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++)
+        {
+            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 (any_devices)
+        {
+            EnableWindow(GetDlgItem(hwnd, IDOK), TRUE);
+
+            /* FIXME: Select the supplied product name or default source. */
+            SendMessageW(sourcelist, LB_SETCURSEL, 0, 0);
+        }
+
+        return TRUE;
+    }
+    case WM_CLOSE:
+        EndDialog(hwnd, 0);
+        return TRUE;
+    case WM_COMMAND:
+        if (wparam == MAKEWPARAM(IDCANCEL, BN_CLICKED))
+        {
+            EndDialog(hwnd, 0);
+            return TRUE;
+        }
+        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;
+
+            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)
 {
-       pTW_IDENTITY    selected = (pTW_IDENTITY)pData;
+    userselect_data param = {pOrigin, pData};
+    HWND parent = DSM_parent;
 
-       if (!nrdevices) {
-               DSM_twCC = TWCC_OPERATIONERROR;
-               return TWRC_FAILURE;
-       }
-       *selected = devices[0].identity;
-       DSM_twCC = TWCC_SUCCESS;
-       return TWRC_SUCCESS;
+    TRACE("DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT SupportedGroups=0x%x ProductName=%s\n",
+        pOrigin->SupportedGroups, wine_dbgstr_a(param.result->ProductName));
+
+    twain_autodetect();
+
+    if (!IsWindow(parent))
+        parent = NULL;
+
+    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_SUCCESS;
 }
 
 /* DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM */
@@ -280,6 +463,7 @@ TW_UINT16 TWAIN_CloseDSM (pTW_IDENTITY pOrigin, TW_MEMREF pData)
             currentDS = nextDS;
         }
         activeSources = NULL;
+        DSM_parent = NULL;
         DSM_twCC = TWCC_SUCCESS;
         return TWRC_SUCCESS;
     } else {
@@ -295,6 +479,7 @@ TW_UINT16 TWAIN_OpenDSM (pTW_IDENTITY pOrigin, TW_MEMREF pData)
 
        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;
@@ -304,6 +489,7 @@ TW_UINT16 TWAIN_OpenDSM (pTW_IDENTITY pOrigin, TW_MEMREF pData)
                DSM_twCC = TWCC_SEQERROR;
                twRC = TWRC_FAILURE;
        }
+        DSM_parent = (HWND)pData;
        return twRC;
 }
 
diff --git a/dll/win32/twain_32/resource.h b/dll/win32/twain_32/resource.h
new file mode 100644 (file)
index 0000000..1cbc26e
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2017 Vincent Povirk for CodeWeavers
+ *
+ * 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 <windef.h>
+#include <winuser.h>
+
+#define DLG_USERSELECT 1
+
+#define IDC_LISTSOURCE 1001
diff --git a/dll/win32/twain_32/twain.rc b/dll/win32/twain_32/twain.rc
new file mode 100644 (file)
index 0000000..339ead6
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017 Vincent Povirk for CodeWeavers
+ *
+ * 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 "resource.h"
+
+#pragma makedep po
+
+LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT
+
+DLG_USERSELECT DIALOG  0, 0, 140, 140
+STYLE DS_MODALFRAME | DS_3DLOOK | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_NOIDLEMSG
+FONT 8, "MS Shell Dlg"
+CAPTION "Select Source"
+{
+    LISTBOX         IDC_LISTSOURCE,      7,     7,  126, 104
+    DEFPUSHBUTTON   "OK",     IDOK,      29,  118,  50,  15,  BS_DEFPUSHBUTTON|WS_TABSTOP|WS_DISABLED
+    PUSHBUTTON      "Cancel", IDCANCEL,  83,  118,  50,  15
+}
index 4044cb2..1607c9b 100644 (file)
 
 #include "twain_i.h"
 
+extern HINSTANCE DSM_hinstance;
+
+BOOL WINAPI DllMain (HINSTANCE hinstance, DWORD reason, LPVOID reserved)
+{
+    TRACE("%p,%x,%p\n", hinstance, reason, reserved);
+
+    switch (reason)
+    {
+        case DLL_PROCESS_ATTACH:
+            DisableThreadLibraryCalls(hinstance);
+            DSM_hinstance = hinstance;
+            break;
+        case DLL_PROCESS_DETACH:
+            break;
+    }
+    return TRUE;
+}
+
 /* A helper function that looks up a destination identity in the active
    source list */
 static activeDS *TWAIN_LookupSource (const TW_IDENTITY *pDest)
@@ -132,6 +150,19 @@ DSM_Entry (pTW_IDENTITY pOrigin,
 
     TRACE("(DG=%d DAT=%d MSG=%d)\n", DG, DAT, MSG);
 
+    if (DG == DG_CONTROL && DAT == DAT_NULL)
+    {
+        activeDS *pSource = TWAIN_LookupSource (pOrigin);
+        if (!pSource)
+        {
+            ERR("No source associated with pSource %p\n", pDest);
+            DSM_twCC = TWCC_BADPROTOCOL;
+            return TWRC_FAILURE;
+        }
+
+        return TWAIN_ControlNull (pOrigin, pDest, pSource, MSG, pData);
+    }
+
     if (pDest)
     {
         activeDS *pSource = TWAIN_LookupSource (pDest);
@@ -142,6 +173,21 @@ DSM_Entry (pTW_IDENTITY pOrigin,
            DSM_twCC = TWCC_BADDEST;
            return TWRC_FAILURE;
        }
+
+        if (DG == DG_CONTROL && DAT == DAT_EVENT && MSG == MSG_PROCESSEVENT)
+        {
+            twRC = TWAIN_ProcessEvent(pOrigin, pSource, pData);
+            if (twRC == TWRC_DSEVENT)
+                return twRC;
+        }
+
+        if (DG == DG_CONTROL && DAT == DAT_USERINTERFACE &&
+            (MSG == MSG_ENABLEDS || MSG == MSG_ENABLEDSUIONLY) &&
+            pData != NULL)
+        {
+            pSource->ui_window = ((TW_USERINTERFACE*)pData)->hParent;
+        }
+
        DSM_twCC = TWCC_SUCCESS;
         TRACE("Forwarding %d/%d/%d/%p to DS.\n", DG, DAT, MSG, pData);
        twRC = pSource->dsEntry(pOrigin, DG, DAT, MSG, pData);
index a09a0ee..bf1cff4 100644 (file)
@@ -3,3 +3,6 @@
 #define REACTOS_STR_INTERNAL_NAME     "twain_32"
 #define REACTOS_STR_ORIGINAL_FILENAME "twain_32.dll"
 #include <reactos/version.rc>
+
+#include "twain.rc"
+
index a170454..32ebc2a 100644 (file)
 #include <twain.h>
 
 #include <wine/debug.h>
+#include <wine/list.h>
+
 WINE_DEFAULT_DEBUG_CHANNEL(twain);
 
+struct pending_message
+{
+    struct list entry;
+    TW_UINT16 msg;
+};
+
 /* internal information about an active data source */
 typedef struct tagActiveDS
 {
@@ -41,12 +49,17 @@ typedef struct tagActiveDS
     TW_IDENTITY                identity;               /* identity */
     HMODULE            hmod;
     DSENTRYPROC                dsEntry;
+    struct list         pending_messages;
+    HWND                ui_window;
+    HWND                event_window;
 } activeDS;
 
 TW_UINT16 DSM_twCC DECLSPEC_HIDDEN;             /* current condition code of Source Manager */
 
 activeDS *activeSources DECLSPEC_HIDDEN;       /* list of active data sources */
 
+HINSTANCE DSM_hinstance DECLSPEC_HIDDEN;
+
 /* Implementation of operation triplets (From Application to Source Manager) */
 extern TW_UINT16 TWAIN_CloseDS
            (pTW_IDENTITY pOrigin, TW_MEMREF pData) DECLSPEC_HIDDEN;
@@ -66,5 +79,9 @@ extern TW_UINT16 TWAIN_OpenDSM
            (pTW_IDENTITY pOrigin, TW_MEMREF pData) DECLSPEC_HIDDEN;
 extern TW_UINT16 TWAIN_GetDSMStatus
            (pTW_IDENTITY pOrigin, TW_MEMREF pData) DECLSPEC_HIDDEN;
+extern TW_UINT16 TWAIN_ControlNull
+           (pTW_IDENTITY pOrigin, pTW_IDENTITY pDest, activeDS *pSource, TW_UINT16 MSG, TW_MEMREF pData) DECLSPEC_HIDDEN;
+extern TW_UINT16 TWAIN_ProcessEvent
+           (pTW_IDENTITY pOrigin, activeDS *pSource, TW_MEMREF pData) DECLSPEC_HIDDEN;
 
 #endif /* _TWAIN32_H */
index a4f3a13..5106521 100644 (file)
@@ -185,7 +185,7 @@ reactos/dll/win32/sxs                 # Synced to WineStaging-2.16
 reactos/dll/win32/t2embed             # Synced to WineStaging-2.9
 reactos/dll/win32/tapi32              # Synced to WineStaging-2.9
 reactos/dll/win32/traffic             # Synced to WineStaging-2.9
-reactos/dll/win32/twain_32            # Synced to WineStaging-2.9
+reactos/dll/win32/twain_32            # Synced to Wine-3.0
 reactos/dll/win32/updspapi            # Synced to WineStaging-2.9
 reactos/dll/win32/url                 # Synced to WineStaging-2.9
 reactos/dll/win32/urlmon              # Synced to WineStaging-2.16