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