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