Skip to content
This repository has been archived by the owner on Feb 11, 2021. It is now read-only.

Commit

Permalink
Browse files Browse the repository at this point in the history
XDnD implementation from Davey Taylor, need some cleanup
  • Loading branch information
slouken committed Mar 14, 2013
1 parent 4c02282 commit 04d53b2
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 3 deletions.
215 changes: 213 additions & 2 deletions src/video/x11/SDL_x11events.c
Expand Up @@ -41,6 +41,62 @@

#include <stdio.h>

typedef struct {
unsigned char *data;
int format, count;
Atom type;
} SDL_x11Prop;

/* Reads property
Must call XFree on results
*/
static void X11_ReadProperty(SDL_x11Prop *p, Display *disp, Window w, Atom prop)
{
unsigned char *ret=NULL;
Atom type;
int fmt;
unsigned long count;
unsigned long bytes_left;
int bytes_fetch = 0;

do {
if (ret != 0) XFree(ret);
XGetWindowProperty(disp, w, prop, 0, bytes_fetch, False, AnyPropertyType, &type, &fmt, &count, &bytes_left, &ret);
bytes_fetch += bytes_left;
} while (bytes_left != 0);

p->data=ret;
p->format=fmt;
p->count=count;
p->type=type;
}

/* Find text-uri-list in a list of targets and return it's atom
if available, else return None */
static Atom X11_PickTarget(Display *disp, Atom list[], int list_count)
{
Atom request = None;
char *name;
int i;
for (i=0; i < list_count && request == None; i++) {
name = XGetAtomName(disp, list[i]);
if (strcmp("text/uri-list", name)==0) request = list[i];
XFree(name);
}
return request;
}

/* Wrapper for X11_PickTarget for a maximum of three targets, a special
case in the Xdnd protocol */
static Atom X11_PickTargetFromAtoms(Display *disp, Atom a0, Atom a1, Atom a2)
{
int count=0;
Atom atom[3];
if (a0 != None) atom[count++] = a0;
if (a1 != None) atom[count++] = a1;
if (a2 != None) atom[count++] = a2;
return X11_PickTarget(disp, atom, count);
}
/*#define DEBUG_XEVENTS*/

/* Check to see if this is a repeated key.
Expand Down Expand Up @@ -92,6 +148,41 @@ static SDL_bool X11_IsWheelEvent(Display * display,XEvent * event,int * ticks)
return SDL_FALSE;
}

/* Convert URI to local filename
return filename if possible, else NULL
*/
static char* X11_URIToLocal(char* uri) {
char *file = NULL;

if (memcmp(uri,"file:/",6) == 0) uri += 6; /* local file? */
else if (strstr(uri,":/") != NULL) return file; /* wrong scheme */

SDL_bool local = uri[0] != '/' || ( uri[0] != '\0' && uri[1] == '/' );

/* got a hostname? */
if ( !local && uri[0] == '/' && uri[2] != '/' ) {
char* hostname_end = strchr( uri+1, '/' );
if ( hostname_end != NULL ) {
char hostname[ 257 ];
if ( gethostname( hostname, 255 ) == 0 ) {
hostname[ 256 ] = '\0';
if ( memcmp( uri+1, hostname, hostname_end - ( uri+1 )) == 0 ) {
uri = hostname_end + 1;
local = SDL_TRUE;
}
}
}
}
if ( local ) {
file = uri;
if ( uri[1] == '/' ) {
file++;
} else {
file--;
}
}
return file;
}

#if SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
static void X11_HandleGenericEvent(SDL_VideoData *videodata,XEvent event)
Expand Down Expand Up @@ -400,7 +491,68 @@ X11_DispatchEvent(_THIS)

/* Have we been requested to quit (or another client message?) */
case ClientMessage:{
if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) &&

int xdnd_version=0;

if (xevent.xclient.message_type == videodata->XdndEnter) {
SDL_bool use_list = xevent.xclient.data.l[1] & 1;
data->xdnd_source = xevent.xclient.data.l[0];
xdnd_version = ( xevent.xclient.data.l[1] >> 24);
if (use_list) {
/* fetch conversion targets */
SDL_x11Prop p;
X11_ReadProperty(&p, display, data->xdnd_source, videodata->XdndTypeList);
/* pick one */
data->xdnd_req = X11_PickTarget(display, (Atom*)p.data, p.count);
XFree(p.data);
} else {
/* pick from list of three */
data->xdnd_req = X11_PickTargetFromAtoms(display, xevent.xclient.data.l[2], xevent.xclient.data.l[3], xevent.xclient.data.l[4]);
}
}
else if (xevent.xclient.message_type == videodata->XdndPosition) {

/* reply with status */
XClientMessageEvent m;
memset(&m, 0, sizeof(XClientMessageEvent));
m.type = ClientMessage;
m.display = xevent.xclient.display;
m.window = xevent.xclient.data.l[0];
m.message_type = videodata->XdndStatus;
m.format=32;
m.data.l[0] = data->xwindow;
m.data.l[1] = (data->xdnd_req != None);
m.data.l[2] = 0; /* specify an empty rectangle */
m.data.l[3] = 0;
m.data.l[4] = videodata->XdndActionCopy; /* we only accept copying anyway */

XSendEvent(display, xevent.xclient.data.l[0], False, NoEventMask, (XEvent*)&m);
XFlush(display);
}
else if(xevent.xclient.message_type == videodata->XdndDrop) {
if (data->xdnd_req == None) {
/* say again - not interested! */
XClientMessageEvent m;
memset(&m, 0, sizeof(XClientMessageEvent));
m.type = ClientMessage;
m.display = xevent.xclient.display;
m.window = xevent.xclient.data.l[0];
m.message_type = videodata->XdndFinished;
m.format=32;
m.data.l[0] = data->xwindow;
m.data.l[1] = 0;
m.data.l[2] = None; /* fail! */
XSendEvent(display, xevent.xclient.data.l[0], False, NoEventMask, (XEvent*)&m);
} else {
/* convert */
if(xdnd_version >= 1) {
XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, xevent.xclient.data.l[2]);
} else {
XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, CurrentTime);
}
}
}
else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) &&
(xevent.xclient.format == 32) &&
(xevent.xclient.data.l[0] == videodata->_NET_WM_PING)) {
Window root = DefaultRootWindow(display);
Expand Down Expand Up @@ -601,7 +753,66 @@ X11_DispatchEvent(_THIS)
printf("window %p: SelectionNotify (requestor = %ld, target = %ld)\n", data,
xevent.xselection.requestor, xevent.xselection.target);
#endif
videodata->selection_waiting = SDL_FALSE;
Atom target = xevent.xselection.target;
if (target == data->xdnd_req) {

/* read data */
SDL_x11Prop p;
X11_ReadProperty(&p, display, data->xwindow, videodata->PRIMARY);

if(p.format==8) {
SDL_bool expect_lf = SDL_FALSE;
char *start = NULL;
char *scan = (char*)p.data;
char *fn;
char *uri;
int length = 0;
while (p.count--) {
if (!expect_lf) {
if (*scan==0x0D) {
expect_lf = SDL_TRUE;
} else if(start == NULL) {
start = scan;
length = 0;
}
length++;
} else {
if (*scan==0x0A && length>0) {
uri = malloc(length--);
memcpy(uri, start, length);
uri[length] = 0;
fn = X11_URIToLocal(uri);
if (fn) SDL_SendDropFile(fn);
free(uri);
}
expect_lf = SDL_FALSE;
start = NULL;
}
scan++;
}
}

XFree(p.data);

/* send reply */
XClientMessageEvent m;
memset(&m, 0, sizeof(XClientMessageEvent));
m.type = ClientMessage;
m.display = display;
m.window = data->xdnd_source;
m.message_type = videodata->XdndFinished;
m.format=32;
m.data.l[0] = data->xwindow;
m.data.l[1] = 1;
m.data.l[2] = videodata->XdndActionCopy;
XSendEvent(display, data->xdnd_source, False, NoEventMask, (XEvent*)&m);

XSync(display, False);

} else {
videodata->selection_waiting = SDL_FALSE;
}

}
break;

Expand Down
9 changes: 9 additions & 0 deletions src/video/x11/SDL_x11video.c
Expand Up @@ -524,6 +524,15 @@ X11_VideoInit(_THIS)
GET_ATOM(_NET_WM_PING);
GET_ATOM(_NET_ACTIVE_WINDOW);
GET_ATOM(UTF8_STRING);
GET_ATOM(PRIMARY);
GET_ATOM(XdndEnter);
GET_ATOM(XdndPosition);
GET_ATOM(XdndStatus);
GET_ATOM(XdndTypeList);
GET_ATOM(XdndActionCopy);
GET_ATOM(XdndDrop);
GET_ATOM(XdndFinished);
GET_ATOM(XdndSelection);

/* Detect the window manager */
X11_CheckWindowManager(_this);
Expand Down
11 changes: 10 additions & 1 deletion src/video/x11/SDL_x11video.h
Expand Up @@ -101,7 +101,16 @@ typedef struct SDL_VideoData
Atom _NET_WM_PING;
Atom _NET_ACTIVE_WINDOW;
Atom UTF8_STRING;

Atom PRIMARY;
Atom XdndEnter;
Atom XdndPosition;
Atom XdndStatus;
Atom XdndTypeList;
Atom XdndActionCopy;
Atom XdndDrop;
Atom XdndFinished;
Atom XdndSelection;

SDL_Scancode key_layout[256];
SDL_bool selection_waiting;

Expand Down
6 changes: 6 additions & 0 deletions src/video/x11/SDL_x11window.c
Expand Up @@ -344,6 +344,7 @@ X11_CreateWindow(_THIS, SDL_Window * window)
Atom _NET_WM_WINDOW_TYPE;
Atom _NET_WM_WINDOW_TYPE_NORMAL;
Atom _NET_WM_PID;
Atom XdndAware, xdnd_version = 5;
Uint32 fevent = 0;

#if SDL_VIDEO_OPENGL_GLX || SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2
Expand Down Expand Up @@ -567,6 +568,11 @@ X11_CreateWindow(_THIS, SDL_Window * window)
PropertyChangeMask | StructureNotifyMask |
KeymapStateMask | fevent));

XdndAware = XInternAtom(display, "XdndAware", False);
XChangeProperty(display, w, XdndAware, XA_ATOM, 32,
PropModeReplace,
(unsigned char*)&xdnd_version, 1);

XFlush(display);

return 0;
Expand Down
2 changes: 2 additions & 0 deletions src/video/x11/SDL_x11window.h
Expand Up @@ -57,6 +57,8 @@ typedef struct
Uint32 pending_focus_time;
XConfigureEvent last_xconfigure;
struct SDL_VideoData *videodata;
Atom xdnd_req;
Window xdnd_source;
} SDL_WindowData;

extern void X11_SetNetWMState(_THIS, Window xwindow, Uint32 flags);
Expand Down

0 comments on commit 04d53b2

Please sign in to comment.