From 0edcbac349451ffbf0a99e84a4a8ad74ea641096 Mon Sep 17 00:00:00 2001 From: Amine Khaldi Date: Sat, 20 Jan 2018 13:17:45 +0100 Subject: [PATCH] [TWAIN_32] Sync with Wine 3.0. CORE-14225 --- dll/win32/twain_32/CMakeLists.txt | 2 +- dll/win32/twain_32/dsm_ctrl.c | 206 ++++++++++++++++++++++++++++-- dll/win32/twain_32/resource.h | 24 ++++ dll/win32/twain_32/twain.rc | 33 +++++ dll/win32/twain_32/twain32_main.c | 46 +++++++ dll/win32/twain_32/twain_32.rc | 3 + dll/win32/twain_32/twain_i.h | 17 +++ media/doc/README.WINE | 2 +- 8 files changed, 321 insertions(+), 12 deletions(-) create mode 100644 dll/win32/twain_32/resource.h create mode 100644 dll/win32/twain_32/twain.rc diff --git a/dll/win32/twain_32/CMakeLists.txt b/dll/win32/twain_32/CMakeLists.txt index d25fde9596b..ac028ed450e 100644 --- a/dll/win32/twain_32/CMakeLists.txt +++ b/dll/win32/twain_32/CMakeLists.txt @@ -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) diff --git a/dll/win32/twain_32/dsm_ctrl.c b/dll/win32/twain_32/dsm_ctrl.c index aa2a4c1188b..ce845c95319 100644 --- a/dll/win32/twain_32/dsm_ctrl.c +++ b/dll/win32/twain_32/dsm_ctrl.c @@ -21,9 +21,15 @@ #include "twain_i.h" +#include + +#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; iSupportedGroups & 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)¶m) == 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 index 00000000000..1cbc26e7330 --- /dev/null +++ b/dll/win32/twain_32/resource.h @@ -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 +#include + +#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 index 00000000000..339ead68bb2 --- /dev/null +++ b/dll/win32/twain_32/twain.rc @@ -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 +} diff --git a/dll/win32/twain_32/twain32_main.c b/dll/win32/twain_32/twain32_main.c index 4044cb2dcc7..1607c9b2d78 100644 --- a/dll/win32/twain_32/twain32_main.c +++ b/dll/win32/twain_32/twain32_main.c @@ -21,6 +21,24 @@ #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); diff --git a/dll/win32/twain_32/twain_32.rc b/dll/win32/twain_32/twain_32.rc index a09a0ee7e56..bf1cff4e7ee 100644 --- a/dll/win32/twain_32/twain_32.rc +++ b/dll/win32/twain_32/twain_32.rc @@ -3,3 +3,6 @@ #define REACTOS_STR_INTERNAL_NAME "twain_32" #define REACTOS_STR_ORIGINAL_FILENAME "twain_32.dll" #include + +#include "twain.rc" + diff --git a/dll/win32/twain_32/twain_i.h b/dll/win32/twain_32/twain_i.h index a170454a4e0..32ebc2a2217 100644 --- a/dll/win32/twain_32/twain_i.h +++ b/dll/win32/twain_32/twain_i.h @@ -32,8 +32,16 @@ #include #include +#include + 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 */ diff --git a/media/doc/README.WINE b/media/doc/README.WINE index a4f3a1397d3..5106521886b 100644 --- a/media/doc/README.WINE +++ b/media/doc/README.WINE @@ -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 -- 2.17.1