Initial implementation to support XDND protocol.

This commit is contained in:
Ulrich Czekalla 2003-11-20 22:09:51 +00:00 committed by Alexandre Julliard
parent 5a2f3c1f1f
commit 032bdc9f1d
5 changed files with 602 additions and 1 deletions

View File

@ -35,6 +35,7 @@ C_SRCS = \
winpos.c \ winpos.c \
x11ddraw.c \ x11ddraw.c \
x11drv_main.c \ x11drv_main.c \
xdnd.c \
xrandr.c \ xrandr.c \
xrender.c \ xrender.c \
xvidmode.c xvidmode.c

View File

@ -1227,7 +1227,8 @@ static void EVENT_ClientMessage( HWND hWnd, XClientMessageEvent *event )
else if (event->data.l[0] == DndURL) else if (event->data.l[0] == DndURL)
EVENT_DropURLs(hWnd, event); EVENT_DropURLs(hWnd, event);
} }
else { else if (!X11DRV_XDND_Event(hWnd, event))
{
#if 0 #if 0
/* enable this if you want to see the message */ /* enable this if you want to see the message */
unsigned char* p_data = NULL; unsigned char* p_data = NULL;

View File

@ -81,7 +81,24 @@ static const char * const atom_names[NB_XATOMS - FIRST_XATOM] =
"_NET_WM_PID", "_NET_WM_PID",
"_NET_WM_PING", "_NET_WM_PING",
"_NET_WM_NAME", "_NET_WM_NAME",
"XdndAware",
"XdndEnter",
"XdndPosition",
"XdndStatus",
"XdndLeave",
"XdndFinished",
"XdndDrop",
"XdndActionCopy",
"XdndActionMove",
"XdndActionLink",
"XdndActionAsk",
"XdndActionPrivate",
"XdndSelection",
"XdndTarget",
"XdndTypeList",
"image/gif", "image/gif",
"text/html",
"text/plain",
"text/rtf", "text/rtf",
"text/richtext" "text/richtext"
}; };
@ -356,6 +373,7 @@ void X11DRV_set_wm_hints( Display *display, WND *win )
XWMHints* wm_hints; XWMHints* wm_hints;
Atom protocols[3]; Atom protocols[3];
MwmHints mwm_hints; MwmHints mwm_hints;
Atom dndVersion = 4;
int i; int i;
wine_tsx11_lock(); wine_tsx11_lock();
@ -428,6 +446,9 @@ void X11DRV_set_wm_hints( Display *display, WND *win )
x11drv_atom(_MOTIF_WM_HINTS), 32, PropModeReplace, x11drv_atom(_MOTIF_WM_HINTS), 32, PropModeReplace,
(char*)&mwm_hints, sizeof(mwm_hints)/sizeof(long) ); (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(); wm_hints = XAllocWMHints();
wine_tsx11_unlock(); wine_tsx11_unlock();

View File

@ -231,6 +231,8 @@ extern void X11DRV_XRender_UpdateDrawable(X11DRV_PDEVICE *physDev);
extern void X11DRV_OpenGL_Init(Display *display); extern void X11DRV_OpenGL_Init(Display *display);
extern XVisualInfo *X11DRV_setup_opengl_visual(Display *display); extern XVisualInfo *X11DRV_setup_opengl_visual(Display *display);
extern int X11DRV_XDND_Event(HWND hWnd, XClientMessageEvent *event);
/* exported dib functions for now */ /* exported dib functions for now */
/* Additional info for DIB section objects */ /* Additional info for DIB section objects */
@ -390,7 +392,24 @@ enum x11drv_atoms
XATOM__NET_WM_PID, XATOM__NET_WM_PID,
XATOM__NET_WM_PING, XATOM__NET_WM_PING,
XATOM__NET_WM_NAME, 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_image_gif,
XATOM_text_html,
XATOM_text_plain,
XATOM_text_rtf, XATOM_text_rtf,
XATOM_text_richtext, XATOM_text_richtext,
NB_XATOMS NB_XATOMS

559
dlls/x11drv/xdnd.c Normal file
View File

@ -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;
}