4 * Copyright 2003 Ulrich Czekalla
5 * Copyright 2007 Damjan Jovanovic
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include "x11drv_dll.h"
27 #include "wine/debug.h"
28 #include "wine/list.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(xdnd
);
32 static struct format_entry
*xdnd_formats
, *xdnd_formats_end
;
33 static POINT XDNDxy
= { 0, 0 };
34 static IDataObject XDNDDataObject
;
35 static BOOL XDNDAccepted
= FALSE
;
36 static DWORD XDNDDropEffect
= DROPEFFECT_NONE
;
37 /* the last window the mouse was over */
38 static HWND XDNDLastTargetWnd
;
39 /* might be an ancestor of XDNDLastTargetWnd */
40 static HWND XDNDLastDropTargetWnd
;
42 static BOOL
X11DRV_XDND_HasHDROP(void);
43 static HRESULT
X11DRV_XDND_SendDropFiles(HWND hwnd
);
44 static void X11DRV_XDND_FreeDragDropOp(void);
46 static CRITICAL_SECTION xdnd_cs
;
47 static CRITICAL_SECTION_DEBUG critsect_debug
=
50 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
51 0, 0, { (DWORD_PTR
)(__FILE__
": xdnd_cs") }
53 static CRITICAL_SECTION xdnd_cs
= { &critsect_debug
, -1, 0, 0, 0, 0 };
56 static struct format_entry
*next_format( struct format_entry
*entry
)
58 return (struct format_entry
*)&entry
->data
[(entry
->size
+ 7) & ~7];
62 /* Based on functions in dlls/ole32/ole2.c */
63 static HANDLE
get_droptarget_local_handle(HWND hwnd
)
65 static const WCHAR prop_marshalleddroptarget
[] =
66 {'W','i','n','e','M','a','r','s','h','a','l','l','e','d','D','r','o','p','T','a','r','g','e','t',0};
68 HANDLE local_handle
= 0;
70 handle
= GetPropW(hwnd
, prop_marshalleddroptarget
);
76 GetWindowThreadProcessId(hwnd
, &pid
);
77 process
= OpenProcess(PROCESS_DUP_HANDLE
, FALSE
, pid
);
80 DuplicateHandle(process
, handle
, GetCurrentProcess(), &local_handle
, 0, FALSE
, DUPLICATE_SAME_ACCESS
);
87 static HRESULT
create_stream_from_map(HANDLE map
, IStream
**stream
)
89 HRESULT hr
= E_OUTOFMEMORY
;
92 MEMORY_BASIC_INFORMATION info
;
94 data
= MapViewOfFile(map
, FILE_MAP_READ
, 0, 0, 0);
97 VirtualQuery(data
, &info
, sizeof(info
));
98 TRACE("size %d\n", (int)info
.RegionSize
);
100 hmem
= GlobalAlloc(GMEM_MOVEABLE
, info
.RegionSize
);
103 memcpy(GlobalLock(hmem
), data
, info
.RegionSize
);
105 hr
= CreateStreamOnHGlobal(hmem
, TRUE
, stream
);
107 UnmapViewOfFile(data
);
111 static IDropTarget
* get_droptarget_pointer(HWND hwnd
)
113 IDropTarget
*droptarget
= NULL
;
117 map
= get_droptarget_local_handle(hwnd
);
118 if(!map
) return NULL
;
120 if(SUCCEEDED(create_stream_from_map(map
, &stream
)))
122 CoUnmarshalInterface(stream
, &IID_IDropTarget
, (void**)&droptarget
);
123 IStream_Release(stream
);
130 /* Recursively searches for a window on given coordinates in a drag&drop specific manner.
132 * Don't use WindowFromPoint instead, because it omits the STATIC and transparent
133 * windows, but they can be a valid drop targets if have WS_EX_ACCEPTFILES set.
135 static HWND
window_from_point_dnd(HWND hwnd
, POINT point
)
138 ScreenToClient(hwnd
, &point
);
139 while ((child
= ChildWindowFromPointEx(hwnd
, point
, CWP_SKIPDISABLED
| CWP_SKIPINVISIBLE
)) && child
!= hwnd
)
141 MapWindowPoints(hwnd
, child
, &point
, 1);
148 /* Returns the first window down the hierarchy that has WS_EX_ACCEPTFILES set or
149 * returns NULL, if such window does not exists.
151 static HWND
window_accepting_files(HWND hwnd
)
153 while (hwnd
&& !(GetWindowLongW(hwnd
, GWL_EXSTYLE
) & WS_EX_ACCEPTFILES
))
154 /* MUST to be GetParent, not GetAncestor, because the owner window
155 * (with WS_EX_ACCEPTFILES) of a window with WS_POPUP is a valid
156 * drop target. GetParent works exactly this way!
158 hwnd
= GetParent(hwnd
);
162 /**************************************************************************
163 * x11drv_dnd_position_event
165 * Handle an XdndPosition event.
167 NTSTATUS WINAPI
x11drv_dnd_position_event( void *arg
, ULONG size
)
169 struct dnd_position_event_params
*params
= arg
;
170 int accept
= 0; /* Assume we're not accepting */
171 IDropTarget
*dropTarget
= NULL
;
172 DWORD effect
= params
->effect
;
173 POINTL pointl
= { .x
= params
->point
.x
, .y
= params
->point
.y
};
177 XDNDxy
= params
->point
;
178 targetWindow
= window_from_point_dnd( UlongToHandle( params
->hwnd
), XDNDxy
);
180 if (!XDNDAccepted
|| XDNDLastTargetWnd
!= targetWindow
)
182 /* Notify OLE of DragEnter. Result determines if we accept */
183 HWND dropTargetWindow
;
185 if (XDNDAccepted
&& XDNDLastDropTargetWnd
)
187 dropTarget
= get_droptarget_pointer(XDNDLastDropTargetWnd
);
190 hr
= IDropTarget_DragLeave(dropTarget
);
192 WARN("IDropTarget_DragLeave failed, error 0x%08lx\n", hr
);
193 IDropTarget_Release(dropTarget
);
196 dropTargetWindow
= targetWindow
;
199 dropTarget
= get_droptarget_pointer(dropTargetWindow
);
200 } while (dropTarget
== NULL
&& (dropTargetWindow
= GetParent(dropTargetWindow
)) != NULL
);
201 XDNDLastTargetWnd
= targetWindow
;
202 XDNDLastDropTargetWnd
= dropTargetWindow
;
205 DWORD effect_ignore
= effect
;
206 hr
= IDropTarget_DragEnter(dropTarget
, &XDNDDataObject
,
207 MK_LBUTTON
, pointl
, &effect_ignore
);
211 TRACE("the application accepted the drop (effect = %ld)\n", effect_ignore
);
215 XDNDAccepted
= FALSE
;
216 WARN("IDropTarget_DragEnter failed, error 0x%08lx\n", hr
);
218 IDropTarget_Release(dropTarget
);
221 if (XDNDAccepted
&& XDNDLastTargetWnd
== targetWindow
)
223 /* If drag accepted notify OLE of DragOver */
224 dropTarget
= get_droptarget_pointer(XDNDLastDropTargetWnd
);
227 hr
= IDropTarget_DragOver(dropTarget
, MK_LBUTTON
, pointl
, &effect
);
229 XDNDDropEffect
= effect
;
231 WARN("IDropTarget_DragOver failed, error 0x%08lx\n", hr
);
232 IDropTarget_Release(dropTarget
);
240 /* fallback search for window able to accept these files. */
242 if (window_accepting_files(targetWindow
) && X11DRV_XDND_HasHDROP())
245 effect
= DROPEFFECT_COPY
;
249 return accept
? effect
: 0;
252 NTSTATUS
x11drv_dnd_drop_event( UINT arg
)
254 IDropTarget
*dropTarget
;
255 DWORD effect
= XDNDDropEffect
;
256 int accept
= 0; /* Assume we're not accepting */
257 BOOL drop_file
= TRUE
;
259 /* Notify OLE of Drop */
262 dropTarget
= get_droptarget_pointer(XDNDLastDropTargetWnd
);
263 if (dropTarget
&& effect
!=DROPEFFECT_NONE
)
270 hr
= IDropTarget_Drop(dropTarget
, &XDNDDataObject
, MK_LBUTTON
,
274 if (effect
!= DROPEFFECT_NONE
)
276 TRACE("drop succeeded\n");
281 TRACE("the application refused the drop\n");
284 WARN("drop failed, error 0x%08lx\n", hr
);
287 WARN("drop returned 0x%08lx\n", hr
);
290 IDropTarget_Release(dropTarget
);
294 HRESULT hr
= IDropTarget_DragLeave(dropTarget
);
296 WARN("IDropTarget_DragLeave failed, error 0x%08lx\n", hr
);
297 IDropTarget_Release(dropTarget
);
303 /* Only send WM_DROPFILES if Drop didn't succeed or DROPEFFECT_NONE was set.
304 * Doing both causes winamp to duplicate the dropped files (#29081) */
306 HWND hwnd_drop
= window_accepting_files(window_from_point_dnd( UlongToHandle(arg
), XDNDxy
));
308 if (hwnd_drop
&& X11DRV_XDND_HasHDROP())
310 HRESULT hr
= X11DRV_XDND_SendDropFiles(hwnd_drop
);
314 effect
= DROPEFFECT_COPY
;
319 TRACE("effectRequested(0x%lx) accept(%d) performed(0x%lx) at x(%ld),y(%ld)\n",
320 XDNDDropEffect
, accept
, effect
, XDNDxy
.x
, XDNDxy
.y
);
322 return accept
? effect
: 0;
325 /**************************************************************************
326 * x11drv_dnd_leave_event
328 * Handle an XdndLeave event.
330 NTSTATUS
x11drv_dnd_leave_event( UINT arg
)
332 IDropTarget
*dropTarget
;
334 TRACE("DND Operation canceled\n");
336 /* Notify OLE of DragLeave */
339 dropTarget
= get_droptarget_pointer(XDNDLastDropTargetWnd
);
342 HRESULT hr
= IDropTarget_DragLeave(dropTarget
);
344 WARN("IDropTarget_DragLeave failed, error 0x%08lx\n", hr
);
345 IDropTarget_Release(dropTarget
);
349 X11DRV_XDND_FreeDragDropOp();
354 /**************************************************************************
355 * x11drv_dnd_enter_event
357 NTSTATUS WINAPI
x11drv_dnd_enter_event( void *params
, ULONG size
)
359 struct format_entry
*formats
= params
;
360 XDNDAccepted
= FALSE
;
361 X11DRV_XDND_FreeDragDropOp(); /* Clear previously cached data */
363 if ((xdnd_formats
= HeapAlloc( GetProcessHeap(), 0, size
)))
365 memcpy( xdnd_formats
, formats
, size
);
366 xdnd_formats_end
= (struct format_entry
*)((char *)xdnd_formats
+ size
);
372 /**************************************************************************
373 * X11DRV_XDND_HasHDROP
375 static BOOL
X11DRV_XDND_HasHDROP(void)
377 struct format_entry
*iter
;
380 EnterCriticalSection(&xdnd_cs
);
382 /* Find CF_HDROP type if any */
383 for (iter
= xdnd_formats
; iter
< xdnd_formats_end
; iter
= next_format( iter
))
385 if (iter
->format
== CF_HDROP
)
392 LeaveCriticalSection(&xdnd_cs
);
397 /**************************************************************************
398 * X11DRV_XDND_SendDropFiles
400 static HRESULT
X11DRV_XDND_SendDropFiles(HWND hwnd
)
402 struct format_entry
*iter
;
406 EnterCriticalSection(&xdnd_cs
);
408 for (iter
= xdnd_formats
; iter
< xdnd_formats_end
; iter
= next_format( iter
))
410 if (iter
->format
== CF_HDROP
)
418 HGLOBAL dropHandle
= GlobalAlloc(GMEM_FIXED
, iter
->size
);
422 DROPFILES
*lpDrop
= GlobalLock(dropHandle
);
423 memcpy(lpDrop
, iter
->data
, iter
->size
);
424 lpDrop
->pt
.x
= XDNDxy
.x
;
425 lpDrop
->pt
.y
= XDNDxy
.y
;
426 lpDrop
->fNC
= !(ScreenToClient(hwnd
, &lpDrop
->pt
) &&
427 GetClientRect(hwnd
, &rect
) &&
428 PtInRect(&rect
, lpDrop
->pt
));
429 TRACE("Sending WM_DROPFILES: hWnd=0x%p, fNC=%d, x=%ld, y=%ld, files=%p(%s)\n", hwnd
,
430 lpDrop
->fNC
, lpDrop
->pt
.x
, lpDrop
->pt
.y
, ((char*)lpDrop
) + lpDrop
->pFiles
,
431 debugstr_w((WCHAR
*)(((char*)lpDrop
) + lpDrop
->pFiles
)));
432 GlobalUnlock(dropHandle
);
433 if (PostMessageW(hwnd
, WM_DROPFILES
, (WPARAM
)dropHandle
, 0))
437 hr
= HRESULT_FROM_WIN32(GetLastError());
438 GlobalFree(dropHandle
);
442 hr
= HRESULT_FROM_WIN32(GetLastError());
447 LeaveCriticalSection(&xdnd_cs
);
453 /**************************************************************************
454 * X11DRV_XDND_FreeDragDropOp
456 static void X11DRV_XDND_FreeDragDropOp(void)
460 EnterCriticalSection(&xdnd_cs
);
462 HeapFree( GetProcessHeap(), 0, xdnd_formats
);
463 xdnd_formats
= xdnd_formats_end
= NULL
;
465 XDNDxy
.x
= XDNDxy
.y
= 0;
466 XDNDLastTargetWnd
= NULL
;
467 XDNDLastDropTargetWnd
= NULL
;
468 XDNDAccepted
= FALSE
;
470 LeaveCriticalSection(&xdnd_cs
);
474 /**************************************************************************
475 * X11DRV_XDND_DescribeClipboardFormat
477 static void X11DRV_XDND_DescribeClipboardFormat(int cfFormat
, char *buffer
, int size
)
479 #define D(x) case x: lstrcpynA(buffer, #x, size); return;
502 if (CF_PRIVATEFIRST
<= cfFormat
&& cfFormat
<= CF_PRIVATELAST
)
504 lstrcpynA(buffer
, "some private object", size
);
507 if (CF_GDIOBJFIRST
<= cfFormat
&& cfFormat
<= CF_GDIOBJLAST
)
509 lstrcpynA(buffer
, "some GDI object", size
);
513 GetClipboardFormatNameA(cfFormat
, buffer
, size
);
517 /* The IDataObject singleton we feed to OLE follows */
519 static HRESULT WINAPI
XDNDDATAOBJECT_QueryInterface(IDataObject
*dataObject
,
520 REFIID riid
, void **ppvObject
)
522 TRACE("(%p, %s, %p)\n", dataObject
, debugstr_guid(riid
), ppvObject
);
523 if (IsEqualIID(riid
, &IID_IUnknown
) || IsEqualIID(riid
, &IID_IDataObject
))
525 *ppvObject
= dataObject
;
526 IDataObject_AddRef(dataObject
);
530 return E_NOINTERFACE
;
533 static ULONG WINAPI
XDNDDATAOBJECT_AddRef(IDataObject
*dataObject
)
535 TRACE("(%p)\n", dataObject
);
539 static ULONG WINAPI
XDNDDATAOBJECT_Release(IDataObject
*dataObject
)
541 TRACE("(%p)\n", dataObject
);
545 static HRESULT WINAPI
XDNDDATAOBJECT_GetData(IDataObject
*dataObject
,
546 FORMATETC
*formatEtc
,
550 char formatDesc
[1024];
552 TRACE("(%p, %p, %p)\n", dataObject
, formatEtc
, pMedium
);
553 X11DRV_XDND_DescribeClipboardFormat(formatEtc
->cfFormat
,
554 formatDesc
, sizeof(formatDesc
));
555 TRACE("application is looking for %s\n", formatDesc
);
557 hr
= IDataObject_QueryGetData(dataObject
, formatEtc
);
560 struct format_entry
*iter
;
562 for (iter
= xdnd_formats
; iter
< xdnd_formats_end
; iter
= next_format( iter
))
564 if (iter
->format
== formatEtc
->cfFormat
)
566 pMedium
->tymed
= TYMED_HGLOBAL
;
567 pMedium
->hGlobal
= GlobalAlloc(GMEM_FIXED
| GMEM_ZEROINIT
, iter
->size
);
568 if (pMedium
->hGlobal
== NULL
)
569 return E_OUTOFMEMORY
;
570 memcpy(GlobalLock(pMedium
->hGlobal
), iter
->data
, iter
->size
);
571 GlobalUnlock(pMedium
->hGlobal
);
572 pMedium
->pUnkForRelease
= 0;
580 static HRESULT WINAPI
XDNDDATAOBJECT_GetDataHere(IDataObject
*dataObject
,
581 FORMATETC
*formatEtc
,
584 FIXME("(%p, %p, %p): stub\n", dataObject
, formatEtc
, pMedium
);
585 return DATA_E_FORMATETC
;
588 static HRESULT WINAPI
XDNDDATAOBJECT_QueryGetData(IDataObject
*dataObject
,
589 FORMATETC
*formatEtc
)
591 struct format_entry
*iter
;
592 char formatDesc
[1024];
594 TRACE("(%p, %p={.tymed=0x%lx, .dwAspect=%ld, .cfFormat=%d}\n",
595 dataObject
, formatEtc
, formatEtc
->tymed
, formatEtc
->dwAspect
, formatEtc
->cfFormat
);
596 X11DRV_XDND_DescribeClipboardFormat(formatEtc
->cfFormat
, formatDesc
, sizeof(formatDesc
));
598 if (formatEtc
->tymed
&& !(formatEtc
->tymed
& TYMED_HGLOBAL
))
600 FIXME("only HGLOBAL medium types supported right now\n");
603 /* Windows Explorer ignores .dwAspect and .lindex for CF_HDROP,
604 * and we have no way to implement them on XDnD anyway, so ignore them too.
607 for (iter
= xdnd_formats
; iter
< xdnd_formats_end
; iter
= next_format( iter
))
609 if (iter
->format
== formatEtc
->cfFormat
)
611 TRACE("application found %s\n", formatDesc
);
615 TRACE("application didn't find %s\n", formatDesc
);
616 return DV_E_FORMATETC
;
619 static HRESULT WINAPI
XDNDDATAOBJECT_GetCanonicalFormatEtc(IDataObject
*dataObject
,
620 FORMATETC
*formatEtc
,
621 FORMATETC
*formatEtcOut
)
623 FIXME("(%p, %p, %p): stub\n", dataObject
, formatEtc
, formatEtcOut
);
624 formatEtcOut
->ptd
= NULL
;
628 static HRESULT WINAPI
XDNDDATAOBJECT_SetData(IDataObject
*dataObject
,
629 FORMATETC
*formatEtc
,
630 STGMEDIUM
*pMedium
, BOOL fRelease
)
632 FIXME("(%p, %p, %p, %s): stub\n", dataObject
, formatEtc
,
633 pMedium
, fRelease
?"TRUE":"FALSE");
637 static HRESULT WINAPI
XDNDDATAOBJECT_EnumFormatEtc(IDataObject
*dataObject
,
639 IEnumFORMATETC
**ppEnumFormatEtc
)
641 struct format_entry
*iter
;
645 TRACE("(%p, %lu, %p)\n", dataObject
, dwDirection
, ppEnumFormatEtc
);
647 if (dwDirection
!= DATADIR_GET
)
649 FIXME("only the get direction is implemented\n");
653 for (iter
= xdnd_formats
; iter
< xdnd_formats_end
; iter
= next_format( iter
)) count
++;
655 formats
= HeapAlloc(GetProcessHeap(), 0, count
* sizeof(FORMATETC
));
660 for (iter
= xdnd_formats
; iter
< xdnd_formats_end
; iter
= next_format( iter
))
662 formats
[i
].cfFormat
= iter
->format
;
663 formats
[i
].ptd
= NULL
;
664 formats
[i
].dwAspect
= DVASPECT_CONTENT
;
665 formats
[i
].lindex
= -1;
666 formats
[i
].tymed
= TYMED_HGLOBAL
;
669 hr
= SHCreateStdEnumFmtEtc(count
, formats
, ppEnumFormatEtc
);
670 HeapFree(GetProcessHeap(), 0, formats
);
674 return E_OUTOFMEMORY
;
677 static HRESULT WINAPI
XDNDDATAOBJECT_DAdvise(IDataObject
*dataObject
,
678 FORMATETC
*formatEtc
, DWORD advf
,
679 IAdviseSink
*adviseSink
,
680 DWORD
*pdwConnection
)
682 FIXME("(%p, %p, %lu, %p, %p): stub\n", dataObject
, formatEtc
, advf
,
683 adviseSink
, pdwConnection
);
684 return OLE_E_ADVISENOTSUPPORTED
;
687 static HRESULT WINAPI
XDNDDATAOBJECT_DUnadvise(IDataObject
*dataObject
,
690 FIXME("(%p, %lu): stub\n", dataObject
, dwConnection
);
691 return OLE_E_ADVISENOTSUPPORTED
;
694 static HRESULT WINAPI
XDNDDATAOBJECT_EnumDAdvise(IDataObject
*dataObject
,
695 IEnumSTATDATA
**pEnumAdvise
)
697 FIXME("(%p, %p): stub\n", dataObject
, pEnumAdvise
);
698 return OLE_E_ADVISENOTSUPPORTED
;
701 static IDataObjectVtbl xdndDataObjectVtbl
=
703 XDNDDATAOBJECT_QueryInterface
,
704 XDNDDATAOBJECT_AddRef
,
705 XDNDDATAOBJECT_Release
,
706 XDNDDATAOBJECT_GetData
,
707 XDNDDATAOBJECT_GetDataHere
,
708 XDNDDATAOBJECT_QueryGetData
,
709 XDNDDATAOBJECT_GetCanonicalFormatEtc
,
710 XDNDDATAOBJECT_SetData
,
711 XDNDDATAOBJECT_EnumFormatEtc
,
712 XDNDDATAOBJECT_DAdvise
,
713 XDNDDATAOBJECT_DUnadvise
,
714 XDNDDATAOBJECT_EnumDAdvise
717 static IDataObject XDNDDataObject
= { &xdndDataObjectVtbl
};
719 NTSTATUS WINAPI
x11drv_dnd_post_drop( void *data
, ULONG size
)
723 if ((handle
= GlobalAlloc( GMEM_SHARE
, size
)))
725 DROPFILES
*ptr
= GlobalLock( handle
);
727 memcpy( ptr
, data
, size
);
728 hwnd
= UlongToHandle( ptr
->fWide
);
730 GlobalUnlock( handle
);
731 PostMessageW( hwnd
, WM_DROPFILES
, (WPARAM
)handle
, 0 );