wintrust: Implement WintrustLoadFunctionPointers.
[wine] / dlls / winex11.drv / xdnd.c
1 /*
2  * XDND handler code
3  *
4  * Copyright 2003 Ulrich Czekalla
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <string.h>
25 #ifdef HAVE_UNISTD_H
26 # include <unistd.h>
27 #endif
28 #include <stdarg.h>
29
30 #include "windef.h"
31 #include "winbase.h"
32 #include "wingdi.h"
33 #include "winuser.h"
34 #include "winerror.h"
35
36 #include "x11drv.h"
37 #include "shlobj.h"  /* DROPFILES */
38
39 #include "wine/debug.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(xdnd);
42
43 /* Maximum wait time for selection notify */
44 #define SELECTION_RETRIES 500  /* wait for .1 seconds */
45 #define SELECTION_WAIT    1000 /* us */
46
47 typedef struct tagXDNDDATA
48 {
49     int cf_win;
50     Atom cf_xdnd;
51     void *data;
52     unsigned int size;
53     struct tagXDNDDATA *next;
54 } XDNDDATA, *LPXDNDDATA;
55
56 static LPXDNDDATA XDNDData = NULL;
57 static POINT XDNDxy = { 0, 0 };
58
59 static void X11DRV_XDND_InsertXDNDData(int property, int format, void* data, unsigned int len);
60 static int X11DRV_XDND_DeconstructTextPlain(int property, void* data, int len);
61 static int X11DRV_XDND_DeconstructTextHTML(int property, void* data, int len);
62 static int X11DRV_XDND_MapFormat(unsigned int property, unsigned char *data, int len);
63 static void X11DRV_XDND_ResolveProperty(Display *display, Window xwin, Time tm,
64     Atom *types, unsigned long *count);
65 static void X11DRV_XDND_SendDropFiles(HWND hwnd);
66 static void X11DRV_XDND_FreeDragDropOp(void);
67 static unsigned int X11DRV_XDND_UnixToDos(char** lpdest, char* lpsrc, int len);
68 static DROPFILES* X11DRV_XDND_BuildDropFiles(char* filename, unsigned int len, POINT pt);
69
70 static CRITICAL_SECTION xdnd_cs;
71 static CRITICAL_SECTION_DEBUG critsect_debug =
72 {
73     0, 0, &xdnd_cs,
74     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
75       0, 0, { (DWORD_PTR)(__FILE__ ": xdnd_cs") }
76 };
77 static CRITICAL_SECTION xdnd_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
78
79
80 /**************************************************************************
81  * X11DRV_XDND_EnterEvent
82  *
83  * Handle an XdndEnter event.
84  */
85 void X11DRV_XDND_EnterEvent( HWND hWnd, XClientMessageEvent *event )
86 {
87     Atom *xdndtypes;
88     unsigned long count = 0;
89
90     TRACE("ver(%ld) check-XdndTypeList(%ld) data=%ld,%ld,%ld,%ld,%ld\n",
91           (event->data.l[1] & 0xFF000000) >> 24, (event->data.l[1] & 1),
92           event->data.l[0], event->data.l[1], event->data.l[2],
93           event->data.l[3], event->data.l[4]);
94
95     /* If the source supports more than 3 data types we retrieve
96      * the entire list. */
97     if (event->data.l[1] & 1)
98     {
99         Atom acttype;
100         int actfmt;
101         unsigned long bytesret;
102
103         /* Request supported formats from source window */
104         wine_tsx11_lock();
105         XGetWindowProperty(event->display, event->data.l[0], x11drv_atom(XdndTypeList),
106                            0, 65535, FALSE, AnyPropertyType, &acttype, &actfmt, &count,
107                            &bytesret, (unsigned char**)&xdndtypes);
108         wine_tsx11_unlock();
109     }
110     else
111     {
112         count = 3;
113         xdndtypes = (Atom*) &event->data.l[2];
114     }
115
116     if (TRACE_ON(xdnd))
117     {
118         unsigned int i = 0;
119
120         wine_tsx11_lock();
121         for (; i < count; i++)
122         {
123             if (xdndtypes[i] != 0)
124             {
125                 char * pn = XGetAtomName(event->display, xdndtypes[i]);
126                 TRACE("XDNDEnterAtom %ld: %s\n", xdndtypes[i], pn);
127                 XFree(pn);
128             }
129         }
130         wine_tsx11_unlock();
131     }
132
133     /* Do a one-time data read and cache results */
134     X11DRV_XDND_ResolveProperty(event->display, event->window,
135                                 event->data.l[1], xdndtypes, &count);
136
137     if (event->data.l[1] & 1)
138         XFree(xdndtypes);
139 }
140
141 /**************************************************************************
142  * X11DRV_XDND_PositionEvent
143  *
144  * Handle an XdndPosition event.
145  */
146 void X11DRV_XDND_PositionEvent( HWND hWnd, XClientMessageEvent *event )
147 {
148     XClientMessageEvent e;
149     int accept = 0; /* Assume we're not accepting */
150
151     XDNDxy.x = event->data.l[2] >> 16;
152     XDNDxy.y = event->data.l[2] & 0xFFFF;
153
154     /* FIXME: Notify OLE of DragEnter. Result determines if we accept */
155
156     if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)
157         accept = 1;
158
159     TRACE("action req: %ld accept(%d) at x(%d),y(%d)\n",
160           event->data.l[4], accept, XDNDxy.x, XDNDxy.y);
161
162     /*
163      * Let source know if we're accepting the drop by
164      * sending a status message.
165      */
166     e.type = ClientMessage;
167     e.display = event->display;
168     e.window = event->data.l[0];
169     e.message_type = x11drv_atom(XdndStatus);
170     e.format = 32;
171     e.data.l[0] = event->window;
172     e.data.l[1] = accept;
173     e.data.l[2] = 0; /* Empty Rect */
174     e.data.l[3] = 0; /* Empty Rect */
175     if (accept)
176         e.data.l[4] = event->data.l[4];
177     else
178         e.data.l[4] = None;
179     wine_tsx11_lock();
180     XSendEvent(event->display, event->data.l[0], False, NoEventMask, (XEvent*)&e);
181     wine_tsx11_unlock();
182
183     /* FIXME: if drag accepted notify OLE of DragOver */
184 }
185
186 /**************************************************************************
187  * X11DRV_XDND_DropEvent
188  *
189  * Handle an XdndDrop event.
190  */
191 void X11DRV_XDND_DropEvent( HWND hWnd, XClientMessageEvent *event )
192 {
193     XClientMessageEvent e;
194
195     TRACE("\n");
196
197     /* If we have a HDROP type we send a WM_ACCEPTFILES.*/
198     if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)
199         X11DRV_XDND_SendDropFiles( hWnd );
200
201     /* FIXME: Notify OLE of Drop */
202     X11DRV_XDND_FreeDragDropOp();
203
204     /* Tell the target we are finished. */
205     memset(&e, 0, sizeof(e));
206     e.type = ClientMessage;
207     e.display = event->display;
208     e.window = event->data.l[0];
209     e.message_type = x11drv_atom(XdndFinished);
210     e.format = 32;
211     e.data.l[0] = event->window;
212     wine_tsx11_lock();
213     XSendEvent(event->display, event->data.l[0], False, NoEventMask, (XEvent*)&e);
214     wine_tsx11_unlock();
215 }
216
217 /**************************************************************************
218  * X11DRV_XDND_LeaveEvent
219  *
220  * Handle an XdndLeave event.
221  */
222 void X11DRV_XDND_LeaveEvent( HWND hWnd, XClientMessageEvent *event )
223 {
224     TRACE("DND Operation canceled\n");
225
226     X11DRV_XDND_FreeDragDropOp();
227
228     /* FIXME: Notify OLE of DragLeave */
229 }
230
231
232 /**************************************************************************
233  * X11DRV_XDND_ResolveProperty
234  *
235  * Resolve all MIME types to windows clipboard formats. All data is cached.
236  */
237 static void X11DRV_XDND_ResolveProperty(Display *display, Window xwin, Time tm,
238                                         Atom *types, unsigned long *count)
239 {
240     unsigned int i, j;
241     BOOL res;
242     XEvent xe;
243     Atom acttype;
244     int actfmt;
245     unsigned long bytesret, icount;
246     int entries = 0;
247     unsigned char* data = NULL;
248
249     TRACE("count(%ld)\n", *count);
250
251     X11DRV_XDND_FreeDragDropOp(); /* Clear previously cached data */
252
253     for (i = 0; i < *count; i++)
254     {
255         TRACE("requesting atom %ld from xwin %ld\n", types[i], xwin);
256
257         if (types[i] == 0)
258             continue;
259
260         wine_tsx11_lock();
261         XConvertSelection(display, x11drv_atom(XdndSelection), types[i],
262                           x11drv_atom(XdndTarget), xwin, /*tm*/CurrentTime);
263         wine_tsx11_unlock();
264
265         /*
266          * Wait for SelectionNotify
267          */
268         for (j = 0; j < SELECTION_RETRIES; j++)
269         {
270             wine_tsx11_lock();
271             res = XCheckTypedWindowEvent(display, xwin, SelectionNotify, &xe);
272             wine_tsx11_unlock();
273             if (res && xe.xselection.selection == x11drv_atom(XdndSelection)) break;
274
275             usleep(SELECTION_WAIT);
276         }
277
278         if (xe.xselection.property == None)
279             continue;
280
281         wine_tsx11_lock();
282         XGetWindowProperty(display, xwin, x11drv_atom(XdndTarget), 0, 65535, FALSE,
283             AnyPropertyType, &acttype, &actfmt, &icount, &bytesret, &data);
284         wine_tsx11_unlock();
285
286         entries += X11DRV_XDND_MapFormat(types[i], data, icount * (actfmt / 8));
287         wine_tsx11_lock();
288         XFree(data);
289         wine_tsx11_unlock();
290     }
291
292     *count = entries;
293 }
294
295
296 /**************************************************************************
297  * X11DRV_XDND_InsertXDNDData
298  *
299  * Cache available XDND property
300  */
301 static void X11DRV_XDND_InsertXDNDData(int property, int format, void* data, unsigned int len)
302 {
303     LPXDNDDATA current = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(XDNDDATA));
304
305     if (current)
306     {
307         EnterCriticalSection(&xdnd_cs);
308         current->next = XDNDData;
309         current->cf_xdnd = property;
310         current->cf_win = format;
311         current->data = data;
312         current->size = len;
313         XDNDData = current;
314         LeaveCriticalSection(&xdnd_cs);
315     }
316 }
317
318
319 /**************************************************************************
320  * X11DRV_XDND_MapFormat
321  *
322  * Map XDND MIME format to windows clipboard format.
323  */
324 static int X11DRV_XDND_MapFormat(unsigned int property, unsigned char *data, int len)
325 {
326     void* xdata;
327     int count = 0;
328
329     TRACE("%d: %s\n", property, data);
330
331     /* Always include the raw type */
332     xdata = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len);
333     memcpy(xdata, data, len);
334     X11DRV_XDND_InsertXDNDData(property, property, xdata, len);
335     count++;
336
337     if (property == x11drv_atom(text_plain))
338         count += X11DRV_XDND_DeconstructTextPlain(property, data, len);
339     else if (property == x11drv_atom(text_html))
340         count += X11DRV_XDND_DeconstructTextHTML(property, data, len);
341
342     return count;
343 }
344
345
346 /**************************************************************************
347  * X11DRV_XDND_DeconstructTextPlain
348  *
349  * Interpret text/plain Data and add records to <dndfmt> linked list
350  */
351 static int X11DRV_XDND_DeconstructTextPlain(int property, void* data, int len)
352 {
353     char *p = (char*) data;
354     char* dostext;
355     int count = 0;
356
357     /* Always suppply plain text */
358     X11DRV_XDND_UnixToDos(&dostext, (char*)data, len);
359     X11DRV_XDND_InsertXDNDData(property, CF_TEXT, dostext, strlen(dostext));
360     count++;
361
362     TRACE("CF_TEXT (%d): %s\n", CF_TEXT, dostext);
363
364     /* Check for additional mappings */
365     while (*p != '\0' && *p != ':') /* Advance to end of protocol */
366         p++;
367
368     if (*p == ':')
369     {
370         if (!strncasecmp(data, "http", 4))
371         {
372             X11DRV_XDND_InsertXDNDData(property, RegisterClipboardFormatA("UniformResourceLocator"),
373                 strdup(dostext), strlen(dostext));
374                 count++;
375
376             TRACE("UniformResourceLocator: %s\n", dostext);
377         }
378         else if (!strncasecmp(data, "file", 4))
379         {
380             DROPFILES* pdf;
381
382             pdf = X11DRV_XDND_BuildDropFiles(p+1, len - 5, XDNDxy);
383             if (pdf)
384             {
385                 unsigned int size = HeapSize(GetProcessHeap(), 0, pdf);
386
387                 X11DRV_XDND_InsertXDNDData(property, CF_HDROP, pdf, size);
388                 count++;
389             }
390
391             TRACE("CF_HDROP: %p\n", pdf);
392         }
393     }
394
395     return count;
396 }
397
398
399 /**************************************************************************
400  * X11DRV_XDND_DeconstructTextHTML
401  *
402  * Interpret text/html data and add records to <dndfmt> linked list
403  */
404 static int X11DRV_XDND_DeconstructTextHTML(int property, void* data, int len)
405 {
406     char* dostext;
407
408     X11DRV_XDND_UnixToDos(&dostext, data, len);
409
410     X11DRV_XDND_InsertXDNDData(property,
411         RegisterClipboardFormatA("UniformResourceLocator"), dostext, strlen(dostext));
412
413     TRACE("UniformResourceLocator: %s\n", dostext);
414
415     return 1;
416 }
417
418
419 /**************************************************************************
420  * X11DRV_XDND_SendDropFiles
421  */
422 static void X11DRV_XDND_SendDropFiles(HWND hwnd)
423 {
424     LPXDNDDATA current;
425
426     EnterCriticalSection(&xdnd_cs);
427
428     current = XDNDData;
429
430     /* Find CF_HDROP type if any */
431     while (current != NULL)
432     {
433         if (current->cf_win == CF_HDROP)
434             break;
435         current = current->next;
436     }
437
438     if (current != NULL)
439     {
440         DROPFILES *lpDrop = (DROPFILES*) current->data;
441
442         if (lpDrop)
443         {
444             lpDrop->pt.x = XDNDxy.x;
445             lpDrop->pt.y = XDNDxy.y;
446
447             TRACE("Sending WM_DROPFILES: hWnd(0x%p) %p(%s)\n", hwnd,
448                 ((char*)lpDrop) + lpDrop->pFiles, ((char*)lpDrop) + lpDrop->pFiles);
449
450             PostMessageA(hwnd, WM_DROPFILES, (WPARAM)lpDrop, 0L);
451         }
452     }
453
454     LeaveCriticalSection(&xdnd_cs);
455 }
456
457
458 /**************************************************************************
459  * X11DRV_XDND_FreeDragDropOp
460  */
461 static void X11DRV_XDND_FreeDragDropOp(void)
462 {
463     LPXDNDDATA next;
464     LPXDNDDATA current;
465
466     TRACE("\n");
467
468     EnterCriticalSection(&xdnd_cs);
469
470     current = XDNDData;
471
472     /** Free data cache */
473     while (current != NULL)
474     {
475         next = current->next;
476         HeapFree(GetProcessHeap(), 0, current);
477         current = next;
478     }
479
480     XDNDData = NULL;
481     XDNDxy.x = XDNDxy.y = 0;
482
483     LeaveCriticalSection(&xdnd_cs);
484 }
485
486
487
488 /**************************************************************************
489  * X11DRV_XDND_BuildDropFiles
490  */
491 static DROPFILES* X11DRV_XDND_BuildDropFiles(char* filename, unsigned int len, POINT pt)
492 {
493     char* pfn;
494     int pathlen;
495     char path[MAX_PATH];
496     DROPFILES *lpDrop = NULL;
497
498     /* Advance to last starting slash */
499     pfn = filename + 1;
500     while (*pfn && (*pfn == '\\' || *pfn =='/'))
501     {
502         pfn++;
503         filename++;
504     }
505
506     /* Remove any trailing \r or \n */
507     while (*pfn)
508     {
509         if (*pfn == '\r' || *pfn == '\n')
510         {
511             *pfn = 0;
512             break;
513         }
514         pfn++;
515     }
516
517     TRACE("%s\n", filename);
518
519     pathlen = GetLongPathNameA(filename, path, MAX_PATH);
520     if (pathlen)
521     {
522         lpDrop = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DROPFILES) + pathlen + 1);
523
524         lpDrop->pFiles = sizeof(DROPFILES);
525         lpDrop->pt.x = pt.x;
526         lpDrop->pt.y = pt.y;
527         lpDrop->fNC = 0;
528         lpDrop->fWide = FALSE;
529
530         strcpy(((char*)lpDrop)+lpDrop->pFiles, path);
531     }
532
533     TRACE("resolved %s\n", lpDrop ? filename : NULL);
534
535     return lpDrop;
536 }
537
538
539 /**************************************************************************
540  * X11DRV_XDND_UnixToDos
541  */
542 static unsigned int X11DRV_XDND_UnixToDos(char** lpdest, char* lpsrc, int len)
543 {
544     int i;
545     unsigned int destlen, lines;
546
547     for (i = 0, lines = 0; i <= len; i++)
548     {
549         if (lpsrc[i] == '\n')
550             lines++;
551     }
552
553     destlen = len + lines + 1;
554
555     if (lpdest)
556     {
557         char* lpstr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, destlen);
558         for (i = 0, lines = 0; i <= len; i++)
559         {
560             if (lpsrc[i] == '\n')
561                 lpstr[++lines + i] = '\r';
562             lpstr[lines + i] = lpsrc[i];
563         }
564
565         *lpdest = lpstr;
566     }
567
568     return lines;
569 }