d3dx9_36/tests: Use the proper D3DFVF_PSIZE constant in test_fvf_decl_conversion().
[wine] / dlls / winex11.drv / xdnd.c
1 /*
2  * XDND handler code
3  *
4  * Copyright 2003 Ulrich Czekalla
5  * Copyright 2007 Damjan Jovanovic
6  *
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.
11  *
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.
16  *
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
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <string.h>
26 #ifdef HAVE_UNISTD_H
27 # include <unistd.h>
28 #endif
29 #include <stdarg.h>
30 #include <stdio.h>
31
32 #include "windef.h"
33 #include "winbase.h"
34 #include "wingdi.h"
35 #include "winuser.h"
36
37 #define COBJMACROS
38 #include "x11drv.h"
39 #include "shlobj.h"  /* DROPFILES */
40 #include "oleidl.h"
41 #include "objidl.h"
42
43 #include "wine/unicode.h"
44 #include "wine/debug.h"
45 #include "wine/list.h"
46
47 WINE_DEFAULT_DEBUG_CHANNEL(xdnd);
48
49 /* Maximum wait time for selection notify */
50 #define SELECTION_RETRIES 500  /* wait for .1 seconds */
51 #define SELECTION_WAIT    1000 /* us */
52
53 typedef struct tagXDNDDATA
54 {
55     int cf_win;
56     Atom cf_xdnd;
57     void *data;
58     unsigned int size;
59     struct list entry;
60 } XDNDDATA, *LPXDNDDATA;
61
62 static struct list xdndData = LIST_INIT(xdndData);
63 static POINT XDNDxy = { 0, 0 };
64 static IDataObject XDNDDataObject;
65 static BOOL XDNDAccepted = FALSE;
66 static DWORD XDNDDropEffect = DROPEFFECT_NONE;
67 /* the last window the mouse was over */
68 static HWND XDNDLastTargetWnd;
69 /* might be a ancestor of XDNDLastTargetWnd */
70 static HWND XDNDLastDropTargetWnd;
71
72 static void X11DRV_XDND_InsertXDNDData(int property, int format, void* data, unsigned int len);
73 static int X11DRV_XDND_DeconstructTextURIList(int property, void* data, int len);
74 static int X11DRV_XDND_DeconstructTextPlain(int property, void* data, int len);
75 static int X11DRV_XDND_DeconstructTextHTML(int property, void* data, int len);
76 static int X11DRV_XDND_MapFormat(unsigned int property, unsigned char *data, int len);
77 static void X11DRV_XDND_ResolveProperty(Display *display, Window xwin, Time tm,
78     Atom *types, unsigned long *count);
79 static void X11DRV_XDND_SendDropFiles(HWND hwnd);
80 static void X11DRV_XDND_FreeDragDropOp(void);
81 static unsigned int X11DRV_XDND_UnixToDos(char** lpdest, char* lpsrc, int len);
82 static WCHAR* X11DRV_XDND_URIToDOS(char *encodedURI);
83
84 static CRITICAL_SECTION xdnd_cs;
85 static CRITICAL_SECTION_DEBUG critsect_debug =
86 {
87     0, 0, &xdnd_cs,
88     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
89       0, 0, { (DWORD_PTR)(__FILE__ ": xdnd_cs") }
90 };
91 static CRITICAL_SECTION xdnd_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
92
93
94 /* Based on functions in dlls/ole32/ole2.c */
95 static HANDLE get_droptarget_local_handle(HWND hwnd)
96 {
97     static const WCHAR prop_marshalleddroptarget[] =
98         {'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};
99     HANDLE handle;
100     HANDLE local_handle = 0;
101
102     handle = GetPropW(hwnd, prop_marshalleddroptarget);
103     if (handle)
104     {
105         DWORD pid;
106         HANDLE process;
107
108         GetWindowThreadProcessId(hwnd, &pid);
109         process = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid);
110         if (process)
111         {
112             DuplicateHandle(process, handle, GetCurrentProcess(), &local_handle, 0, FALSE, DUPLICATE_SAME_ACCESS);
113             CloseHandle(process);
114         }
115     }
116     return local_handle;
117 }
118
119 static HRESULT create_stream_from_map(HANDLE map, IStream **stream)
120 {
121     HRESULT hr = E_OUTOFMEMORY;
122     HGLOBAL hmem;
123     void *data;
124     MEMORY_BASIC_INFORMATION info;
125
126     data = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
127     if(!data) return hr;
128
129     VirtualQuery(data, &info, sizeof(info));
130     TRACE("size %d\n", (int)info.RegionSize);
131
132     hmem = GlobalAlloc(GMEM_MOVEABLE, info.RegionSize);
133     if(hmem)
134     {
135         memcpy(GlobalLock(hmem), data, info.RegionSize);
136         GlobalUnlock(hmem);
137         hr = CreateStreamOnHGlobal(hmem, TRUE, stream);
138     }
139     UnmapViewOfFile(data);
140     return hr;
141 }
142
143 static IDropTarget* get_droptarget_pointer(HWND hwnd)
144 {
145     IDropTarget *droptarget = NULL;
146     HANDLE map;
147     IStream *stream;
148
149     map = get_droptarget_local_handle(hwnd);
150     if(!map) return NULL;
151
152     if(SUCCEEDED(create_stream_from_map(map, &stream)))
153     {
154         CoUnmarshalInterface(stream, &IID_IDropTarget, (void**)&droptarget);
155         IStream_Release(stream);
156     }
157     CloseHandle(map);
158     return droptarget;
159 }
160
161 /**************************************************************************
162  * X11DRV_XDND_XdndActionToDROPEFFECT
163  */
164 static DWORD X11DRV_XDND_XdndActionToDROPEFFECT(long action)
165 {
166     /* In Windows, nothing but the given effects is allowed.
167      * In X the given action is just a hint, and you can always
168      * XdndActionCopy and XdndActionPrivate, so be more permissive. */
169     if (action == x11drv_atom(XdndActionCopy))
170         return DROPEFFECT_COPY;
171     else if (action == x11drv_atom(XdndActionMove))
172         return DROPEFFECT_MOVE | DROPEFFECT_COPY;
173     else if (action == x11drv_atom(XdndActionLink))
174         return DROPEFFECT_LINK | DROPEFFECT_COPY;
175     else if (action == x11drv_atom(XdndActionAsk))
176         /* FIXME: should we somehow ask the user what to do here? */
177         return DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK;
178     FIXME("unknown action %ld, assuming DROPEFFECT_COPY\n", action);
179     return DROPEFFECT_COPY;
180 }
181
182 /**************************************************************************
183  * X11DRV_XDND_DROPEFFECTToXdndAction
184  */
185 static long X11DRV_XDND_DROPEFFECTToXdndAction(DWORD effect)
186 {
187     if (effect == DROPEFFECT_COPY)
188         return x11drv_atom(XdndActionCopy);
189     else if (effect == DROPEFFECT_MOVE)
190         return x11drv_atom(XdndActionMove);
191     else if (effect == DROPEFFECT_LINK)
192         return x11drv_atom(XdndActionLink);
193     FIXME("unknown drop effect %u, assuming XdndActionCopy\n", effect);
194     return x11drv_atom(XdndActionCopy);
195 }
196
197 /**************************************************************************
198  * X11DRV_XDND_EnterEvent
199  *
200  * Handle an XdndEnter event.
201  */
202 void X11DRV_XDND_EnterEvent( HWND hWnd, XClientMessageEvent *event )
203 {
204     int version;
205     Atom *xdndtypes;
206     unsigned long count = 0;
207
208     version = (event->data.l[1] & 0xFF000000) >> 24;
209     TRACE("ver(%d) check-XdndTypeList(%ld) data=%ld,%ld,%ld,%ld,%ld\n",
210           version, (event->data.l[1] & 1),
211           event->data.l[0], event->data.l[1], event->data.l[2],
212           event->data.l[3], event->data.l[4]);
213
214     if (version > WINE_XDND_VERSION)
215     {
216         TRACE("Ignores unsupported version\n");
217         return;
218     }
219
220     XDNDAccepted = FALSE;
221
222     /* If the source supports more than 3 data types we retrieve
223      * the entire list. */
224     if (event->data.l[1] & 1)
225     {
226         Atom acttype;
227         int actfmt;
228         unsigned long bytesret;
229
230         /* Request supported formats from source window */
231         wine_tsx11_lock();
232         XGetWindowProperty(event->display, event->data.l[0], x11drv_atom(XdndTypeList),
233                            0, 65535, FALSE, AnyPropertyType, &acttype, &actfmt, &count,
234                            &bytesret, (unsigned char**)&xdndtypes);
235         wine_tsx11_unlock();
236     }
237     else
238     {
239         count = 3;
240         xdndtypes = (Atom*) &event->data.l[2];
241     }
242
243     if (TRACE_ON(xdnd))
244     {
245         unsigned int i = 0;
246
247         wine_tsx11_lock();
248         for (; i < count; i++)
249         {
250             if (xdndtypes[i] != 0)
251             {
252                 char * pn = XGetAtomName(event->display, xdndtypes[i]);
253                 TRACE("XDNDEnterAtom %ld: %s\n", xdndtypes[i], pn);
254                 XFree(pn);
255             }
256         }
257         wine_tsx11_unlock();
258     }
259
260     /* Do a one-time data read and cache results */
261     X11DRV_XDND_ResolveProperty(event->display, event->window,
262                                 event->data.l[1], xdndtypes, &count);
263
264     if (event->data.l[1] & 1)
265         XFree(xdndtypes);
266 }
267
268 /**************************************************************************
269  * X11DRV_XDND_PositionEvent
270  *
271  * Handle an XdndPosition event.
272  */
273 void X11DRV_XDND_PositionEvent( HWND hWnd, XClientMessageEvent *event )
274 {
275     XClientMessageEvent e;
276     int accept = 0; /* Assume we're not accepting */
277     IDropTarget *dropTarget = NULL;
278     DWORD effect;
279     POINTL pointl;
280     HWND targetWindow;
281     HRESULT hr;
282
283     XDNDxy.x = event->data.l[2] >> 16;
284     XDNDxy.y = event->data.l[2] & 0xFFFF;
285     targetWindow = WindowFromPoint(XDNDxy);
286
287     pointl.x = XDNDxy.x;
288     pointl.y = XDNDxy.y;
289     effect = X11DRV_XDND_XdndActionToDROPEFFECT(event->data.l[4]);
290
291     if (!XDNDAccepted || XDNDLastTargetWnd != targetWindow)
292     {
293         /* Notify OLE of DragEnter. Result determines if we accept */
294         HWND dropTargetWindow;
295
296         if (XDNDLastDropTargetWnd)
297         {
298             dropTarget = get_droptarget_pointer(XDNDLastDropTargetWnd);
299             if (dropTarget)
300             {
301                 hr = IDropTarget_DragLeave(dropTarget);
302                 if (FAILED(hr))
303                     WARN("IDropTarget_DragLeave failed, error 0x%08X\n", hr);
304                 IDropTarget_Release(dropTarget);
305             }
306         }
307         dropTargetWindow = targetWindow;
308         do
309         {
310             dropTarget = get_droptarget_pointer(dropTargetWindow);
311         } while (dropTarget == NULL && (dropTargetWindow = GetParent(dropTargetWindow)) != NULL);
312         XDNDLastTargetWnd = targetWindow;
313         XDNDLastDropTargetWnd = dropTargetWindow;
314         if (dropTarget)
315         {
316             hr = IDropTarget_DragEnter(dropTarget, &XDNDDataObject,
317                                        MK_LBUTTON, pointl, &effect);
318             if (SUCCEEDED(hr))
319             {
320                 if (effect != DROPEFFECT_NONE)
321                 {
322                     XDNDAccepted = TRUE;
323                     TRACE("the application accepted the drop\n");
324                 }
325                 else
326                     TRACE("the application refused the drop\n");
327             }
328             else
329                 WARN("IDropTarget_DragEnter failed, error 0x%08X\n", hr);
330             IDropTarget_Release(dropTarget);
331         }
332     }
333     if (XDNDAccepted && XDNDLastTargetWnd == targetWindow)
334     {
335         /* If drag accepted notify OLE of DragOver */
336         dropTarget = get_droptarget_pointer(XDNDLastDropTargetWnd);
337         if (dropTarget)
338         {
339             hr = IDropTarget_DragOver(dropTarget, MK_LBUTTON, pointl, &effect);
340             if (SUCCEEDED(hr))
341                 XDNDDropEffect = effect;
342             else
343                 WARN("IDropTarget_DragOver failed, error 0x%08X\n", hr);
344             IDropTarget_Release(dropTarget);
345         }
346     }
347
348     if (XDNDAccepted)
349         accept = 1;
350     if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)
351         accept = 1;
352
353     TRACE("action req: %ld accept(%d) at x(%d),y(%d)\n",
354           event->data.l[4], accept, XDNDxy.x, XDNDxy.y);
355
356     /*
357      * Let source know if we're accepting the drop by
358      * sending a status message.
359      */
360     e.type = ClientMessage;
361     e.display = event->display;
362     e.window = event->data.l[0];
363     e.message_type = x11drv_atom(XdndStatus);
364     e.format = 32;
365     e.data.l[0] = event->window;
366     e.data.l[1] = accept;
367     e.data.l[2] = 0; /* Empty Rect */
368     e.data.l[3] = 0; /* Empty Rect */
369     if (accept)
370         e.data.l[4] = X11DRV_XDND_DROPEFFECTToXdndAction(effect);
371     else
372         e.data.l[4] = None;
373     wine_tsx11_lock();
374     XSendEvent(event->display, event->data.l[0], False, NoEventMask, (XEvent*)&e);
375     wine_tsx11_unlock();
376 }
377
378 /**************************************************************************
379  * X11DRV_XDND_DropEvent
380  *
381  * Handle an XdndDrop event.
382  */
383 void X11DRV_XDND_DropEvent( HWND hWnd, XClientMessageEvent *event )
384 {
385     XClientMessageEvent e;
386     IDropTarget *dropTarget;
387
388     TRACE("\n");
389
390     /* If we have a HDROP type we send a WM_ACCEPTFILES.*/
391     if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)
392         X11DRV_XDND_SendDropFiles( hWnd );
393
394     /* Notify OLE of Drop */
395     dropTarget = get_droptarget_pointer(XDNDLastDropTargetWnd);
396     if (dropTarget)
397     {
398         HRESULT hr;
399         POINTL pointl;
400         DWORD effect = XDNDDropEffect;
401
402         pointl.x = XDNDxy.x;
403         pointl.y = XDNDxy.y;
404         hr = IDropTarget_Drop(dropTarget, &XDNDDataObject, MK_LBUTTON,
405                               pointl, &effect);
406         if (SUCCEEDED(hr))
407         {
408             if (effect != DROPEFFECT_NONE)
409                 TRACE("drop succeeded\n");
410             else
411                 TRACE("the application refused the drop\n");
412         }
413         else
414             WARN("drop failed, error 0x%08X\n", hr);
415         IDropTarget_Release(dropTarget);
416     }
417
418     X11DRV_XDND_FreeDragDropOp();
419
420     /* Tell the target we are finished. */
421     memset(&e, 0, sizeof(e));
422     e.type = ClientMessage;
423     e.display = event->display;
424     e.window = event->data.l[0];
425     e.message_type = x11drv_atom(XdndFinished);
426     e.format = 32;
427     e.data.l[0] = event->window;
428     wine_tsx11_lock();
429     XSendEvent(event->display, event->data.l[0], False, NoEventMask, (XEvent*)&e);
430     wine_tsx11_unlock();
431 }
432
433 /**************************************************************************
434  * X11DRV_XDND_LeaveEvent
435  *
436  * Handle an XdndLeave event.
437  */
438 void X11DRV_XDND_LeaveEvent( HWND hWnd, XClientMessageEvent *event )
439 {
440     IDropTarget *dropTarget;
441
442     TRACE("DND Operation canceled\n");
443
444     /* Notify OLE of DragLeave */
445     dropTarget = get_droptarget_pointer(XDNDLastDropTargetWnd);
446     if (dropTarget)
447     {
448         HRESULT hr = IDropTarget_DragLeave(dropTarget);
449         if (FAILED(hr))
450             WARN("IDropTarget_DragLeave failed, error 0x%08X\n", hr);
451         IDropTarget_Release(dropTarget);
452     }
453
454     X11DRV_XDND_FreeDragDropOp();
455 }
456
457
458 /**************************************************************************
459  * X11DRV_XDND_ResolveProperty
460  *
461  * Resolve all MIME types to windows clipboard formats. All data is cached.
462  */
463 static void X11DRV_XDND_ResolveProperty(Display *display, Window xwin, Time tm,
464                                         Atom *types, unsigned long *count)
465 {
466     unsigned int i, j;
467     BOOL res;
468     XEvent xe;
469     Atom acttype;
470     int actfmt;
471     unsigned long bytesret, icount;
472     int entries = 0;
473     unsigned char* data = NULL;
474     XDNDDATA *current, *next;
475     BOOL haveHDROP = FALSE;
476
477     TRACE("count(%ld)\n", *count);
478
479     X11DRV_XDND_FreeDragDropOp(); /* Clear previously cached data */
480
481     for (i = 0; i < *count; i++)
482     {
483         TRACE("requesting atom %ld from xwin %ld\n", types[i], xwin);
484
485         if (types[i] == 0)
486             continue;
487
488         wine_tsx11_lock();
489         XConvertSelection(display, x11drv_atom(XdndSelection), types[i],
490                           x11drv_atom(XdndTarget), xwin, /*tm*/CurrentTime);
491         wine_tsx11_unlock();
492
493         /*
494          * Wait for SelectionNotify
495          */
496         for (j = 0; j < SELECTION_RETRIES; j++)
497         {
498             wine_tsx11_lock();
499             res = XCheckTypedWindowEvent(display, xwin, SelectionNotify, &xe);
500             wine_tsx11_unlock();
501             if (res && xe.xselection.selection == x11drv_atom(XdndSelection)) break;
502
503             usleep(SELECTION_WAIT);
504         }
505
506         if (xe.xselection.property == None)
507             continue;
508
509         wine_tsx11_lock();
510         XGetWindowProperty(display, xwin, x11drv_atom(XdndTarget), 0, 65535, FALSE,
511             AnyPropertyType, &acttype, &actfmt, &icount, &bytesret, &data);
512         wine_tsx11_unlock();
513
514         entries += X11DRV_XDND_MapFormat(types[i], data, get_property_size( actfmt, icount ));
515         wine_tsx11_lock();
516         XFree(data);
517         wine_tsx11_unlock();
518     }
519
520     /* On Windows when there is a CF_HDROP, there are no other CF_ formats.
521      * foobar2000 relies on this (spaces -> %20's without it).
522      */
523     LIST_FOR_EACH_ENTRY(current, &xdndData, XDNDDATA, entry)
524     {
525         if (current->cf_win == CF_HDROP)
526         {
527             haveHDROP = TRUE;
528             break;
529         }
530     }
531     if (haveHDROP)
532     {
533         LIST_FOR_EACH_ENTRY_SAFE(current, next, &xdndData, XDNDDATA, entry)
534         {
535             if (current->cf_win != CF_HDROP && current->cf_win < CF_MAX)
536             {
537                 list_remove(&current->entry);
538                 HeapFree(GetProcessHeap(), 0, current->data);
539                 HeapFree(GetProcessHeap(), 0, current);
540                 --entries;
541             }
542         }
543     }
544
545     *count = entries;
546 }
547
548
549 /**************************************************************************
550  * X11DRV_XDND_InsertXDNDData
551  *
552  * Cache available XDND property
553  */
554 static void X11DRV_XDND_InsertXDNDData(int property, int format, void* data, unsigned int len)
555 {
556     LPXDNDDATA current = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(XDNDDATA));
557
558     if (current)
559     {
560         EnterCriticalSection(&xdnd_cs);
561         current->cf_xdnd = property;
562         current->cf_win = format;
563         current->data = data;
564         current->size = len;
565         list_add_tail(&xdndData, &current->entry);
566         LeaveCriticalSection(&xdnd_cs);
567     }
568 }
569
570
571 /**************************************************************************
572  * X11DRV_XDND_MapFormat
573  *
574  * Map XDND MIME format to windows clipboard format.
575  */
576 static int X11DRV_XDND_MapFormat(unsigned int property, unsigned char *data, int len)
577 {
578     void* xdata;
579     int count = 0;
580
581     TRACE("%d: %s\n", property, data);
582
583     /* Always include the raw type */
584     xdata = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len);
585     memcpy(xdata, data, len);
586     X11DRV_XDND_InsertXDNDData(property, property, xdata, len);
587     count++;
588
589     if (property == x11drv_atom(text_uri_list))
590         count += X11DRV_XDND_DeconstructTextURIList(property, data, len);
591     else if (property == x11drv_atom(text_plain))
592         count += X11DRV_XDND_DeconstructTextPlain(property, data, len);
593     else if (property == x11drv_atom(text_html))
594         count += X11DRV_XDND_DeconstructTextHTML(property, data, len);
595
596     return count;
597 }
598
599
600 /**************************************************************************
601  * X11DRV_XDND_DeconstructTextURIList
602  *
603  * Interpret text/uri-list data and add records to <dndfmt> linked list
604  */
605 static int X11DRV_XDND_DeconstructTextURIList(int property, void* data, int len)
606 {
607     char *uriList = data;
608     char *uri;
609     WCHAR *path;
610
611     WCHAR *out = NULL;
612     int size = 0;
613     int capacity = 4096;
614
615     int count = 0;
616     int start = 0;
617     int end = 0;
618
619     out = HeapAlloc(GetProcessHeap(), 0, capacity * sizeof(WCHAR));
620     if (out == NULL)
621         return 0;
622
623     while (end < len)
624     {
625         while (end < len && uriList[end] != '\r')
626             ++end;
627         if (end < (len - 1) && uriList[end+1] != '\n')
628         {
629             WARN("URI list line doesn't end in \\r\\n\n");
630             break;
631         }
632
633         uri = HeapAlloc(GetProcessHeap(), 0, end - start + 1);
634         if (uri == NULL)
635             break;
636         lstrcpynA(uri, &uriList[start], end - start + 1);
637         path = X11DRV_XDND_URIToDOS(uri);
638         TRACE("converted URI %s to DOS path %s\n", debugstr_a(uri), debugstr_w(path));
639         HeapFree(GetProcessHeap(), 0, uri);
640
641         if (path)
642         {
643             int pathSize = strlenW(path) + 1;
644             if (pathSize > capacity-size)
645             {
646                 capacity = 2*capacity + pathSize;
647                 out = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, out, (capacity + 1)*sizeof(WCHAR));
648                 if (out == NULL)
649                     goto done;
650             }
651             memcpy(&out[size], path, pathSize * sizeof(WCHAR));
652             size += pathSize;
653         done:
654             HeapFree(GetProcessHeap(), 0, path);
655             if (out == NULL)
656                 break;
657         }
658
659         start = end + 2;
660         end = start;
661     }
662     if (out && end >= len)
663     {
664         DROPFILES *dropFiles;
665         dropFiles = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DROPFILES) + (size + 1)*sizeof(WCHAR));
666         if (dropFiles)
667         {
668             dropFiles->pFiles = sizeof(DROPFILES);
669             dropFiles->pt.x = XDNDxy.x;
670             dropFiles->pt.y = XDNDxy.y;
671             dropFiles->fNC = 0;
672             dropFiles->fWide = TRUE;
673             out[size] = '\0';
674             memcpy(((char*)dropFiles) + dropFiles->pFiles, out, (size + 1)*sizeof(WCHAR));
675             X11DRV_XDND_InsertXDNDData(property, CF_HDROP, dropFiles, sizeof(DROPFILES) + (size + 1)*sizeof(WCHAR));
676             count = 1;
677         }
678     }
679     HeapFree(GetProcessHeap(), 0, out);
680     return count;
681 }
682
683
684 /**************************************************************************
685  * X11DRV_XDND_DeconstructTextPlain
686  *
687  * Interpret text/plain Data and add records to <dndfmt> linked list
688  */
689 static int X11DRV_XDND_DeconstructTextPlain(int property, void* data, int len)
690 {
691     char* dostext;
692
693     /* Always supply plain text */
694     X11DRV_XDND_UnixToDos(&dostext, data, len);
695     X11DRV_XDND_InsertXDNDData(property, CF_TEXT, dostext, strlen(dostext));
696
697     TRACE("CF_TEXT (%d): %s\n", CF_TEXT, dostext);
698
699     return 1;
700 }
701
702
703 /**************************************************************************
704  * X11DRV_XDND_DeconstructTextHTML
705  *
706  * Interpret text/html data and add records to <dndfmt> linked list
707  */
708 static int X11DRV_XDND_DeconstructTextHTML(int property, void* data, int len)
709 {
710     char* dostext;
711
712     X11DRV_XDND_UnixToDos(&dostext, data, len);
713
714     X11DRV_XDND_InsertXDNDData(property,
715         RegisterClipboardFormatA("UniformResourceLocator"), dostext, strlen(dostext));
716
717     TRACE("UniformResourceLocator: %s\n", dostext);
718
719     return 1;
720 }
721
722
723 /**************************************************************************
724  * X11DRV_XDND_SendDropFiles
725  */
726 static void X11DRV_XDND_SendDropFiles(HWND hwnd)
727 {
728     LPXDNDDATA current = NULL;
729     BOOL found = FALSE;
730
731     EnterCriticalSection(&xdnd_cs);
732
733     /* Find CF_HDROP type if any */
734     LIST_FOR_EACH_ENTRY(current, &xdndData, XDNDDATA, entry)
735     {
736         if (current->cf_win == CF_HDROP)
737         {
738             found = TRUE;
739             break;
740         }
741     }
742
743     if (found)
744     {
745         DROPFILES *lpDrop = current->data;
746
747         if (lpDrop)
748         {
749             lpDrop->pt.x = XDNDxy.x;
750             lpDrop->pt.y = XDNDxy.y;
751
752             TRACE("Sending WM_DROPFILES: hWnd(0x%p) %p(%s)\n", hwnd,
753                 ((char*)lpDrop) + lpDrop->pFiles, debugstr_w((WCHAR*)(((char*)lpDrop) + lpDrop->pFiles)));
754
755             PostMessageW(hwnd, WM_DROPFILES, (WPARAM)lpDrop, 0L);
756         }
757     }
758
759     LeaveCriticalSection(&xdnd_cs);
760 }
761
762
763 /**************************************************************************
764  * X11DRV_XDND_FreeDragDropOp
765  */
766 static void X11DRV_XDND_FreeDragDropOp(void)
767 {
768     LPXDNDDATA next;
769     LPXDNDDATA current;
770
771     TRACE("\n");
772
773     EnterCriticalSection(&xdnd_cs);
774
775     /** Free data cache */
776     LIST_FOR_EACH_ENTRY_SAFE(current, next, &xdndData, XDNDDATA, entry)
777     {
778         list_remove(&current->entry);
779         HeapFree(GetProcessHeap(), 0, current);
780     }
781
782     XDNDxy.x = XDNDxy.y = 0;
783     XDNDLastTargetWnd = NULL;
784     XDNDLastDropTargetWnd = NULL;
785     XDNDAccepted = FALSE;
786
787     LeaveCriticalSection(&xdnd_cs);
788 }
789
790
791
792 /**************************************************************************
793  * X11DRV_XDND_UnixToDos
794  */
795 static unsigned int X11DRV_XDND_UnixToDos(char** lpdest, char* lpsrc, int len)
796 {
797     int i;
798     unsigned int destlen, lines;
799
800     for (i = 0, lines = 0; i <= len; i++)
801     {
802         if (lpsrc[i] == '\n')
803             lines++;
804     }
805
806     destlen = len + lines + 1;
807
808     if (lpdest)
809     {
810         char* lpstr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, destlen);
811         for (i = 0, lines = 0; i <= len; i++)
812         {
813             if (lpsrc[i] == '\n')
814                 lpstr[++lines + i] = '\r';
815             lpstr[lines + i] = lpsrc[i];
816         }
817
818         *lpdest = lpstr;
819     }
820
821     return lines;
822 }
823
824
825 /**************************************************************************
826  * X11DRV_XDND_URIToDOS
827  */
828 static WCHAR* X11DRV_XDND_URIToDOS(char *encodedURI)
829 {
830     WCHAR *ret = NULL;
831     int i;
832     int j = 0;
833     char *uri = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, strlen(encodedURI) + 1);
834     if (uri == NULL)
835         return NULL;
836     for (i = 0; encodedURI[i]; ++i)
837     {
838         if (encodedURI[i] == '%')
839         { 
840             if (encodedURI[i+1] && encodedURI[i+2])
841             {
842                 char buffer[3];
843                 int number;
844                 buffer[0] = encodedURI[i+1];
845                 buffer[1] = encodedURI[i+2];
846                 buffer[2] = '\0';
847                 sscanf(buffer, "%x", &number);
848                 uri[j++] = number;
849                 i += 2;
850             }
851             else
852             {
853                 WARN("invalid URI encoding in %s\n", debugstr_a(encodedURI));
854                 HeapFree(GetProcessHeap(), 0, uri);
855                 return NULL;
856             }
857         }
858         else
859             uri[j++] = encodedURI[i];
860     }
861
862     /* Read http://www.freedesktop.org/wiki/Draganddropwarts and cry... */
863     if (strncmp(uri, "file:/", 6) == 0)
864     {
865         if (uri[6] == '/')
866         {
867             if (uri[7] == '/')
868             {
869                 /* file:///path/to/file (nautilus, thunar) */
870                 ret = wine_get_dos_file_name(&uri[7]);
871             }
872             else if (uri[7])
873             {
874                 /* file://hostname/path/to/file (X file drag spec) */
875                 char hostname[256];
876                 char *path = strchr(&uri[7], '/');
877                 if (path)
878                 {
879                     *path = '\0';
880                     if (strcmp(&uri[7], "localhost") == 0)
881                     {
882                         *path = '/';
883                         ret = wine_get_dos_file_name(path);
884                     }
885                     else if (gethostname(hostname, sizeof(hostname)) == 0)
886                     {
887                         if (strcmp(hostname, &uri[7]) == 0)
888                         {
889                             *path = '/';
890                             ret = wine_get_dos_file_name(path);
891                         }
892                     }
893                 }
894             }
895         }
896         else if (uri[6])
897         {
898             /* file:/path/to/file (konqueror) */
899             ret = wine_get_dos_file_name(&uri[5]);
900         }
901     }
902     HeapFree(GetProcessHeap(), 0, uri);
903     return ret;
904 }
905
906
907 /**************************************************************************
908  * X11DRV_XDND_DescribeClipboardFormat
909  */
910 static void X11DRV_XDND_DescribeClipboardFormat(int cfFormat, char *buffer, int size)
911 {
912 #define D(x) case x: lstrcpynA(buffer, #x, size); return;
913     switch (cfFormat)
914     {
915         D(CF_TEXT)
916         D(CF_BITMAP)
917         D(CF_METAFILEPICT)
918         D(CF_SYLK)
919         D(CF_DIF)
920         D(CF_TIFF)
921         D(CF_OEMTEXT)
922         D(CF_DIB)
923         D(CF_PALETTE)
924         D(CF_PENDATA)
925         D(CF_RIFF)
926         D(CF_WAVE)
927         D(CF_UNICODETEXT)
928         D(CF_ENHMETAFILE)
929         D(CF_HDROP)
930         D(CF_LOCALE)
931         D(CF_DIBV5)
932     }
933 #undef D
934
935     if (CF_PRIVATEFIRST <= cfFormat && cfFormat <= CF_PRIVATELAST)
936     {
937         lstrcpynA(buffer, "some private object", size);
938         return;
939     }
940     if (CF_GDIOBJFIRST <= cfFormat && cfFormat <= CF_GDIOBJLAST)
941     {
942         lstrcpynA(buffer, "some GDI object", size);
943         return;
944     }
945
946     GetClipboardFormatNameA(cfFormat, buffer, size);
947 }
948
949
950 /* The IDataObject singleton we feed to OLE follows */
951
952 static HRESULT WINAPI XDNDDATAOBJECT_QueryInterface(IDataObject *dataObject,
953                                                     REFIID riid, void **ppvObject)
954 {
955     TRACE("(%p, %s, %p)\n", dataObject, debugstr_guid(riid), ppvObject);
956     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDataObject))
957     {
958         *ppvObject = dataObject;
959         IDataObject_AddRef(dataObject);
960         return S_OK;
961     }
962     *ppvObject = NULL;
963     return E_NOINTERFACE;
964 }
965
966 static ULONG WINAPI XDNDDATAOBJECT_AddRef(IDataObject *dataObject)
967 {
968     TRACE("(%p)\n", dataObject);
969     return 2;
970 }
971
972 static ULONG WINAPI XDNDDATAOBJECT_Release(IDataObject *dataObject)
973 {
974     TRACE("(%p)\n", dataObject);
975     return 1;
976 }
977
978 static HRESULT WINAPI XDNDDATAOBJECT_GetData(IDataObject *dataObject,
979                                              FORMATETC *formatEtc,
980                                              STGMEDIUM *pMedium)
981 {
982     HRESULT hr;
983     char formatDesc[1024];
984
985     TRACE("(%p, %p, %p)\n", dataObject, formatEtc, pMedium);
986     X11DRV_XDND_DescribeClipboardFormat(formatEtc->cfFormat,
987         formatDesc, sizeof(formatDesc));
988     TRACE("application is looking for %s\n", formatDesc);
989
990     hr = IDataObject_QueryGetData(dataObject, formatEtc);
991     if (SUCCEEDED(hr))
992     {
993         XDNDDATA *current;
994         LIST_FOR_EACH_ENTRY(current, &xdndData, XDNDDATA, entry)
995         {
996             if (current->cf_win == formatEtc->cfFormat)
997             {
998                 pMedium->tymed = TYMED_HGLOBAL;
999                 pMedium->hGlobal = HeapAlloc(GetProcessHeap(), 0, current->size);
1000                 if (pMedium->hGlobal == NULL)
1001                     return E_OUTOFMEMORY;
1002                 memcpy(pMedium->hGlobal, current->data, current->size);
1003                 pMedium->pUnkForRelease = 0;
1004                 return S_OK;
1005             }
1006         }
1007     }
1008     return hr;
1009 }
1010
1011 static HRESULT WINAPI XDNDDATAOBJECT_GetDataHere(IDataObject *dataObject,
1012                                                  FORMATETC *formatEtc,
1013                                                  STGMEDIUM *pMedium)
1014 {
1015     FIXME("(%p, %p, %p): stub\n", dataObject, formatEtc, pMedium);
1016     return DATA_E_FORMATETC;
1017 }
1018
1019 static HRESULT WINAPI XDNDDATAOBJECT_QueryGetData(IDataObject *dataObject,
1020                                                   FORMATETC *formatEtc)
1021 {
1022     char formatDesc[1024];
1023     XDNDDATA *current;
1024
1025     TRACE("(%p, %p={.tymed=0x%x, .dwAspect=%d, .cfFormat=%d}\n",
1026         dataObject, formatEtc, formatEtc->tymed, formatEtc->dwAspect, formatEtc->cfFormat);
1027     X11DRV_XDND_DescribeClipboardFormat(formatEtc->cfFormat, formatDesc, sizeof(formatDesc));
1028
1029     if (formatEtc->tymed && !(formatEtc->tymed & TYMED_HGLOBAL))
1030     {
1031         FIXME("only HGLOBAL medium types supported right now\n");
1032         return DV_E_TYMED;
1033     }
1034     if (formatEtc->dwAspect != DVASPECT_CONTENT)
1035     {
1036         FIXME("only the content aspect is supported right now\n");
1037         return E_NOTIMPL;
1038     }
1039
1040     LIST_FOR_EACH_ENTRY(current, &xdndData, XDNDDATA, entry)
1041     {
1042         if (current->cf_win == formatEtc->cfFormat)
1043         {
1044             TRACE("application found %s\n", formatDesc);
1045             return S_OK;
1046         }
1047     }
1048     TRACE("application didn't find %s\n", formatDesc);
1049     return DV_E_FORMATETC;
1050 }
1051
1052 static HRESULT WINAPI XDNDDATAOBJECT_GetCanonicalFormatEtc(IDataObject *dataObject,
1053                                                            FORMATETC *formatEtc,
1054                                                            FORMATETC *formatEtcOut)
1055 {
1056     FIXME("(%p, %p, %p): stub\n", dataObject, formatEtc, formatEtcOut);
1057     formatEtcOut->ptd = NULL;
1058     return E_NOTIMPL;
1059 }
1060
1061 static HRESULT WINAPI XDNDDATAOBJECT_SetData(IDataObject *dataObject,
1062                                              FORMATETC *formatEtc,
1063                                              STGMEDIUM *pMedium, BOOL fRelease)
1064 {
1065     FIXME("(%p, %p, %p, %s): stub\n", dataObject, formatEtc,
1066         pMedium, fRelease?"TRUE":"FALSE");
1067     return E_NOTIMPL;
1068 }
1069
1070 static HRESULT WINAPI XDNDDATAOBJECT_EnumFormatEtc(IDataObject *dataObject,
1071                                                    DWORD dwDirection,
1072                                                    IEnumFORMATETC **ppEnumFormatEtc)
1073 {
1074     DWORD count;
1075     FORMATETC *formats;
1076
1077     TRACE("(%p, %u, %p)\n", dataObject, dwDirection, ppEnumFormatEtc);
1078
1079     if (dwDirection != DATADIR_GET)
1080     {
1081         FIXME("only the get direction is implemented\n");
1082         return E_NOTIMPL;
1083     }
1084
1085     count = list_count(&xdndData);
1086     formats = HeapAlloc(GetProcessHeap(), 0, count * sizeof(FORMATETC));
1087     if (formats)
1088     {
1089         XDNDDATA *current;
1090         DWORD i = 0;
1091         HRESULT hr;
1092         LIST_FOR_EACH_ENTRY(current, &xdndData, XDNDDATA, entry)
1093         {
1094             formats[i].cfFormat = current->cf_win;
1095             formats[i].ptd = NULL;
1096             formats[i].dwAspect = DVASPECT_CONTENT;
1097             formats[i].lindex = -1;
1098             formats[i].tymed = TYMED_HGLOBAL;
1099             i++;
1100         }
1101         hr = SHCreateStdEnumFmtEtc(count, formats, ppEnumFormatEtc);
1102         HeapFree(GetProcessHeap(), 0, formats);
1103         return hr;
1104     }
1105     else
1106         return E_OUTOFMEMORY;
1107 }
1108
1109 static HRESULT WINAPI XDNDDATAOBJECT_DAdvise(IDataObject *dataObject,
1110                                              FORMATETC *formatEtc, DWORD advf,
1111                                              IAdviseSink *adviseSink,
1112                                              DWORD *pdwConnection)
1113 {
1114     FIXME("(%p, %p, %u, %p, %p): stub\n", dataObject, formatEtc, advf,
1115         adviseSink, pdwConnection);
1116     return OLE_E_ADVISENOTSUPPORTED;
1117 }
1118
1119 static HRESULT WINAPI XDNDDATAOBJECT_DUnadvise(IDataObject *dataObject,
1120                                                DWORD dwConnection)
1121 {
1122     FIXME("(%p, %u): stub\n", dataObject, dwConnection);
1123     return OLE_E_ADVISENOTSUPPORTED;
1124 }
1125
1126 static HRESULT WINAPI XDNDDATAOBJECT_EnumDAdvise(IDataObject *dataObject,
1127                                                  IEnumSTATDATA **pEnumAdvise)
1128 {
1129     FIXME("(%p, %p): stub\n", dataObject, pEnumAdvise);
1130     return OLE_E_ADVISENOTSUPPORTED;
1131 }
1132
1133 static IDataObjectVtbl xdndDataObjectVtbl =
1134 {
1135     XDNDDATAOBJECT_QueryInterface,
1136     XDNDDATAOBJECT_AddRef,
1137     XDNDDATAOBJECT_Release,
1138     XDNDDATAOBJECT_GetData,
1139     XDNDDATAOBJECT_GetDataHere,
1140     XDNDDATAOBJECT_QueryGetData,
1141     XDNDDATAOBJECT_GetCanonicalFormatEtc,
1142     XDNDDATAOBJECT_SetData,
1143     XDNDDATAOBJECT_EnumFormatEtc,
1144     XDNDDATAOBJECT_DAdvise,
1145     XDNDDATAOBJECT_DUnadvise,
1146     XDNDDATAOBJECT_EnumDAdvise
1147 };
1148
1149 static IDataObject XDNDDataObject = { &xdndDataObjectVtbl };