From: Damjan Jovanovic Subject: [PATCH 5/7] winex11.drv: only fetch DND data when requested by the receiving application Message-Id: Date: Mon, 25 Apr 2016 03:00:58 +0200 We currently fetch all the DND data when the first XdndPosition message arrives, and cache it for the duration of the drop. The spec does allows this, but: * it's not what Windows does. * even on X11, the drag source can change the data during the drag, causing the drop target to receive outdated data. * clipboard formats can have side effects (ICCCM 2.6.3): "DELETE" can cause data loss if requested during a move operation before the drop has occured. Blindly requesting every data type could be dangerous. Signed-off-by: Damjan Jovanovic --- dlls/winex11.drv/clipboard.c | 12 ++ dlls/winex11.drv/x11drv.h | 1 + dlls/winex11.drv/xdnd.c | 279 +++++++++++++++---------------------------- 3 files changed, 107 insertions(+), 185 deletions(-) diff --git a/dlls/winex11.drv/clipboard.c b/dlls/winex11.drv/clipboard.c index 5dc5e3c..212f79e 100644 --- a/dlls/winex11.drv/clipboard.c +++ b/dlls/winex11.drv/clipboard.c @@ -1703,6 +1703,18 @@ HANDLE X11DRV_CLIPBOARD_ImportSelection(Display *d, Atom target, Window w, Atom return NULL; } +/************************************************************************** + * X11DRV_CLIPBOARD_X11AtomToWindowsFormat + * + * Returns the Windows format equivalent to the given X11 atom. + */ +UINT X11DRV_CLIPBOARD_X11AtomToWindowsFormat(Atom x11atom) +{ + WINE_CLIPFORMAT *clipFormat = X11DRV_CLIPBOARD_LookupProperty(NULL, x11atom); + if (clipFormat) + return clipFormat->wFormatID; + return 0; +} /************************************************************************** X11DRV_CLIPBOARD_ExportClipboardData diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 23f4ab6..31f6ec3 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -237,6 +237,7 @@ extern void X11DRV_XDND_PositionEvent( HWND hWnd, XClientMessageEvent *event ) D extern void X11DRV_XDND_DropEvent( HWND hWnd, XClientMessageEvent *event ) DECLSPEC_HIDDEN; extern void X11DRV_XDND_LeaveEvent( HWND hWnd, XClientMessageEvent *event ) DECLSPEC_HIDDEN; extern HANDLE X11DRV_CLIPBOARD_ImportSelection(Display *d, Atom target, Window w, Atom prop, UINT *windowsFormat) DECLSPEC_HIDDEN; +extern UINT X11DRV_CLIPBOARD_X11AtomToWindowsFormat(Atom x11atom) DECLSPEC_HIDDEN; /************************************************************************** * X11 GDI driver diff --git a/dlls/winex11.drv/xdnd.c b/dlls/winex11.drv/xdnd.c index c9fddbe..19a1b50 100644 --- a/dlls/winex11.drv/xdnd.c +++ b/dlls/winex11.drv/xdnd.c @@ -53,19 +53,12 @@ WINE_DEFAULT_DEBUG_CHANNEL(xdnd); #define SELECTION_RETRIES 500 /* wait for .1 seconds */ #define SELECTION_WAIT 1000 /* us */ -typedef struct tagXDNDDATA -{ - int cf_win; - Atom cf_xdnd; - HANDLE contents; - struct list entry; -} XDNDDATA, *LPXDNDDATA; - -static struct list xdndData = LIST_INIT(xdndData); +static Window XDNDDropSourceWindow = None; +static Window XDNDDropTargetWindow = None; static Atom *XDNDTypeList = NULL; static unsigned long XDNDTypeListSize = 0; -static BOOL XDNDResolvedProperties = FALSE; static POINT XDNDxy = { 0, 0 }; +static Time XDNDTimestamp; static IDataObject XDNDDataObject; static BOOL XDNDAccepted = FALSE; static DWORD XDNDDropEffect = DROPEFFECT_NONE; @@ -74,9 +67,6 @@ static HWND XDNDLastTargetWnd; /* might be an ancestor of XDNDLastTargetWnd */ static HWND XDNDLastDropTargetWnd; -static void X11DRV_XDND_InsertXDNDData(int property, int format, HANDLE contents); -static void X11DRV_XDND_ResolveProperty(Display *display, Window xwin, Time tm, - Atom *types, unsigned long count); static BOOL X11DRV_XDND_HasHDROP(void); static HRESULT X11DRV_XDND_SendDropFiles(HWND hwnd); static void X11DRV_XDND_FreeDragDropOp(void); @@ -208,6 +198,8 @@ void X11DRV_XDND_EnterEvent( HWND hWnd, XClientMessageEvent *event ) } XDNDAccepted = FALSE; + XDNDDropSourceWindow = event->data.l[0]; + XDNDDropTargetWindow = event->window; /* If the source supports more than 3 data types we retrieve * the entire list. */ @@ -273,28 +265,21 @@ void X11DRV_XDND_PositionEvent( HWND hWnd, XClientMessageEvent *event ) XClientMessageEvent e; int accept = 0; /* Assume we're not accepting */ IDropTarget *dropTarget = NULL; - Time time; DWORD effect; POINTL pointl; HWND targetWindow; HRESULT hr; + XDNDDropSourceWindow = event->data.l[0]; + XDNDDropTargetWindow = event->window; XDNDxy = root_to_virtual_screen( event->data.l[2] >> 16, event->data.l[2] & 0xFFFF ); targetWindow = WindowFromPoint(XDNDxy); pointl.x = XDNDxy.x; pointl.y = XDNDxy.y; - time = event->data.l[3]; + XDNDTimestamp = event->data.l[3]; effect = X11DRV_XDND_XdndActionToDROPEFFECT(event->data.l[4]); - /* Do a one-time data read and cache results */ - if (!XDNDResolvedProperties) - { - X11DRV_XDND_ResolveProperty(event->display, event->window, - time, XDNDTypeList, XDNDTypeListSize); - XDNDResolvedProperties = TRUE; - } - if (!XDNDAccepted || XDNDLastTargetWnd != targetWindow) { /* Notify OLE of DragEnter. Result determines if we accept */ @@ -396,6 +381,10 @@ void X11DRV_XDND_DropEvent( HWND hWnd, XClientMessageEvent *event ) int accept = 0; /* Assume we're not accepting */ BOOL drop_file = TRUE; + XDNDDropSourceWindow = event->data.l[0]; + XDNDDropTargetWindow = event->window; + XDNDTimestamp = event->data.l[2]; + /* Notify OLE of Drop */ if (XDNDAccepted) { @@ -504,117 +493,18 @@ void X11DRV_XDND_LeaveEvent( HWND hWnd, XClientMessageEvent *event ) /************************************************************************** - * X11DRV_XDND_ResolveProperty - * - * Resolve all MIME types to windows clipboard formats. All data is cached. - */ -static void X11DRV_XDND_ResolveProperty(Display *display, Window xwin, Time tm, - Atom *types, unsigned long count) -{ - unsigned int i, j; - BOOL res; - XEvent xe; - XDNDDATA *current, *next; - BOOL haveHDROP = FALSE; - - TRACE("count(%ld)\n", count); - - for (i = 0; i < count; i++) - { - HANDLE contents; - UINT windowsFormat; - - TRACE("requesting atom %ld from xwin %ld\n", types[i], xwin); - - if (types[i] == 0) - continue; - - XConvertSelection(display, x11drv_atom(XdndSelection), types[i], - x11drv_atom(XdndTarget), xwin, tm); - - /* - * Wait for SelectionNotify - */ - for (j = 0; j < SELECTION_RETRIES; j++) - { - res = XCheckTypedWindowEvent(display, xwin, SelectionNotify, &xe); - if (res && xe.xselection.selection == x11drv_atom(XdndSelection)) break; - - usleep(SELECTION_WAIT); - } - - if (xe.xselection.property == None) - continue; - - contents = X11DRV_CLIPBOARD_ImportSelection(display, types[i], xwin, x11drv_atom(XdndTarget), &windowsFormat); - if (contents) - X11DRV_XDND_InsertXDNDData(types[i], windowsFormat, contents); - } - - /* On Windows when there is a CF_HDROP, there are no other CF_ formats. - * foobar2000 relies on this (spaces -> %20's without it). - */ - LIST_FOR_EACH_ENTRY(current, &xdndData, XDNDDATA, entry) - { - if (current->cf_win == CF_HDROP) - { - haveHDROP = TRUE; - break; - } - } - if (haveHDROP) - { - LIST_FOR_EACH_ENTRY_SAFE(current, next, &xdndData, XDNDDATA, entry) - { - if (current->cf_win != CF_HDROP && current->cf_win < CF_MAX) - { - list_remove(¤t->entry); - GlobalFree(current->contents); - HeapFree(GetProcessHeap(), 0, current); - } - } - } -} - - -/************************************************************************** - * X11DRV_XDND_InsertXDNDData - * - * Cache available XDND property - */ -static void X11DRV_XDND_InsertXDNDData(int property, int format, HANDLE contents) -{ - LPXDNDDATA current = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(XDNDDATA)); - - if (current) - { - current->cf_xdnd = property; - current->cf_win = format; - current->contents = contents; - list_add_tail(&xdndData, ¤t->entry); - } -} - - -/************************************************************************** * X11DRV_XDND_HasHDROP */ static BOOL X11DRV_XDND_HasHDROP(void) { - LPXDNDDATA current = NULL; - BOOL found = FALSE; - - /* Find CF_HDROP type if any */ - LIST_FOR_EACH_ENTRY(current, &xdndData, XDNDDATA, entry) + DWORD i; + for (i = 0; i < XDNDTypeListSize; i++) { - if (current->cf_win == CF_HDROP) - { - found = TRUE; - break; - } + UINT windowsFormat = X11DRV_CLIPBOARD_X11AtomToWindowsFormat(XDNDTypeList[i]); + if (windowsFormat == CF_HDROP) + return TRUE; } - - return found; + return FALSE; } /************************************************************************** @@ -623,42 +513,38 @@ static BOOL X11DRV_XDND_HasHDROP(void) static HRESULT X11DRV_XDND_SendDropFiles(HWND hwnd) { HRESULT hr; - LPXDNDDATA current = NULL; - BOOL found = FALSE; - LIST_FOR_EACH_ENTRY(current, &xdndData, XDNDDATA, entry) - { - if (current->cf_win == CF_HDROP) - { - found = TRUE; - break; - } - } - if (found) + if (X11DRV_XDND_HasHDROP()) { - HGLOBAL dropHandle = GlobalAlloc(GMEM_FIXED, GlobalSize(current->contents)); - if (dropHandle) + FORMATETC formatEtc; + STGMEDIUM stgMedium; + + formatEtc.cfFormat = CF_HDROP; + formatEtc.dwAspect = DVASPECT_CONTENT; + formatEtc.ptd = NULL; + formatEtc.tymed = TYMED_HGLOBAL; + + hr = IDataObject_GetData(&XDNDDataObject, &formatEtc, &stgMedium); + if (SUCCEEDED(hr)) { - DROPFILES *lpDrop = GlobalLock(dropHandle); - memcpy(lpDrop, GlobalLock(current->contents), GlobalSize(current->contents)); - GlobalUnlock(current->contents); + DROPFILES *lpDrop = GlobalLock(stgMedium.u.hGlobal); lpDrop->pt.x = XDNDxy.x; lpDrop->pt.y = XDNDxy.y; lpDrop->fNC = !ScreenToClient(hwnd, &lpDrop->pt); TRACE("Sending WM_DROPFILES: hWnd=0x%p, fNC=%d, x=%d, y=%d, files=%p(%s)\n", hwnd, lpDrop->fNC, lpDrop->pt.x, lpDrop->pt.y, ((char*)lpDrop) + lpDrop->pFiles, debugstr_w((WCHAR*)(((char*)lpDrop) + lpDrop->pFiles))); - GlobalUnlock(dropHandle); - if (PostMessageW(hwnd, WM_DROPFILES, (WPARAM)dropHandle, 0)) + GlobalUnlock(stgMedium.u.hGlobal); + if (PostMessageW(hwnd, WM_DROPFILES, (WPARAM)stgMedium.u.hGlobal, 0)) hr = S_OK; else { hr = HRESULT_FROM_WIN32(GetLastError()); - GlobalFree(dropHandle); + GlobalFree(stgMedium.u.hGlobal); } } else - hr = HRESULT_FROM_WIN32(GetLastError()); + ERR("fetching CF_HDROP data for WM_DROPFILES failed, hr=0x%08x\n", hr); } else hr = E_FAIL; @@ -672,23 +558,11 @@ static HRESULT X11DRV_XDND_SendDropFiles(HWND hwnd) */ static void X11DRV_XDND_FreeDragDropOp(void) { - LPXDNDDATA next; - LPXDNDDATA current; - TRACE("\n"); - /** Free data cache */ - LIST_FOR_EACH_ENTRY_SAFE(current, next, &xdndData, XDNDDATA, entry) - { - list_remove(¤t->entry); - GlobalFree(current->contents); - HeapFree(GetProcessHeap(), 0, current); - } - HeapFree(GetProcessHeap(), 0, XDNDTypeList); XDNDTypeList = NULL; XDNDTypeListSize = 0; - XDNDResolvedProperties = FALSE; XDNDxy.x = XDNDxy.y = 0; XDNDLastTargetWnd = NULL; XDNDLastDropTargetWnd = NULL; @@ -782,20 +656,46 @@ static HRESULT WINAPI XDNDDATAOBJECT_GetData(IDataObject *dataObject, hr = IDataObject_QueryGetData(dataObject, formatEtc); if (SUCCEEDED(hr)) { - XDNDDATA *current; - LIST_FOR_EACH_ENTRY(current, &xdndData, XDNDDATA, entry) + XEvent xe; + DWORD i; + + for (i = 0; i < XDNDTypeListSize; i++) { - if (current->cf_win == formatEtc->cfFormat) + UINT windowsFormat = X11DRV_CLIPBOARD_X11AtomToWindowsFormat(XDNDTypeList[i]); + if (windowsFormat != 0 && windowsFormat == formatEtc->cfFormat) { - pMedium->tymed = TYMED_HGLOBAL; - pMedium->u.hGlobal = GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, GlobalSize(current->contents)); - if (pMedium->u.hGlobal == NULL) + int j; + + XConvertSelection(thread_display(), x11drv_atom(XdndSelection), XDNDTypeList[i], + x11drv_atom(XdndTarget), XDNDDropTargetWindow, XDNDTimestamp); + + /* + * Wait for SelectionNotify + */ + for (j = 0; j < SELECTION_RETRIES; j++) + { + Bool res = XCheckTypedWindowEvent(thread_display(), XDNDDropTargetWindow, SelectionNotify, &xe); + if (res && xe.xselection.selection == x11drv_atom(XdndSelection)) break; + + usleep(SELECTION_WAIT); + } + + if (xe.xselection.property == None) + { + ERR("No SelectionNotify event received\n"); + return E_FAIL; + } + + pMedium->u.hGlobal = X11DRV_CLIPBOARD_ImportSelection(thread_display(), XDNDTypeList[i], + XDNDDropTargetWindow, x11drv_atom(XdndTarget), &windowsFormat); + if (pMedium->u.hGlobal) + { + pMedium->tymed = TYMED_HGLOBAL; + pMedium->pUnkForRelease = 0; + return S_OK; + } + else return E_OUTOFMEMORY; - memcpy(GlobalLock(pMedium->u.hGlobal), GlobalLock(current->contents), GlobalSize(current->contents)); - GlobalUnlock(pMedium->u.hGlobal); - GlobalUnlock(current->contents); - pMedium->pUnkForRelease = 0; - return S_OK; } } } @@ -814,7 +714,7 @@ static HRESULT WINAPI XDNDDATAOBJECT_QueryGetData(IDataObject *dataObject, FORMATETC *formatEtc) { char formatDesc[1024]; - XDNDDATA *current; + DWORD i; TRACE("(%p, %p={.tymed=0x%x, .dwAspect=%d, .cfFormat=%d}\n", dataObject, formatEtc, formatEtc->tymed, formatEtc->dwAspect, formatEtc->cfFormat); @@ -831,9 +731,10 @@ static HRESULT WINAPI XDNDDATAOBJECT_QueryGetData(IDataObject *dataObject, return E_NOTIMPL; } - LIST_FOR_EACH_ENTRY(current, &xdndData, XDNDDATA, entry) + for (i = 0; i < XDNDTypeListSize; i++) { - if (current->cf_win == formatEtc->cfFormat) + UINT windowsFormat = X11DRV_CLIPBOARD_X11AtomToWindowsFormat(XDNDTypeList[i]); + if (windowsFormat != 0 && windowsFormat == formatEtc->cfFormat) { TRACE("application found %s\n", formatDesc); return S_OK; @@ -865,7 +766,8 @@ static HRESULT WINAPI XDNDDATAOBJECT_EnumFormatEtc(IDataObject *dataObject, DWORD dwDirection, IEnumFORMATETC **ppEnumFormatEtc) { - DWORD count; + DWORD count = 0; + DWORD i; FORMATETC *formats; TRACE("(%p, %u, %p)\n", dataObject, dwDirection, ppEnumFormatEtc); @@ -876,21 +778,28 @@ static HRESULT WINAPI XDNDDATAOBJECT_EnumFormatEtc(IDataObject *dataObject, return E_NOTIMPL; } - count = list_count(&xdndData); + for (i = 0; i < XDNDTypeListSize; i++) + { + UINT windowsFormat = X11DRV_CLIPBOARD_X11AtomToWindowsFormat(XDNDTypeList[i]); + if (windowsFormat != 0) + ++count; + } formats = HeapAlloc(GetProcessHeap(), 0, count * sizeof(FORMATETC)); if (formats) { - XDNDDATA *current; - DWORD i = 0; + DWORD next = 0; HRESULT hr; - LIST_FOR_EACH_ENTRY(current, &xdndData, XDNDDATA, entry) + for (i = 0; i < XDNDTypeListSize; i++) { - formats[i].cfFormat = current->cf_win; - formats[i].ptd = NULL; - formats[i].dwAspect = DVASPECT_CONTENT; - formats[i].lindex = -1; - formats[i].tymed = TYMED_HGLOBAL; - i++; + UINT windowsFormat = X11DRV_CLIPBOARD_X11AtomToWindowsFormat(XDNDTypeList[i]); + if (windowsFormat == 0) + continue; + formats[next].cfFormat = windowsFormat; + formats[next].ptd = NULL; + formats[next].dwAspect = DVASPECT_CONTENT; + formats[next].lindex = -1; + formats[next].tymed = TYMED_HGLOBAL; + next++; } hr = SHCreateStdEnumFmtEtc(count, formats, ppEnumFormatEtc); HeapFree(GetProcessHeap(), 0, formats);