Initial implementation to support XDND protocol.
diff --git a/dlls/x11drv/Makefile.in b/dlls/x11drv/Makefile.in
index dc24c19..481b00b 100644
--- a/dlls/x11drv/Makefile.in
+++ b/dlls/x11drv/Makefile.in
@@ -35,6 +35,7 @@
winpos.c \
x11ddraw.c \
x11drv_main.c \
+ xdnd.c \
xrandr.c \
xrender.c \
xvidmode.c
diff --git a/dlls/x11drv/event.c b/dlls/x11drv/event.c
index 1d2d490..67cb16e 100644
--- a/dlls/x11drv/event.c
+++ b/dlls/x11drv/event.c
@@ -1227,7 +1227,8 @@
else if (event->data.l[0] == DndURL)
EVENT_DropURLs(hWnd, event);
}
- else {
+ else if (!X11DRV_XDND_Event(hWnd, event))
+ {
#if 0
/* enable this if you want to see the message */
unsigned char* p_data = NULL;
diff --git a/dlls/x11drv/window.c b/dlls/x11drv/window.c
index 77e1f2e..0d33423 100644
--- a/dlls/x11drv/window.c
+++ b/dlls/x11drv/window.c
@@ -81,7 +81,24 @@
"_NET_WM_PID",
"_NET_WM_PING",
"_NET_WM_NAME",
+ "XdndAware",
+ "XdndEnter",
+ "XdndPosition",
+ "XdndStatus",
+ "XdndLeave",
+ "XdndFinished",
+ "XdndDrop",
+ "XdndActionCopy",
+ "XdndActionMove",
+ "XdndActionLink",
+ "XdndActionAsk",
+ "XdndActionPrivate",
+ "XdndSelection",
+ "XdndTarget",
+ "XdndTypeList",
"image/gif",
+ "text/html",
+ "text/plain",
"text/rtf",
"text/richtext"
};
@@ -356,6 +373,7 @@
XWMHints* wm_hints;
Atom protocols[3];
MwmHints mwm_hints;
+ Atom dndVersion = 4;
int i;
wine_tsx11_lock();
@@ -428,6 +446,9 @@
x11drv_atom(_MOTIF_WM_HINTS), 32, PropModeReplace,
(char*)&mwm_hints, sizeof(mwm_hints)/sizeof(long) );
+ XChangeProperty( display, data->whole_window, x11drv_atom(XdndAware),
+ XA_ATOM, 32, PropModeReplace, (unsigned char*)&dndVersion, 1 );
+
wm_hints = XAllocWMHints();
wine_tsx11_unlock();
diff --git a/dlls/x11drv/x11drv.h b/dlls/x11drv/x11drv.h
index 3b0ecb3..245790a 100644
--- a/dlls/x11drv/x11drv.h
+++ b/dlls/x11drv/x11drv.h
@@ -231,6 +231,8 @@
extern void X11DRV_OpenGL_Init(Display *display);
extern XVisualInfo *X11DRV_setup_opengl_visual(Display *display);
+extern int X11DRV_XDND_Event(HWND hWnd, XClientMessageEvent *event);
+
/* exported dib functions for now */
/* Additional info for DIB section objects */
@@ -390,7 +392,24 @@
XATOM__NET_WM_PID,
XATOM__NET_WM_PING,
XATOM__NET_WM_NAME,
+ XATOM_XdndAware,
+ XATOM_XdndEnter,
+ XATOM_XdndPosition,
+ XATOM_XdndStatus,
+ XATOM_XdndLeave,
+ XATOM_XdndFinished,
+ XATOM_XdndDrop,
+ XATOM_XdndActionCopy,
+ XATOM_XdndActionMove,
+ XATOM_XdndActionLink,
+ XATOM_XdndActionAsk,
+ XATOM_XdndActionPrivate,
+ XATOM_XdndSelection,
+ XATOM_XdndTarget,
+ XATOM_XdndTypeList,
XATOM_image_gif,
+ XATOM_text_html,
+ XATOM_text_plain,
XATOM_text_rtf,
XATOM_text_richtext,
NB_XATOMS
diff --git a/dlls/x11drv/xdnd.c b/dlls/x11drv/xdnd.c
new file mode 100644
index 0000000..2f50d4f
--- /dev/null
+++ b/dlls/x11drv/xdnd.c
@@ -0,0 +1,559 @@
+/*
+ * XDND handler code
+ *
+ * Copyright 2003 Ulrich Czekalla
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+
+#include "ts_xlib.h"
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winerror.h"
+#include "wownt32.h"
+
+#include "x11drv.h"
+#include "shlobj.h" /* DROPFILES */
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(xdnd);
+
+/* Maximum wait time for selection notify */
+#define SELECTION_RETRIES 500 /* wait for .1 seconds */
+#define SELECTION_WAIT 1000 /* us */
+
+typedef struct tagXDNDDATA
+{
+ int cf_win;
+ Atom cf_xdnd;
+ void *data;
+ unsigned int size;
+ struct tagXDNDDATA *next;
+} XDNDDATA, *LPXDNDDATA;
+
+static LPXDNDDATA XDNDData = NULL;
+static POINT XDNDxy = { 0, 0 };
+
+static void X11DRV_XDND_InsertXDNDData(int property, int format, void* data, unsigned int len);
+static int X11DRV_XDND_DeconstructTextPlain(int property, void* data, int len);
+static int X11DRV_XDND_DeconstructTextHTML(int property, void* data, int len);
+static int X11DRV_XDND_MapFormat(unsigned int property, unsigned char *data, int len);
+static void X11DRV_XDND_ResolveProperty(Display *display, Window xwin, Time tm,
+ Atom *types, unsigned long *count);
+static void X11DRV_XDND_SendDropFiles(HWND hwnd);
+static void X11DRV_XDND_FreeDragDropOp(void);
+static unsigned int X11DRV_XDND_UnixToDos(char** lpdest, char* lpsrc, int len);
+static DROPFILES* X11DRV_XDND_BuildDropFiles(char* filename, unsigned int len, POINT pt);
+
+static CRITICAL_SECTION xdnd_cs;
+static CRITICAL_SECTION_DEBUG critsect_debug =
+{
+ 0, 0, &xdnd_cs,
+ { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
+ 0, 0, { 0, (DWORD)(__FILE__ ": xdnd_cs") }
+};
+static CRITICAL_SECTION xdnd_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
+
+/**************************************************************************
+ * X11DRV_XDND_Event
+ *
+ * Entry point for X11 XDND events. Returns FALSE if event is not handled.
+ */
+int X11DRV_XDND_Event(HWND hWnd, XClientMessageEvent *event)
+{
+ int isXDNDMsg = 1;
+
+ TRACE("0x%p\n", hWnd);
+
+ if (event->message_type == x11drv_atom(XdndEnter))
+ {
+ Atom *xdndtypes;
+ unsigned long count = 0;
+
+ TRACE("XDNDEnter: ver(%ld) check-XdndTypeList(%ld) data=%ld,%ld,%ld,%ld,%ld\n",
+ (event->data.l[1] & 0xFF000000) >> 24, (event->data.l[1] & 1),
+ event->data.l[0], event->data.l[1], event->data.l[2],
+ event->data.l[3], event->data.l[4]);
+
+ /* If the source supports more than 3 data types we retrieve
+ * the entire list. */
+ if (event->data.l[1] & 1)
+ {
+ Atom acttype;
+ int actfmt;
+ unsigned long bytesret;
+
+ /* Request supported formats from source window */
+ wine_tsx11_lock();
+ XGetWindowProperty(event->display, event->data.l[0], x11drv_atom(XdndTypeList),
+ 0, 65535, FALSE, AnyPropertyType, &acttype, &actfmt, &count,
+ &bytesret, (unsigned char**)&xdndtypes);
+ wine_tsx11_unlock();
+ }
+ else
+ {
+ count = 3;
+ xdndtypes = &event->data.l[2];
+ }
+
+ if (TRACE_ON(xdnd))
+ {
+ unsigned int i = 0;
+
+ for (; i < count; i++)
+ {
+ if (xdndtypes[i] != 0)
+ {
+ char * pn = TSXGetAtomName(event->display, xdndtypes[i]);
+ TRACE("XDNDEnterAtom %ld: %s\n", xdndtypes[i], pn);
+ TSXFree(pn);
+ }
+ }
+ }
+
+ /* Do a one-time data read and cache results */
+ X11DRV_XDND_ResolveProperty(event->display, event->window,
+ event->data.l[1], xdndtypes, &count);
+
+ if (event->data.l[1] & 1)
+ XFree(xdndtypes);
+ }
+ else if (event->message_type == x11drv_atom(XdndPosition))
+ {
+ XClientMessageEvent e;
+ int accept = 0; /* Assume we're not accepting */
+
+ XDNDxy.x = event->data.l[2] >> 16;
+ XDNDxy.y = event->data.l[2] & 0xFFFF;
+
+ /* FIXME: Notify OLE of DragEnter. Result determines if we accept */
+
+ if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)
+ accept = 1;
+
+ TRACE("XDNDPosition. action req: %ld accept(%d) at x(%ld),y(%ld)\n",
+ event->data.l[4], accept, XDNDxy.x, XDNDxy.y);
+
+ /*
+ * Let source know if we're accepting the drop by
+ * sending a status message.
+ */
+ e.type = ClientMessage;
+ e.display = event->display;
+ e.window = event->data.l[0];
+ e.message_type = x11drv_atom(XdndStatus);
+ e.format = 32;
+ e.data.l[0] = event->window;
+ e.data.l[1] = accept;
+ e.data.l[2] = 0; /* Empty Rect */
+ e.data.l[3] = 0; /* Empty Rect */
+ if (accept)
+ e.data.l[4] = event->data.l[4];
+ else
+ e.data.l[4] = None;
+ wine_tsx11_lock();
+ XSendEvent(event->display, event->data.l[0], False, NoEventMask, (XEvent*)&e);
+ wine_tsx11_unlock();
+
+ /* FIXME: if drag accepted notify OLE of DragOver */
+ }
+ else if (event->message_type == x11drv_atom(XdndDrop))
+ {
+ XClientMessageEvent e;
+
+ TRACE("XDNDDrop\n");
+
+ /* If we have a HDROP type we send a WM_ACCEPTFILES.*/
+ if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES)
+ X11DRV_XDND_SendDropFiles( hWnd );
+
+ /* FIXME: Notify OLE of Drop */
+ X11DRV_XDND_FreeDragDropOp();
+
+ /* Tell the target we are finished. */
+ bzero(&e, sizeof(e));
+ e.type = ClientMessage;
+ e.display = event->display;
+ e.window = event->data.l[0];
+ e.message_type = x11drv_atom(XdndFinished);
+ e.format = 32;
+ e.data.l[0] = event->window;
+ wine_tsx11_lock();
+ XSendEvent(event->display, event->data.l[0], False, NoEventMask, (XEvent*)&e);
+ wine_tsx11_unlock();
+ }
+ else if (event->message_type == x11drv_atom(XdndLeave))
+ {
+ TRACE("DND Operation canceled\n");
+
+ X11DRV_XDND_FreeDragDropOp();
+
+ /* FIXME: Notify OLE of DragLeave */
+ }
+ else /* Not an XDND message */
+ isXDNDMsg = 0;
+
+ return isXDNDMsg;
+}
+
+
+/**************************************************************************
+ * X11DRV_XDND_ResolveProperty
+ *
+ * Resolve all MIME types to windows clipboard formats. All data is cached.
+ */
+static void X11DRV_XDND_ResolveProperty(Display *display, Window xwin, Time tm,
+ Atom *types, unsigned long *count)
+{
+ unsigned int i, j;
+ BOOL res;
+ XEvent xe;
+ Atom acttype;
+ int actfmt;
+ unsigned long bytesret, icount;
+ int entries = 0;
+ unsigned char* data = NULL;
+
+ TRACE("count(%ld)\n", *count);
+
+ X11DRV_XDND_FreeDragDropOp(); /* Clear previously cached data */
+
+ for (i = 0; i < *count; i++)
+ {
+ TRACE("requesting atom %ld from xwin %ld\n", types[i], xwin);
+
+ if (types[i] == 0)
+ continue;
+
+ wine_tsx11_lock();
+ XConvertSelection(display, x11drv_atom(XdndSelection), types[i],
+ x11drv_atom(XdndTarget), xwin, /*tm*/CurrentTime);
+ wine_tsx11_unlock();
+
+ /*
+ * Wait for SelectionNotify
+ */
+ for (j = 0; j < SELECTION_RETRIES; j++)
+ {
+ wine_tsx11_lock();
+ res = XCheckTypedWindowEvent(display, xwin, SelectionNotify, &xe);
+ wine_tsx11_unlock();
+ if (res && xe.xselection.selection == x11drv_atom(XdndSelection)) break;
+
+ usleep(SELECTION_WAIT);
+ }
+
+ if (xe.xselection.property == None)
+ continue;
+
+ wine_tsx11_lock();
+ XGetWindowProperty(display, xwin, x11drv_atom(XdndTarget), 0, 65535, FALSE,
+ AnyPropertyType, &acttype, &actfmt, &icount, &bytesret, &data);
+ wine_tsx11_unlock();
+
+ entries += X11DRV_XDND_MapFormat(types[i], data, icount * (actfmt / 8));
+ TSXFree(data);
+ }
+
+ *count = entries;
+}
+
+
+/**************************************************************************
+ * X11DRV_XDND_InsertXDNDData
+ *
+ * Cache available XDND property
+ */
+static void X11DRV_XDND_InsertXDNDData(int property, int format, void* data, unsigned int len)
+{
+ LPXDNDDATA current = (LPXDNDDATA) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(XDNDDATA));
+
+ if (current)
+ {
+ EnterCriticalSection(&xdnd_cs);
+ current->next = XDNDData;
+ current->cf_xdnd = property;
+ current->cf_win = format;
+ current->data = data;
+ current->size = len;
+ XDNDData = current;
+ LeaveCriticalSection(&xdnd_cs);
+ }
+}
+
+
+/**************************************************************************
+ * X11DRV_XDND_MapFormat
+ *
+ * Map XDND MIME format to windows clipboard format.
+ */
+static int X11DRV_XDND_MapFormat(unsigned int property, unsigned char *data, int len)
+{
+ void* xdata;
+ int count = 0;
+
+ TRACE("%d: %s\n", property, data);
+
+ /* Always include the raw type */
+ xdata = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len);
+ memcpy(xdata, data, len);
+ X11DRV_XDND_InsertXDNDData(property, property, xdata, len);
+ count++;
+
+ if (property == x11drv_atom(text_plain))
+ count += X11DRV_XDND_DeconstructTextPlain(property, data, len);
+ else if (property == x11drv_atom(text_html))
+ count += X11DRV_XDND_DeconstructTextHTML(property, data, len);
+
+ return count;
+}
+
+
+/**************************************************************************
+ * X11DRV_XDND_DeconstructTextPlain
+ *
+ * Interpret text/plain Data and add records to <dndfmt> linked list
+ */
+static int X11DRV_XDND_DeconstructTextPlain(int property, void* data, int len)
+{
+ char *p = (char*) data;
+ char* dostext;
+ int count = 0;
+
+ /* Always suppply plain text */
+ X11DRV_XDND_UnixToDos(&dostext, (char*)data, len);
+ X11DRV_XDND_InsertXDNDData(property, CF_TEXT, dostext, strlen(dostext));
+ count++;
+
+ TRACE("CF_TEXT (%d): %s\n", CF_TEXT, dostext);
+
+ /* Check for additional mappings */
+ while (*p != '\0' && *p != ':') /* Advance to end of protocol */
+ p++;
+
+ if (*p == ':')
+ {
+ if (!strncasecmp(data, "http", 4))
+ {
+ X11DRV_XDND_InsertXDNDData(property, RegisterClipboardFormatA("UniformResourceLocator"),
+ strdup(dostext), strlen(dostext));
+ count++;
+
+ TRACE("UniformResourceLocator: %s\n", dostext);
+ }
+ else if (!strncasecmp(data, "file", 4))
+ {
+ DROPFILES* pdf;
+
+ pdf = X11DRV_XDND_BuildDropFiles(p+1, len - 5, XDNDxy);
+ if (pdf)
+ {
+ unsigned int size = HeapSize(GetProcessHeap(), 0, pdf);
+
+ X11DRV_XDND_InsertXDNDData(property, CF_HDROP, pdf, size);
+ count++;
+ }
+
+ TRACE("CF_HDROP: %p\n", pdf);
+ }
+ }
+
+ return count;
+}
+
+
+/**************************************************************************
+ * X11DRV_XDND_DeconstructTextHTML
+ *
+ * Interpret text/html data and add records to <dndfmt> linked list
+ */
+static int X11DRV_XDND_DeconstructTextHTML(int property, void* data, int len)
+{
+ char* dostext;
+
+ X11DRV_XDND_UnixToDos(&dostext, data, len);
+
+ X11DRV_XDND_InsertXDNDData(property,
+ RegisterClipboardFormatA("UniformResourceLocator"), dostext, strlen(dostext));
+
+ TRACE("UniformResourceLocator: %s\n", dostext);
+
+ return 1;
+}
+
+
+/**************************************************************************
+ * X11DRV_XDND_SendDropFiles
+ */
+static void X11DRV_XDND_SendDropFiles(HWND hwnd)
+{
+ LPXDNDDATA current;
+
+ EnterCriticalSection(&xdnd_cs);
+
+ current = XDNDData;
+
+ /* Find CF_HDROP type if any */
+ while (current != NULL)
+ {
+ if (current->cf_win == CF_HDROP)
+ break;
+ current = current->next;
+ }
+
+ if (current != NULL)
+ {
+ DROPFILES *lpDrop = (DROPFILES*) current->data;
+
+ if (lpDrop)
+ {
+ lpDrop->pt.x = XDNDxy.x;
+ lpDrop->pt.y = XDNDxy.y;
+
+ TRACE("Sending WM_DROPFILES: hWnd(0x%p) %p(%s)\n", hwnd,
+ ((char*)lpDrop) + lpDrop->pFiles, ((char*)lpDrop) + lpDrop->pFiles);
+
+ PostMessageA(hwnd, WM_DROPFILES, (WPARAM)lpDrop, 0L);
+ }
+ }
+
+ LeaveCriticalSection(&xdnd_cs);
+}
+
+
+/**************************************************************************
+ * X11DRV_XDND_FreeDragDropOp
+ */
+static void X11DRV_XDND_FreeDragDropOp(void)
+{
+ LPXDNDDATA next;
+ LPXDNDDATA current;
+
+ TRACE("\n");
+
+ EnterCriticalSection(&xdnd_cs);
+
+ current = XDNDData;
+
+ /** Free data cache */
+ while (current != NULL)
+ {
+ next = current->next;
+ HeapFree(GetProcessHeap(), 0, current);
+ current = next;
+ }
+
+ XDNDData = NULL;
+ XDNDxy.x = XDNDxy.y = 0;
+
+ LeaveCriticalSection(&xdnd_cs);
+}
+
+
+
+/**************************************************************************
+ * X11DRV_XDND_BuildDropFiles
+ */
+static DROPFILES* X11DRV_XDND_BuildDropFiles(char* filename, unsigned int len, POINT pt)
+{
+ char* pfn;
+ int pathlen;
+ char path[MAX_PATH];
+ DROPFILES *lpDrop = NULL;
+
+ /* Advance to last starting slash */
+ pfn = filename + 1;
+ while (*pfn && (*pfn == '\\' || *pfn =='/'))
+ {
+ pfn++;
+ filename++;
+ }
+
+ /* Remove any trailing \r or \n */
+ while (*pfn)
+ {
+ if (*pfn == '\r' || *pfn == '\n')
+ {
+ *pfn = 0;
+ break;
+ }
+ pfn++;
+ }
+
+ TRACE("%s\n", filename);
+
+ pathlen = GetLongPathNameA(filename, path, MAX_PATH);
+ if (pathlen)
+ {
+ lpDrop = (DROPFILES*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
+ sizeof(DROPFILES) + pathlen + 1);
+
+ lpDrop->pFiles = sizeof(DROPFILES);
+ lpDrop->pt.x = pt.x;
+ lpDrop->pt.y = pt.y;
+ lpDrop->fNC = 0;
+ lpDrop->fWide = FALSE;
+
+ strcpy(((char*)lpDrop)+lpDrop->pFiles, path);
+ }
+
+ TRACE("resolved %s\n", lpDrop ? filename : NULL);
+
+ return lpDrop;
+}
+
+
+/**************************************************************************
+ * X11DRV_XDND_UnixToDos
+ */
+static unsigned int X11DRV_XDND_UnixToDos(char** lpdest, char* lpsrc, int len)
+{
+ int i;
+ unsigned int destlen, lines;
+
+ for (i = 0, lines = 0; i <= len; i++)
+ {
+ if (lpsrc[i] == '\n')
+ lines++;
+ }
+
+ destlen = len + lines + 1;
+
+ if (lpdest)
+ {
+ char* lpstr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, destlen);
+ for (i = 0, lines = 0; i <= len; i++)
+ {
+ if (lpsrc[i] == '\n')
+ lpstr[++lines + i] = '\r';
+ lpstr[lines + i] = lpsrc[i];
+ }
+
+ *lpdest = lpstr;
+ }
+
+ return lines;
+}