From: Damjan Jovanovic Subject: [PATCH 7/7] winex11.drv: use the original IDataObject for Wine to Wine drag and drop (try 3) Message-Id: Date: Sat, 18 Jul 2015 10:43:18 +0200 Marshal the drag source's IDataObject into an X11 window property, and get the drop target to use it instead of XdndSelection and the X11 targets, so that format conversions within a Wine application or between 2 Wine applications are lossless. Try 2 and 3: no changes. Damjan Jovanovic --- dlls/winex11.drv/x11drv.h | 2 + dlls/winex11.drv/x11drv_main.c | 2 + dlls/winex11.drv/xdnd.c | 148 ++++++++++++++++++++++++++++++++++------- 3 files changed, 129 insertions(+), 23 deletions(-) diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 5f277e3..3886050 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -481,6 +481,8 @@ enum x11drv_atoms XATOM_WCF_SYLK, XATOM_WCF_TIFF, XATOM_WCF_WAVE, + XATOM_application_octet_stream, + XATOM_application_x_wine_dataobject, XATOM_image_bmp, XATOM_image_gif, XATOM_image_jpeg, diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index af730cd..395a63c 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -190,6 +190,8 @@ static const char * const atom_names[NB_XATOMS - FIRST_XATOM] = "WCF_SYLK", "WCF_TIFF", "WCF_WAVE", + "application/octet-stream", + "application/x-wine-dataobject", "image/bmp", "image/gif", "image/jpeg", diff --git a/dlls/winex11.drv/xdnd.c b/dlls/winex11.drv/xdnd.c index 9cd72da..155f45c 100644 --- a/dlls/winex11.drv/xdnd.c +++ b/dlls/winex11.drv/xdnd.c @@ -59,7 +59,10 @@ static Atom *XDNDTypeList = NULL; static unsigned long XDNDTypeListSize = 0; static POINT XDNDxy = { 0, 0 }; static Time XDNDTimestamp; -static IDataObject XDNDDataObject; +/* converts from XDND types */ +static IDataObject XDNDInternalDataObject; +/* points to either XDNDInternalDataObject, or the source Wine's window's IDataObject */ +static IDataObject *XDNDDataObject; static BOOL XDNDAccepted = FALSE; static DWORD XDNDKeys = 0; static DWORD XDNDDropEffect = DROPEFFECT_NONE; @@ -146,7 +149,7 @@ static IDropTarget* get_droptarget_pointer(HWND hwnd) * chosen by the drag source based on the Ctrl/Shift/Alt key state, only * chooses a preferred action among these. On the drop target side, * the union of XdndActionList and the preferred action become the drop - * effects offered to the drop target, while the preferred action is translated + * effects offered to our drop target, while the preferred action is translated * into the keyboard buttons that indicate that action. */ /************************************************************************** @@ -247,6 +250,38 @@ static DWORD get_keys_for_action(Atom action) return 0; } +static HRESULT marshal_dataobject(IDataObject *dataObject, IStream **pStream, DWORD *pSize) +{ + HRESULT hr; + IStream *stream = NULL; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); + if (SUCCEEDED(hr)) + { + hr = CoMarshalInterface(stream, &IID_IDataObject, (IUnknown*)dataObject, MSHCTX_LOCAL, + NULL, MSHLFLAGS_TABLESTRONG); + if (SUCCEEDED(hr)) + { + STATSTG statStorage; + LARGE_INTEGER zero; + zero.QuadPart = 0; + IStream_Seek(stream, zero, STREAM_SEEK_SET, NULL); + hr = IStream_Stat(stream, &statStorage, STATFLAG_NONAME); + if (SUCCEEDED(hr)) + { + *pStream = stream; + *pSize = statStorage.cbSize.LowPart; + return S_OK; + } + else + CoReleaseMarshalData(stream); + } + IStream_Release(stream); + } + return hr; +} + + /************************************************************************** * X11DRV_XDND_EnterEvent * @@ -255,6 +290,11 @@ static DWORD get_keys_for_action(Atom action) void X11DRV_XDND_EnterEvent( HWND hWnd, XClientMessageEvent *event ) { int version; + Atom acttype; + int actfmt; + unsigned long bytesret; + unsigned char *marshalledDataObject = NULL; + unsigned long marshalledDataObjectSize; version = (event->data.l[1] & 0xFF000000) >> 24; TRACE("ver(%d) check-XdndTypeList(%ld) data=%ld,%ld,%ld,%ld,%ld\n", @@ -272,13 +312,52 @@ void X11DRV_XDND_EnterEvent( HWND hWnd, XClientMessageEvent *event ) XDNDDropSourceWindow = event->data.l[0]; XDNDDropTargetWindow = event->window; + XGetWindowProperty(event->display, event->data.l[0], x11drv_atom(application_x_wine_dataobject), + 0, 65535, FALSE, x11drv_atom(application_octet_stream), &acttype, &actfmt, &marshalledDataObjectSize, + &bytesret, (unsigned char**)&marshalledDataObject); + if (marshalledDataObject) + { + /* The source window must also be a Wine window, so use its IDataObject directly. */ + if (acttype == x11drv_atom(application_octet_stream) && actfmt == 8) + { + HGLOBAL gmem = GlobalAlloc(GMEM_FIXED, marshalledDataObjectSize); + HRESULT hr; + if (gmem) + { + IStream *stream = NULL; + memcpy(gmem, marshalledDataObject, marshalledDataObjectSize); + hr = CreateStreamOnHGlobal(gmem, FALSE, &stream); + if (SUCCEEDED(hr)) + { + ULARGE_INTEGER size; + size.QuadPart = marshalledDataObjectSize; + IStream_SetSize(stream, size); + hr = CoUnmarshalInterface(stream, &IID_IDataObject, (LPVOID*)&XDNDDataObject); + if (FAILED(hr)) + ERR("could not unmarshal IDataObject, hr=0x%08x\n", hr); + IStream_Release(stream); + } + else + ERR("could not create stream, hr=0x%08x\n", hr); + GlobalFree(gmem); + } + else + ERR("out of memory for IDataObject\n"); + } + XFree(marshalledDataObject); + } + if (XDNDDataObject) + { + TRACE("XDNDEnter Wine to Wine drag and drop\n"); + return; + } + /* Otherwise fall back to converting from X11 types */ + XDNDDataObject = &XDNDInternalDataObject; + /* If the source supports more than 3 data types we retrieve * the entire list. */ if (event->data.l[1] & 1) { - Atom acttype; - int actfmt; - unsigned long bytesret; Atom *xdndtypes = NULL; /* Request supported formats from source window */ @@ -387,7 +466,7 @@ void X11DRV_XDND_PositionEvent( HWND hWnd, XClientMessageEvent *event ) XDNDLastDropTargetWnd = dropTargetWindow; if (dropTarget) { - hr = IDropTarget_DragEnter(dropTarget, &XDNDDataObject, + hr = IDropTarget_DragEnter(dropTarget, XDNDDataObject, XDNDKeys, pointl, &effect); if (SUCCEEDED(hr)) { @@ -481,7 +560,7 @@ void X11DRV_XDND_DropEvent( HWND hWnd, XClientMessageEvent *event ) pointl.x = XDNDxy.x; pointl.y = XDNDxy.y; - hr = IDropTarget_Drop(dropTarget, &XDNDDataObject, XDNDKeys, + hr = IDropTarget_Drop(dropTarget, XDNDDataObject, XDNDKeys, pointl, &effect); if (SUCCEEDED(hr)) { @@ -562,21 +641,16 @@ void X11DRV_XDND_LeaveEvent( HWND hWnd, XClientMessageEvent *event ) */ static BOOL X11DRV_XDND_HasHDROP(void) { - BOOL found = FALSE; - DWORD i; + HRESULT hr; + FORMATETC formatEtc; - /* Find CF_HDROP type if any */ - for (i = 0; i < XDNDTypeListSize; i++) - { - UINT windowsFormat = X11DRV_CLIPBOARD_X11AtomToWindowsFormat(XDNDTypeList[i]); - if (windowsFormat == CF_HDROP) - { - found = TRUE; - break; - } - } + formatEtc.cfFormat = CF_HDROP; + formatEtc.dwAspect = DVASPECT_CONTENT; + formatEtc.ptd = NULL; + formatEtc.tymed = TYMED_HGLOBAL; - return found; + hr = IDataObject_QueryGetData(XDNDDataObject, &formatEtc); + return hr == S_OK; } @@ -594,7 +668,7 @@ static HRESULT X11DRV_XDND_SendDropFiles(HWND hwnd) formatEtc.ptd = NULL; formatEtc.tymed = TYMED_HGLOBAL; - hr = IDataObject_GetData(&XDNDDataObject, &formatEtc, &stgMedium); + hr = IDataObject_GetData(XDNDDataObject, &formatEtc, &stgMedium); if (SUCCEEDED(hr)) { DROPFILES *lpDrop = GlobalLock(stgMedium.u.hGlobal); @@ -630,6 +704,8 @@ static void X11DRV_XDND_FreeDragDropOp(void) HeapFree(GetProcessHeap(), 0, XDNDTypeList); XDNDTypeList = NULL; XDNDTypeListSize = 0; + IDataObject_Release(XDNDDataObject); + XDNDDataObject = NULL; XDNDxy.x = XDNDxy.y = 0; XDNDLastTargetWnd = NULL; XDNDLastDropTargetWnd = NULL; @@ -900,7 +976,7 @@ static HRESULT WINAPI XDNDDATAOBJECT_EnumDAdvise(IDataObject *dataObject, return OLE_E_ADVISENOTSUPPORTED; } -static IDataObjectVtbl xdndDataObjectVtbl = +static IDataObjectVtbl xdndInternalDataObjectVtbl = { XDNDDATAOBJECT_QueryInterface, XDNDDATAOBJECT_AddRef, @@ -916,7 +992,7 @@ static IDataObjectVtbl xdndDataObjectVtbl = XDNDDATAOBJECT_EnumDAdvise }; -static IDataObject XDNDDataObject = { &xdndDataObjectVtbl }; +static IDataObject XDNDInternalDataObject = { &xdndInternalDataObjectVtbl }; /* ============================ * The dragging implementation: @@ -934,6 +1010,8 @@ static const WCHAR X11DRV_DRAGTRACKERCLASS[] = typedef struct tagDragState { IDataObject* dataObject; + IStream* marshalledDataObject; + DWORD marshalledDataObjectSize; IDropSource* dropSource; DWORD dwOKEffect; DWORD* pdwEffect; @@ -1631,6 +1709,11 @@ static void drag_cleanup(void) HeapFree(GetProcessHeap(), 0, dragState.xdndTypeList); if (XGetSelectionOwner(thread_display(), x11drv_atom(XdndSelection)) == dragState.source) XSetSelectionOwner(thread_display(), x11drv_atom(XdndSelection), None, CurrentTime); + if (dragState.marshalledDataObject) + { + CoReleaseMarshalData(dragState.marshalledDataObject); + IStream_Release(dragState.marshalledDataObject); + } } /************************************************************************** @@ -1681,6 +1764,25 @@ HRESULT CDECL X11DRV_DoDragDrop ( return hr; } dragState.dataObject = pDataObject; + dragState.marshalledDataObject = NULL; + /* unlike XdndTypeList, the IDataObject property is optional, so it's allowed to fail: */ + hr = marshal_dataobject(dragState.dataObject, &dragState.marshalledDataObject, &dragState.marshalledDataObjectSize); + if (SUCCEEDED(hr)) + { + HGLOBAL memory; + hr = GetHGlobalFromStream(dragState.marshalledDataObject, &memory); + if (SUCCEEDED(hr)) + { + XChangeProperty(thread_display(), dragState.source, x11drv_atom(application_x_wine_dataobject), + x11drv_atom(application_octet_stream), 8, PropModeReplace, + (const unsigned char*)GlobalLock(memory), dragState.marshalledDataObjectSize); + GlobalUnlock(memory); + } + else + ERR("GetHGlobalFromStream() failed, hr=0x%08x\n", hr); + } + else + ERR("could not marshal IDataObject, hr=0x%08x\n", hr); dragState.dropSource = pDropSource; dragState.dwOKEffect = dwOKEffect; dragState.pdwEffect = pdwEffect;