Add support for display of dibs on MSB XServers.
[wine/wine-kai.git] / dlls / x11drv / xdnd.c
blobc6309fda594f2560587f03bf1865fa633337c0e6
1 /*
2 * XDND handler code
4 * Copyright 2003 Ulrich Czekalla
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "config.h"
23 #include <string.h>
24 #ifdef HAVE_UNISTD_H
25 # include <unistd.h>
26 #endif
27 #include <stdarg.h>
29 #include "windef.h"
30 #include "winbase.h"
31 #include "wingdi.h"
32 #include "winuser.h"
33 #include "winerror.h"
34 #include "wownt32.h"
36 #include "x11drv.h"
37 #include "shlobj.h" /* DROPFILES */
39 #include "wine/debug.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(xdnd);
43 /* Maximum wait time for selection notify */
44 #define SELECTION_RETRIES 500 /* wait for .1 seconds */
45 #define SELECTION_WAIT 1000 /* us */
47 typedef struct tagXDNDDATA
49 int cf_win;
50 Atom cf_xdnd;
51 void *data;
52 unsigned int size;
53 struct tagXDNDDATA *next;
54 } XDNDDATA, *LPXDNDDATA;
56 static LPXDNDDATA XDNDData = NULL;
57 static POINT XDNDxy = { 0, 0 };
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);
70 static CRITICAL_SECTION xdnd_cs;
71 static CRITICAL_SECTION_DEBUG critsect_debug =
73 0, 0, &xdnd_cs,
74 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
75 0, 0, { 0, (DWORD)(__FILE__ ": xdnd_cs") }
77 static CRITICAL_SECTION xdnd_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
79 /**************************************************************************
80 * X11DRV_XDND_Event
82 * Entry point for X11 XDND events. Returns FALSE if event is not handled.
84 int X11DRV_XDND_Event(HWND hWnd, XClientMessageEvent *event)
86 int isXDNDMsg = 1;
88 TRACE("0x%p\n", hWnd);
90 if (event->message_type == x11drv_atom(XdndEnter))
92 Atom *xdndtypes;
93 unsigned long count = 0;
95 TRACE("XDNDEnter: ver(%ld) check-XdndTypeList(%ld) data=%ld,%ld,%ld,%ld,%ld\n",
96 (event->data.l[1] & 0xFF000000) >> 24, (event->data.l[1] & 1),
97 event->data.l[0], event->data.l[1], event->data.l[2],
98 event->data.l[3], event->data.l[4]);
100 /* If the source supports more than 3 data types we retrieve
101 * the entire list. */
102 if (event->data.l[1] & 1)
104 Atom acttype;
105 int actfmt;
106 unsigned long bytesret;
108 /* Request supported formats from source window */
109 wine_tsx11_lock();
110 XGetWindowProperty(event->display, event->data.l[0], x11drv_atom(XdndTypeList),
111 0, 65535, FALSE, AnyPropertyType, &acttype, &actfmt, &count,
112 &bytesret, (unsigned char**)&xdndtypes);
113 wine_tsx11_unlock();
115 else
117 count = 3;
118 xdndtypes = &event->data.l[2];
121 if (TRACE_ON(xdnd))
123 unsigned int i = 0;
125 wine_tsx11_lock();
126 for (; i < count; i++)
128 if (xdndtypes[i] != 0)
130 char * pn = XGetAtomName(event->display, xdndtypes[i]);
131 TRACE("XDNDEnterAtom %ld: %s\n", xdndtypes[i], pn);
132 XFree(pn);
135 wine_tsx11_unlock();
138 /* Do a one-time data read and cache results */
139 X11DRV_XDND_ResolveProperty(event->display, event->window,
140 event->data.l[1], xdndtypes, &count);
142 if (event->data.l[1] & 1)
143 XFree(xdndtypes);
145 else if (event->message_type == x11drv_atom(XdndPosition))
147 XClientMessageEvent e;
148 int accept = 0; /* Assume we're not accepting */
150 XDNDxy.x = event->data.l[2] >> 16;
151 XDNDxy.y = event->data.l[2] & 0xFFFF;
153 /* FIXME: Notify OLE of DragEnter. Result determines if we accept */
155 if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)
156 accept = 1;
158 TRACE("XDNDPosition. action req: %ld accept(%d) at x(%ld),y(%ld)\n",
159 event->data.l[4], accept, XDNDxy.x, XDNDxy.y);
162 * Let source know if we're accepting the drop by
163 * sending a status message.
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();
182 /* FIXME: if drag accepted notify OLE of DragOver */
184 else if (event->message_type == x11drv_atom(XdndDrop))
186 XClientMessageEvent e;
188 TRACE("XDNDDrop\n");
190 /* If we have a HDROP type we send a WM_ACCEPTFILES.*/
191 if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)
192 X11DRV_XDND_SendDropFiles( hWnd );
194 /* FIXME: Notify OLE of Drop */
195 X11DRV_XDND_FreeDragDropOp();
197 /* Tell the target we are finished. */
198 bzero(&e, sizeof(e));
199 e.type = ClientMessage;
200 e.display = event->display;
201 e.window = event->data.l[0];
202 e.message_type = x11drv_atom(XdndFinished);
203 e.format = 32;
204 e.data.l[0] = event->window;
205 wine_tsx11_lock();
206 XSendEvent(event->display, event->data.l[0], False, NoEventMask, (XEvent*)&e);
207 wine_tsx11_unlock();
209 else if (event->message_type == x11drv_atom(XdndLeave))
211 TRACE("DND Operation canceled\n");
213 X11DRV_XDND_FreeDragDropOp();
215 /* FIXME: Notify OLE of DragLeave */
217 else /* Not an XDND message */
218 isXDNDMsg = 0;
220 return isXDNDMsg;
224 /**************************************************************************
225 * X11DRV_XDND_ResolveProperty
227 * Resolve all MIME types to windows clipboard formats. All data is cached.
229 static void X11DRV_XDND_ResolveProperty(Display *display, Window xwin, Time tm,
230 Atom *types, unsigned long *count)
232 unsigned int i, j;
233 BOOL res;
234 XEvent xe;
235 Atom acttype;
236 int actfmt;
237 unsigned long bytesret, icount;
238 int entries = 0;
239 unsigned char* data = NULL;
241 TRACE("count(%ld)\n", *count);
243 X11DRV_XDND_FreeDragDropOp(); /* Clear previously cached data */
245 for (i = 0; i < *count; i++)
247 TRACE("requesting atom %ld from xwin %ld\n", types[i], xwin);
249 if (types[i] == 0)
250 continue;
252 wine_tsx11_lock();
253 XConvertSelection(display, x11drv_atom(XdndSelection), types[i],
254 x11drv_atom(XdndTarget), xwin, /*tm*/CurrentTime);
255 wine_tsx11_unlock();
258 * Wait for SelectionNotify
260 for (j = 0; j < SELECTION_RETRIES; j++)
262 wine_tsx11_lock();
263 res = XCheckTypedWindowEvent(display, xwin, SelectionNotify, &xe);
264 wine_tsx11_unlock();
265 if (res && xe.xselection.selection == x11drv_atom(XdndSelection)) break;
267 usleep(SELECTION_WAIT);
270 if (xe.xselection.property == None)
271 continue;
273 wine_tsx11_lock();
274 XGetWindowProperty(display, xwin, x11drv_atom(XdndTarget), 0, 65535, FALSE,
275 AnyPropertyType, &acttype, &actfmt, &icount, &bytesret, &data);
276 wine_tsx11_unlock();
278 entries += X11DRV_XDND_MapFormat(types[i], data, icount * (actfmt / 8));
279 wine_tsx11_lock();
280 XFree(data);
281 wine_tsx11_unlock();
284 *count = entries;
288 /**************************************************************************
289 * X11DRV_XDND_InsertXDNDData
291 * Cache available XDND property
293 static void X11DRV_XDND_InsertXDNDData(int property, int format, void* data, unsigned int len)
295 LPXDNDDATA current = (LPXDNDDATA) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(XDNDDATA));
297 if (current)
299 EnterCriticalSection(&xdnd_cs);
300 current->next = XDNDData;
301 current->cf_xdnd = property;
302 current->cf_win = format;
303 current->data = data;
304 current->size = len;
305 XDNDData = current;
306 LeaveCriticalSection(&xdnd_cs);
311 /**************************************************************************
312 * X11DRV_XDND_MapFormat
314 * Map XDND MIME format to windows clipboard format.
316 static int X11DRV_XDND_MapFormat(unsigned int property, unsigned char *data, int len)
318 void* xdata;
319 int count = 0;
321 TRACE("%d: %s\n", property, data);
323 /* Always include the raw type */
324 xdata = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len);
325 memcpy(xdata, data, len);
326 X11DRV_XDND_InsertXDNDData(property, property, xdata, len);
327 count++;
329 if (property == x11drv_atom(text_plain))
330 count += X11DRV_XDND_DeconstructTextPlain(property, data, len);
331 else if (property == x11drv_atom(text_html))
332 count += X11DRV_XDND_DeconstructTextHTML(property, data, len);
334 return count;
338 /**************************************************************************
339 * X11DRV_XDND_DeconstructTextPlain
341 * Interpret text/plain Data and add records to <dndfmt> linked list
343 static int X11DRV_XDND_DeconstructTextPlain(int property, void* data, int len)
345 char *p = (char*) data;
346 char* dostext;
347 int count = 0;
349 /* Always suppply plain text */
350 X11DRV_XDND_UnixToDos(&dostext, (char*)data, len);
351 X11DRV_XDND_InsertXDNDData(property, CF_TEXT, dostext, strlen(dostext));
352 count++;
354 TRACE("CF_TEXT (%d): %s\n", CF_TEXT, dostext);
356 /* Check for additional mappings */
357 while (*p != '\0' && *p != ':') /* Advance to end of protocol */
358 p++;
360 if (*p == ':')
362 if (!strncasecmp(data, "http", 4))
364 X11DRV_XDND_InsertXDNDData(property, RegisterClipboardFormatA("UniformResourceLocator"),
365 strdup(dostext), strlen(dostext));
366 count++;
368 TRACE("UniformResourceLocator: %s\n", dostext);
370 else if (!strncasecmp(data, "file", 4))
372 DROPFILES* pdf;
374 pdf = X11DRV_XDND_BuildDropFiles(p+1, len - 5, XDNDxy);
375 if (pdf)
377 unsigned int size = HeapSize(GetProcessHeap(), 0, pdf);
379 X11DRV_XDND_InsertXDNDData(property, CF_HDROP, pdf, size);
380 count++;
383 TRACE("CF_HDROP: %p\n", pdf);
387 return count;
391 /**************************************************************************
392 * X11DRV_XDND_DeconstructTextHTML
394 * Interpret text/html data and add records to <dndfmt> linked list
396 static int X11DRV_XDND_DeconstructTextHTML(int property, void* data, int len)
398 char* dostext;
400 X11DRV_XDND_UnixToDos(&dostext, data, len);
402 X11DRV_XDND_InsertXDNDData(property,
403 RegisterClipboardFormatA("UniformResourceLocator"), dostext, strlen(dostext));
405 TRACE("UniformResourceLocator: %s\n", dostext);
407 return 1;
411 /**************************************************************************
412 * X11DRV_XDND_SendDropFiles
414 static void X11DRV_XDND_SendDropFiles(HWND hwnd)
416 LPXDNDDATA current;
418 EnterCriticalSection(&xdnd_cs);
420 current = XDNDData;
422 /* Find CF_HDROP type if any */
423 while (current != NULL)
425 if (current->cf_win == CF_HDROP)
426 break;
427 current = current->next;
430 if (current != NULL)
432 DROPFILES *lpDrop = (DROPFILES*) current->data;
434 if (lpDrop)
436 lpDrop->pt.x = XDNDxy.x;
437 lpDrop->pt.y = XDNDxy.y;
439 TRACE("Sending WM_DROPFILES: hWnd(0x%p) %p(%s)\n", hwnd,
440 ((char*)lpDrop) + lpDrop->pFiles, ((char*)lpDrop) + lpDrop->pFiles);
442 PostMessageA(hwnd, WM_DROPFILES, (WPARAM)lpDrop, 0L);
446 LeaveCriticalSection(&xdnd_cs);
450 /**************************************************************************
451 * X11DRV_XDND_FreeDragDropOp
453 static void X11DRV_XDND_FreeDragDropOp(void)
455 LPXDNDDATA next;
456 LPXDNDDATA current;
458 TRACE("\n");
460 EnterCriticalSection(&xdnd_cs);
462 current = XDNDData;
464 /** Free data cache */
465 while (current != NULL)
467 next = current->next;
468 HeapFree(GetProcessHeap(), 0, current);
469 current = next;
472 XDNDData = NULL;
473 XDNDxy.x = XDNDxy.y = 0;
475 LeaveCriticalSection(&xdnd_cs);
480 /**************************************************************************
481 * X11DRV_XDND_BuildDropFiles
483 static DROPFILES* X11DRV_XDND_BuildDropFiles(char* filename, unsigned int len, POINT pt)
485 char* pfn;
486 int pathlen;
487 char path[MAX_PATH];
488 DROPFILES *lpDrop = NULL;
490 /* Advance to last starting slash */
491 pfn = filename + 1;
492 while (*pfn && (*pfn == '\\' || *pfn =='/'))
494 pfn++;
495 filename++;
498 /* Remove any trailing \r or \n */
499 while (*pfn)
501 if (*pfn == '\r' || *pfn == '\n')
503 *pfn = 0;
504 break;
506 pfn++;
509 TRACE("%s\n", filename);
511 pathlen = GetLongPathNameA(filename, path, MAX_PATH);
512 if (pathlen)
514 lpDrop = (DROPFILES*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
515 sizeof(DROPFILES) + pathlen + 1);
517 lpDrop->pFiles = sizeof(DROPFILES);
518 lpDrop->pt.x = pt.x;
519 lpDrop->pt.y = pt.y;
520 lpDrop->fNC = 0;
521 lpDrop->fWide = FALSE;
523 strcpy(((char*)lpDrop)+lpDrop->pFiles, path);
526 TRACE("resolved %s\n", lpDrop ? filename : NULL);
528 return lpDrop;
532 /**************************************************************************
533 * X11DRV_XDND_UnixToDos
535 static unsigned int X11DRV_XDND_UnixToDos(char** lpdest, char* lpsrc, int len)
537 int i;
538 unsigned int destlen, lines;
540 for (i = 0, lines = 0; i <= len; i++)
542 if (lpsrc[i] == '\n')
543 lines++;
546 destlen = len + lines + 1;
548 if (lpdest)
550 char* lpstr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, destlen);
551 for (i = 0, lines = 0; i <= len; i++)
553 if (lpsrc[i] == '\n')
554 lpstr[++lines + i] = '\r';
555 lpstr[lines + i] = lpsrc[i];
558 *lpdest = lpstr;
561 return lines;