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 #define NONAMELESSUNION
25 #include "x11drv_dll.h"
29 #include "wine/debug.h"
30 #include "wine/list.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(xdnd
);
34 static struct format_entry
*xdnd_formats
, *xdnd_formats_end
;
35 static POINT XDNDxy
= { 0, 0 };
36 static IDataObject XDNDDataObject
;
37 static BOOL XDNDAccepted
= FALSE
;
38 static DWORD XDNDDropEffect
= DROPEFFECT_NONE
;
39 /* the last window the mouse was over */
40 static HWND XDNDLastTargetWnd
;
41 /* might be an ancestor of XDNDLastTargetWnd */
42 static HWND XDNDLastDropTargetWnd
;
44 static BOOL
X11DRV_XDND_HasHDROP(void);
45 static HRESULT
X11DRV_XDND_SendDropFiles(HWND hwnd
);
46 static void X11DRV_XDND_FreeDragDropOp(void);
48 static CRITICAL_SECTION xdnd_cs
;
49 static CRITICAL_SECTION_DEBUG critsect_debug
=
52 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
53 0, 0, { (DWORD_PTR
)(__FILE__
": xdnd_cs") }
55 static CRITICAL_SECTION xdnd_cs
= { &critsect_debug
, -1, 0, 0, 0, 0 };
58 static struct format_entry
*next_format( struct format_entry
*entry
)
60 return (struct format_entry
*)&entry
->data
[(entry
->size
+ 7) & ~7];
64 /* Based on functions in dlls/ole32/ole2.c */
65 static HANDLE
get_droptarget_local_handle(HWND hwnd
)
67 static const WCHAR prop_marshalleddroptarget
[] =
68 {'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};
70 HANDLE local_handle
= 0;
72 handle
= GetPropW(hwnd
, prop_marshalleddroptarget
);
78 GetWindowThreadProcessId(hwnd
, &pid
);
79 process
= OpenProcess(PROCESS_DUP_HANDLE
, FALSE
, pid
);
82 DuplicateHandle(process
, handle
, GetCurrentProcess(), &local_handle
, 0, FALSE
, DUPLICATE_SAME_ACCESS
);
89 static HRESULT
create_stream_from_map(HANDLE map
, IStream
**stream
)
91 HRESULT hr
= E_OUTOFMEMORY
;
94 MEMORY_BASIC_INFORMATION info
;
96 data
= MapViewOfFile(map
, FILE_MAP_READ
, 0, 0, 0);
99 VirtualQuery(data
, &info
, sizeof(info
));
100 TRACE("size %d\n", (int)info
.RegionSize
);
102 hmem
= GlobalAlloc(GMEM_MOVEABLE
, info
.RegionSize
);
105 memcpy(GlobalLock(hmem
), data
, info
.RegionSize
);
107 hr
= CreateStreamOnHGlobal(hmem
, TRUE
, stream
);
109 UnmapViewOfFile(data
);
113 static IDropTarget
* get_droptarget_pointer(HWND hwnd
)
115 IDropTarget
*droptarget
= NULL
;
119 map
= get_droptarget_local_handle(hwnd
);
120 if(!map
) return NULL
;
122 if(SUCCEEDED(create_stream_from_map(map
, &stream
)))
124 CoUnmarshalInterface(stream
, &IID_IDropTarget
, (void**)&droptarget
);
125 IStream_Release(stream
);
132 /* Recursively searches for a window on given coordinates in a drag&drop specific manner.
134 * Don't use WindowFromPoint instead, because it omits the STATIC and transparent
135 * windows, but they can be a valid drop targets if have WS_EX_ACCEPTFILES set.
137 static HWND
window_from_point_dnd(HWND hwnd
, POINT point
)
140 ScreenToClient(hwnd
, &point
);
141 while ((child
= ChildWindowFromPointEx(hwnd
, point
, CWP_SKIPDISABLED
| CWP_SKIPINVISIBLE
)) && child
!= hwnd
)
143 MapWindowPoints(hwnd
, child
, &point
, 1);
150 /* Returns the first window down the hierarchy that has WS_EX_ACCEPTFILES set or
151 * returns NULL, if such window does not exists.
153 static HWND
window_accepting_files(HWND hwnd
)
155 while (hwnd
&& !(GetWindowLongW(hwnd
, GWL_EXSTYLE
) & WS_EX_ACCEPTFILES
))
156 /* MUST to be GetParent, not GetAncestor, because the owner window
157 * (with WS_EX_ACCEPTFILES) of a window with WS_POPUP is a valid
158 * drop target. GetParent works exactly this way!
160 hwnd
= GetParent(hwnd
);
164 /**************************************************************************
165 * x11drv_dnd_position_event
167 * Handle an XdndPosition event.
169 NTSTATUS WINAPI
x11drv_dnd_position_event( void *arg
, ULONG size
)
171 struct dnd_position_event_params
*params
= arg
;
172 int accept
= 0; /* Assume we're not accepting */
173 IDropTarget
*dropTarget
= NULL
;
174 DWORD effect
= params
->effect
;
175 POINTL pointl
= { .x
= params
->point
.x
, .y
= params
->point
.y
};
179 XDNDxy
= params
->point
;
180 targetWindow
= window_from_point_dnd( UlongToHandle( params
->hwnd
), XDNDxy
);
182 if (!XDNDAccepted
|| XDNDLastTargetWnd
!= targetWindow
)
184 /* Notify OLE of DragEnter. Result determines if we accept */
185 HWND dropTargetWindow
;
187 if (XDNDAccepted
&& XDNDLastDropTargetWnd
)
189 dropTarget
= get_droptarget_pointer(XDNDLastDropTargetWnd
);
192 hr
= IDropTarget_DragLeave(dropTarget
);
194 WARN("IDropTarget_DragLeave failed, error 0x%08X\n", hr
);
195 IDropTarget_Release(dropTarget
);
198 dropTargetWindow
= targetWindow
;
201 dropTarget
= get_droptarget_pointer(dropTargetWindow
);
202 } while (dropTarget
== NULL
&& (dropTargetWindow
= GetParent(dropTargetWindow
)) != NULL
);
203 XDNDLastTargetWnd
= targetWindow
;
204 XDNDLastDropTargetWnd
= dropTargetWindow
;
207 DWORD effect_ignore
= effect
;
208 hr
= IDropTarget_DragEnter(dropTarget
, &XDNDDataObject
,
209 MK_LBUTTON
, pointl
, &effect_ignore
);
213 TRACE("the application accepted the drop (effect = %d)\n", effect_ignore
);
217 XDNDAccepted
= FALSE
;
218 WARN("IDropTarget_DragEnter failed, error 0x%08X\n", hr
);
220 IDropTarget_Release(dropTarget
);
223 if (XDNDAccepted
&& XDNDLastTargetWnd
== targetWindow
)
225 /* If drag accepted notify OLE of DragOver */
226 dropTarget
= get_droptarget_pointer(XDNDLastDropTargetWnd
);
229 hr
= IDropTarget_DragOver(dropTarget
, MK_LBUTTON
, pointl
, &effect
);
231 XDNDDropEffect
= effect
;
233 WARN("IDropTarget_DragOver failed, error 0x%08X\n", hr
);
234 IDropTarget_Release(dropTarget
);
242 /* fallback search for window able to accept these files. */
244 if (window_accepting_files(targetWindow
) && X11DRV_XDND_HasHDROP())
247 effect
= DROPEFFECT_COPY
;
251 return accept
? effect
: 0;
254 NTSTATUS
x11drv_dnd_drop_event( UINT arg
)
256 IDropTarget
*dropTarget
;
257 DWORD effect
= XDNDDropEffect
;
258 int accept
= 0; /* Assume we're not accepting */
259 BOOL drop_file
= TRUE
;
261 /* Notify OLE of Drop */
264 dropTarget
= get_droptarget_pointer(XDNDLastDropTargetWnd
);
265 if (dropTarget
&& effect
!=DROPEFFECT_NONE
)
272 hr
= IDropTarget_Drop(dropTarget
, &XDNDDataObject
, MK_LBUTTON
,
276 if (effect
!= DROPEFFECT_NONE
)
278 TRACE("drop succeeded\n");
283 TRACE("the application refused the drop\n");
286 WARN("drop failed, error 0x%08X\n", hr
);
289 WARN("drop returned 0x%08X\n", hr
);
292 IDropTarget_Release(dropTarget
);
296 HRESULT hr
= IDropTarget_DragLeave(dropTarget
);
298 WARN("IDropTarget_DragLeave failed, error 0x%08X\n", hr
);
299 IDropTarget_Release(dropTarget
);
305 /* Only send WM_DROPFILES if Drop didn't succeed or DROPEFFECT_NONE was set.
306 * Doing both causes winamp to duplicate the dropped files (#29081) */
308 HWND hwnd_drop
= window_accepting_files(window_from_point_dnd( UlongToHandle(arg
), XDNDxy
));
310 if (hwnd_drop
&& X11DRV_XDND_HasHDROP())
312 HRESULT hr
= X11DRV_XDND_SendDropFiles(hwnd_drop
);
316 effect
= DROPEFFECT_COPY
;
321 TRACE("effectRequested(0x%x) accept(%d) performed(0x%x) at x(%d),y(%d)\n",
322 XDNDDropEffect
, accept
, effect
, XDNDxy
.x
, XDNDxy
.y
);
324 return accept
? effect
: 0;
327 /**************************************************************************
328 * x11drv_dnd_leave_event
330 * Handle an XdndLeave event.
332 NTSTATUS
x11drv_dnd_leave_event( UINT arg
)
334 IDropTarget
*dropTarget
;
336 TRACE("DND Operation canceled\n");
338 /* Notify OLE of DragLeave */
341 dropTarget
= get_droptarget_pointer(XDNDLastDropTargetWnd
);
344 HRESULT hr
= IDropTarget_DragLeave(dropTarget
);
346 WARN("IDropTarget_DragLeave failed, error 0x%08X\n", hr
);
347 IDropTarget_Release(dropTarget
);
351 X11DRV_XDND_FreeDragDropOp();
356 /**************************************************************************
357 * x11drv_dnd_enter_event
359 NTSTATUS WINAPI
x11drv_dnd_enter_event( void *params
, ULONG size
)
361 struct format_entry
*formats
= params
;
362 XDNDAccepted
= FALSE
;
363 X11DRV_XDND_FreeDragDropOp(); /* Clear previously cached data */
365 if ((xdnd_formats
= HeapAlloc( GetProcessHeap(), 0, size
)))
367 memcpy( xdnd_formats
, formats
, size
);
368 xdnd_formats_end
= (struct format_entry
*)((char *)xdnd_formats
+ size
);
374 /**************************************************************************
375 * X11DRV_XDND_HasHDROP
377 static BOOL
X11DRV_XDND_HasHDROP(void)
379 struct format_entry
*iter
;
382 EnterCriticalSection(&xdnd_cs
);
384 /* Find CF_HDROP type if any */
385 for (iter
= xdnd_formats
; iter
< xdnd_formats_end
; iter
= next_format( iter
))
387 if (iter
->format
== CF_HDROP
)
394 LeaveCriticalSection(&xdnd_cs
);
399 /**************************************************************************
400 * X11DRV_XDND_SendDropFiles
402 static HRESULT
X11DRV_XDND_SendDropFiles(HWND hwnd
)
404 struct format_entry
*iter
;
408 EnterCriticalSection(&xdnd_cs
);
410 for (iter
= xdnd_formats
; iter
< xdnd_formats_end
; iter
= next_format( iter
))
412 if (iter
->format
== CF_HDROP
)
420 HGLOBAL dropHandle
= GlobalAlloc(GMEM_FIXED
, iter
->size
);
424 DROPFILES
*lpDrop
= GlobalLock(dropHandle
);
425 memcpy(lpDrop
, iter
->data
, iter
->size
);
426 lpDrop
->pt
.x
= XDNDxy
.x
;
427 lpDrop
->pt
.y
= XDNDxy
.y
;
428 lpDrop
->fNC
= !(ScreenToClient(hwnd
, &lpDrop
->pt
) &&
429 GetClientRect(hwnd
, &rect
) &&
430 PtInRect(&rect
, lpDrop
->pt
));
431 TRACE("Sending WM_DROPFILES: hWnd=0x%p, fNC=%d, x=%d, y=%d, files=%p(%s)\n", hwnd
,
432 lpDrop
->fNC
, lpDrop
->pt
.x
, lpDrop
->pt
.y
, ((char*)lpDrop
) + lpDrop
->pFiles
,
433 debugstr_w((WCHAR
*)(((char*)lpDrop
) + lpDrop
->pFiles
)));
434 GlobalUnlock(dropHandle
);
435 if (PostMessageW(hwnd
, WM_DROPFILES
, (WPARAM
)dropHandle
, 0))
439 hr
= HRESULT_FROM_WIN32(GetLastError());
440 GlobalFree(dropHandle
);
444 hr
= HRESULT_FROM_WIN32(GetLastError());
449 LeaveCriticalSection(&xdnd_cs
);
455 /**************************************************************************
456 * X11DRV_XDND_FreeDragDropOp
458 static void X11DRV_XDND_FreeDragDropOp(void)
462 EnterCriticalSection(&xdnd_cs
);
464 HeapFree( GetProcessHeap(), 0, xdnd_formats
);
465 xdnd_formats
= xdnd_formats_end
= NULL
;
467 XDNDxy
.x
= XDNDxy
.y
= 0;
468 XDNDLastTargetWnd
= NULL
;
469 XDNDLastDropTargetWnd
= NULL
;
470 XDNDAccepted
= FALSE
;
472 LeaveCriticalSection(&xdnd_cs
);
476 /**************************************************************************
477 * X11DRV_XDND_DescribeClipboardFormat
479 static void X11DRV_XDND_DescribeClipboardFormat(int cfFormat
, char *buffer
, int size
)
481 #define D(x) case x: lstrcpynA(buffer, #x, size); return;
504 if (CF_PRIVATEFIRST
<= cfFormat
&& cfFormat
<= CF_PRIVATELAST
)
506 lstrcpynA(buffer
, "some private object", size
);
509 if (CF_GDIOBJFIRST
<= cfFormat
&& cfFormat
<= CF_GDIOBJLAST
)
511 lstrcpynA(buffer
, "some GDI object", size
);
515 GetClipboardFormatNameA(cfFormat
, buffer
, size
);
519 /* The IDataObject singleton we feed to OLE follows */
521 static HRESULT WINAPI
XDNDDATAOBJECT_QueryInterface(IDataObject
*dataObject
,
522 REFIID riid
, void **ppvObject
)
524 TRACE("(%p, %s, %p)\n", dataObject
, debugstr_guid(riid
), ppvObject
);
525 if (IsEqualIID(riid
, &IID_IUnknown
) || IsEqualIID(riid
, &IID_IDataObject
))
527 *ppvObject
= dataObject
;
528 IDataObject_AddRef(dataObject
);
532 return E_NOINTERFACE
;
535 static ULONG WINAPI
XDNDDATAOBJECT_AddRef(IDataObject
*dataObject
)
537 TRACE("(%p)\n", dataObject
);
541 static ULONG WINAPI
XDNDDATAOBJECT_Release(IDataObject
*dataObject
)
543 TRACE("(%p)\n", dataObject
);
547 static HRESULT WINAPI
XDNDDATAOBJECT_GetData(IDataObject
*dataObject
,
548 FORMATETC
*formatEtc
,
552 char formatDesc
[1024];
554 TRACE("(%p, %p, %p)\n", dataObject
, formatEtc
, pMedium
);
555 X11DRV_XDND_DescribeClipboardFormat(formatEtc
->cfFormat
,
556 formatDesc
, sizeof(formatDesc
));
557 TRACE("application is looking for %s\n", formatDesc
);
559 hr
= IDataObject_QueryGetData(dataObject
, formatEtc
);
562 struct format_entry
*iter
;
564 for (iter
= xdnd_formats
; iter
< xdnd_formats_end
; iter
= next_format( iter
))
566 if (iter
->format
== formatEtc
->cfFormat
)
568 pMedium
->tymed
= TYMED_HGLOBAL
;
569 pMedium
->u
.hGlobal
= GlobalAlloc(GMEM_FIXED
| GMEM_ZEROINIT
, iter
->size
);
570 if (pMedium
->u
.hGlobal
== NULL
)
571 return E_OUTOFMEMORY
;
572 memcpy(GlobalLock(pMedium
->u
.hGlobal
), iter
->data
, iter
->size
);
573 GlobalUnlock(pMedium
->u
.hGlobal
);
574 pMedium
->pUnkForRelease
= 0;
582 static HRESULT WINAPI
XDNDDATAOBJECT_GetDataHere(IDataObject
*dataObject
,
583 FORMATETC
*formatEtc
,
586 FIXME("(%p, %p, %p): stub\n", dataObject
, formatEtc
, pMedium
);
587 return DATA_E_FORMATETC
;
590 static HRESULT WINAPI
XDNDDATAOBJECT_QueryGetData(IDataObject
*dataObject
,
591 FORMATETC
*formatEtc
)
593 struct format_entry
*iter
;
594 char formatDesc
[1024];
596 TRACE("(%p, %p={.tymed=0x%x, .dwAspect=%d, .cfFormat=%d}\n",
597 dataObject
, formatEtc
, formatEtc
->tymed
, formatEtc
->dwAspect
, formatEtc
->cfFormat
);
598 X11DRV_XDND_DescribeClipboardFormat(formatEtc
->cfFormat
, formatDesc
, sizeof(formatDesc
));
600 if (formatEtc
->tymed
&& !(formatEtc
->tymed
& TYMED_HGLOBAL
))
602 FIXME("only HGLOBAL medium types supported right now\n");
605 /* Windows Explorer ignores .dwAspect and .lindex for CF_HDROP,
606 * and we have no way to implement them on XDnD anyway, so ignore them too.
609 for (iter
= xdnd_formats
; iter
< xdnd_formats_end
; iter
= next_format( iter
))
611 if (iter
->format
== formatEtc
->cfFormat
)
613 TRACE("application found %s\n", formatDesc
);
617 TRACE("application didn't find %s\n", formatDesc
);
618 return DV_E_FORMATETC
;
621 static HRESULT WINAPI
XDNDDATAOBJECT_GetCanonicalFormatEtc(IDataObject
*dataObject
,
622 FORMATETC
*formatEtc
,
623 FORMATETC
*formatEtcOut
)
625 FIXME("(%p, %p, %p): stub\n", dataObject
, formatEtc
, formatEtcOut
);
626 formatEtcOut
->ptd
= NULL
;
630 static HRESULT WINAPI
XDNDDATAOBJECT_SetData(IDataObject
*dataObject
,
631 FORMATETC
*formatEtc
,
632 STGMEDIUM
*pMedium
, BOOL fRelease
)
634 FIXME("(%p, %p, %p, %s): stub\n", dataObject
, formatEtc
,
635 pMedium
, fRelease
?"TRUE":"FALSE");
639 static HRESULT WINAPI
XDNDDATAOBJECT_EnumFormatEtc(IDataObject
*dataObject
,
641 IEnumFORMATETC
**ppEnumFormatEtc
)
643 struct format_entry
*iter
;
647 TRACE("(%p, %u, %p)\n", dataObject
, dwDirection
, ppEnumFormatEtc
);
649 if (dwDirection
!= DATADIR_GET
)
651 FIXME("only the get direction is implemented\n");
655 for (iter
= xdnd_formats
; iter
< xdnd_formats_end
; iter
= next_format( iter
)) count
++;
657 formats
= HeapAlloc(GetProcessHeap(), 0, count
* sizeof(FORMATETC
));
662 for (iter
= xdnd_formats
; iter
< xdnd_formats_end
; iter
= next_format( iter
))
664 formats
[i
].cfFormat
= iter
->format
;
665 formats
[i
].ptd
= NULL
;
666 formats
[i
].dwAspect
= DVASPECT_CONTENT
;
667 formats
[i
].lindex
= -1;
668 formats
[i
].tymed
= TYMED_HGLOBAL
;
671 hr
= SHCreateStdEnumFmtEtc(count
, formats
, ppEnumFormatEtc
);
672 HeapFree(GetProcessHeap(), 0, formats
);
676 return E_OUTOFMEMORY
;
679 static HRESULT WINAPI
XDNDDATAOBJECT_DAdvise(IDataObject
*dataObject
,
680 FORMATETC
*formatEtc
, DWORD advf
,
681 IAdviseSink
*adviseSink
,
682 DWORD
*pdwConnection
)
684 FIXME("(%p, %p, %u, %p, %p): stub\n", dataObject
, formatEtc
, advf
,
685 adviseSink
, pdwConnection
);
686 return OLE_E_ADVISENOTSUPPORTED
;
689 static HRESULT WINAPI
XDNDDATAOBJECT_DUnadvise(IDataObject
*dataObject
,
692 FIXME("(%p, %u): stub\n", dataObject
, dwConnection
);
693 return OLE_E_ADVISENOTSUPPORTED
;
696 static HRESULT WINAPI
XDNDDATAOBJECT_EnumDAdvise(IDataObject
*dataObject
,
697 IEnumSTATDATA
**pEnumAdvise
)
699 FIXME("(%p, %p): stub\n", dataObject
, pEnumAdvise
);
700 return OLE_E_ADVISENOTSUPPORTED
;
703 static IDataObjectVtbl xdndDataObjectVtbl
=
705 XDNDDATAOBJECT_QueryInterface
,
706 XDNDDATAOBJECT_AddRef
,
707 XDNDDATAOBJECT_Release
,
708 XDNDDATAOBJECT_GetData
,
709 XDNDDATAOBJECT_GetDataHere
,
710 XDNDDATAOBJECT_QueryGetData
,
711 XDNDDATAOBJECT_GetCanonicalFormatEtc
,
712 XDNDDATAOBJECT_SetData
,
713 XDNDDATAOBJECT_EnumFormatEtc
,
714 XDNDDATAOBJECT_DAdvise
,
715 XDNDDATAOBJECT_DUnadvise
,
716 XDNDDATAOBJECT_EnumDAdvise
719 static IDataObject XDNDDataObject
= { &xdndDataObjectVtbl
};
721 NTSTATUS WINAPI
x11drv_dnd_post_drop( void *data
, ULONG size
)
725 if ((handle
= GlobalAlloc( GMEM_SHARE
, size
)))
727 DROPFILES
*ptr
= GlobalLock( handle
);
729 memcpy( ptr
, data
, size
);
730 hwnd
= UlongToHandle( ptr
->fWide
);
732 GlobalUnlock( handle
);
733 PostMessageW( hwnd
, WM_DROPFILES
, (WPARAM
)handle
, 0 );