XDnD implementation from Davey Taylor, need some cleanup
authorSam Lantinga <slouken@libsdl.org>
Wed, 13 Mar 2013 21:41:43 -0700
changeset 7001ccc0d3207639
parent 7000 1afa71309a0e
child 7002 978ff4e06388
XDnD implementation from Davey Taylor, need some cleanup
src/video/x11/SDL_x11events.c
src/video/x11/SDL_x11video.c
src/video/x11/SDL_x11video.h
src/video/x11/SDL_x11window.c
src/video/x11/SDL_x11window.h
     1.1 --- a/src/video/x11/SDL_x11events.c	Wed Mar 13 09:14:45 2013 -0700
     1.2 +++ b/src/video/x11/SDL_x11events.c	Wed Mar 13 21:41:43 2013 -0700
     1.3 @@ -41,6 +41,62 @@
     1.4  
     1.5  #include <stdio.h>
     1.6  
     1.7 +typedef struct {
     1.8 +	unsigned char *data;
     1.9 +	int format, count;
    1.10 +	Atom type;
    1.11 +} SDL_x11Prop;
    1.12 +
    1.13 +/* Reads property
    1.14 +   Must call XFree on results
    1.15 + */
    1.16 +static void X11_ReadProperty(SDL_x11Prop *p, Display *disp, Window w, Atom prop) 
    1.17 +{
    1.18 +    unsigned char *ret=NULL;
    1.19 +    Atom type;
    1.20 +    int fmt;
    1.21 +    unsigned long count;
    1.22 +    unsigned long bytes_left;
    1.23 +    int bytes_fetch = 0;	
    1.24 +    
    1.25 +    do {
    1.26 +        if (ret != 0) XFree(ret);
    1.27 +        XGetWindowProperty(disp, w, prop, 0, bytes_fetch, False, AnyPropertyType, &type, &fmt, &count, &bytes_left, &ret);
    1.28 +        bytes_fetch += bytes_left;
    1.29 +    } while (bytes_left != 0);
    1.30 +    
    1.31 +    p->data=ret;
    1.32 +    p->format=fmt;
    1.33 +    p->count=count;
    1.34 +    p->type=type;
    1.35 +}
    1.36 +
    1.37 +/* Find text-uri-list in a list of targets and return it's atom
    1.38 +   if available, else return None */
    1.39 +static Atom X11_PickTarget(Display *disp, Atom list[], int list_count)
    1.40 +{
    1.41 +    Atom request = None;
    1.42 +    char *name;
    1.43 +    int i;
    1.44 +    for (i=0; i < list_count && request == None; i++) {
    1.45 +        name = XGetAtomName(disp, list[i]);
    1.46 +        if (strcmp("text/uri-list", name)==0) request = list[i];
    1.47 +        XFree(name);
    1.48 +    }
    1.49 +    return request;
    1.50 +}
    1.51 +
    1.52 +/* Wrapper for X11_PickTarget for a maximum of three targets, a special
    1.53 +   case in the Xdnd protocol */
    1.54 +static Atom X11_PickTargetFromAtoms(Display *disp, Atom a0, Atom a1, Atom a2)
    1.55 +{
    1.56 +    int count=0;
    1.57 +    Atom atom[3];
    1.58 +    if (a0 != None) atom[count++] = a0;
    1.59 +    if (a1 != None) atom[count++] = a1;
    1.60 +    if (a2 != None) atom[count++] = a2;
    1.61 +    return X11_PickTarget(disp, atom, count);
    1.62 +}
    1.63  /*#define DEBUG_XEVENTS*/
    1.64  
    1.65  /* Check to see if this is a repeated key.
    1.66 @@ -92,6 +148,41 @@
    1.67      return SDL_FALSE;
    1.68  }
    1.69  
    1.70 +/* Convert URI to local filename
    1.71 +   return filename if possible, else NULL
    1.72 +*/
    1.73 +static char* X11_URIToLocal(char* uri) {
    1.74 +    char *file = NULL;
    1.75 +
    1.76 +    if (memcmp(uri,"file:/",6) == 0) uri += 6;      /* local file? */
    1.77 +    else if (strstr(uri,":/") != NULL) return file; /* wrong scheme */
    1.78 +
    1.79 +    SDL_bool local = uri[0] != '/' || ( uri[0] != '\0' && uri[1] == '/' );
    1.80 +
    1.81 +    /* got a hostname? */
    1.82 +    if ( !local && uri[0] == '/' && uri[2] != '/' ) {
    1.83 +      char* hostname_end = strchr( uri+1, '/' );
    1.84 +      if ( hostname_end != NULL ) {
    1.85 +          char hostname[ 257 ];
    1.86 +          if ( gethostname( hostname, 255 ) == 0 ) {
    1.87 +            hostname[ 256 ] = '\0';
    1.88 +            if ( memcmp( uri+1, hostname, hostname_end - ( uri+1 )) == 0 ) {
    1.89 +                uri = hostname_end + 1;
    1.90 +                local = SDL_TRUE;
    1.91 +            }
    1.92 +          }
    1.93 +      }
    1.94 +    }
    1.95 +    if ( local ) {
    1.96 +      file = uri;
    1.97 +      if ( uri[1] == '/' ) {
    1.98 +          file++;
    1.99 +      } else {
   1.100 +          file--;
   1.101 +      }
   1.102 +    }
   1.103 +    return file;
   1.104 +}
   1.105  
   1.106  #if SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
   1.107  static void X11_HandleGenericEvent(SDL_VideoData *videodata,XEvent event)
   1.108 @@ -400,7 +491,68 @@
   1.109  
   1.110          /* Have we been requested to quit (or another client message?) */
   1.111      case ClientMessage:{
   1.112 -            if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) &&
   1.113 +
   1.114 +            int xdnd_version=0;
   1.115 +    
   1.116 +            if (xevent.xclient.message_type == videodata->XdndEnter) {
   1.117 +                SDL_bool use_list = xevent.xclient.data.l[1] & 1;
   1.118 +                data->xdnd_source = xevent.xclient.data.l[0];
   1.119 +                xdnd_version = ( xevent.xclient.data.l[1] >> 24);
   1.120 +                if (use_list) {
   1.121 +                    /* fetch conversion targets */
   1.122 +                    SDL_x11Prop p;
   1.123 +                    X11_ReadProperty(&p, display, data->xdnd_source, videodata->XdndTypeList);
   1.124 +                    /* pick one */
   1.125 +                    data->xdnd_req = X11_PickTarget(display, (Atom*)p.data, p.count);
   1.126 +                    XFree(p.data);
   1.127 +                } else {
   1.128 +                    /* pick from list of three */
   1.129 +                    data->xdnd_req = X11_PickTargetFromAtoms(display, xevent.xclient.data.l[2], xevent.xclient.data.l[3], xevent.xclient.data.l[4]);
   1.130 +                }
   1.131 +            }
   1.132 +            else if (xevent.xclient.message_type == videodata->XdndPosition) {
   1.133 +            
   1.134 +                /* reply with status */
   1.135 +                XClientMessageEvent m;
   1.136 +                memset(&m, 0, sizeof(XClientMessageEvent));
   1.137 +                m.type = ClientMessage;
   1.138 +                m.display = xevent.xclient.display;
   1.139 +                m.window = xevent.xclient.data.l[0];
   1.140 +                m.message_type = videodata->XdndStatus;
   1.141 +                m.format=32;
   1.142 +                m.data.l[0] = data->xwindow;
   1.143 +                m.data.l[1] = (data->xdnd_req != None);
   1.144 +                m.data.l[2] = 0; /* specify an empty rectangle */
   1.145 +                m.data.l[3] = 0;
   1.146 +                m.data.l[4] = videodata->XdndActionCopy; /* we only accept copying anyway */
   1.147 +                
   1.148 +                XSendEvent(display, xevent.xclient.data.l[0], False, NoEventMask, (XEvent*)&m);
   1.149 +                XFlush(display);
   1.150 +            }
   1.151 +            else if(xevent.xclient.message_type == videodata->XdndDrop) {
   1.152 +                if (data->xdnd_req == None) {
   1.153 +                    /* say again - not interested! */
   1.154 +                    XClientMessageEvent m;
   1.155 +                    memset(&m, 0, sizeof(XClientMessageEvent));
   1.156 +                    m.type = ClientMessage;
   1.157 +                    m.display = xevent.xclient.display;
   1.158 +                    m.window = xevent.xclient.data.l[0];
   1.159 +                    m.message_type = videodata->XdndFinished;
   1.160 +                    m.format=32;
   1.161 +                    m.data.l[0] = data->xwindow;
   1.162 +                    m.data.l[1] = 0;
   1.163 +                    m.data.l[2] = None; /* fail! */
   1.164 +                    XSendEvent(display, xevent.xclient.data.l[0], False, NoEventMask, (XEvent*)&m);
   1.165 +                } else {
   1.166 +                    /* convert */
   1.167 +                    if(xdnd_version >= 1) {
   1.168 +                    	XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, xevent.xclient.data.l[2]);
   1.169 +                    } else {
   1.170 +                    	XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, CurrentTime);
   1.171 +                    }
   1.172 +                }
   1.173 +            }
   1.174 +            else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) &&
   1.175                  (xevent.xclient.format == 32) &&
   1.176                  (xevent.xclient.data.l[0] == videodata->_NET_WM_PING)) {
   1.177                  Window root = DefaultRootWindow(display);
   1.178 @@ -601,7 +753,66 @@
   1.179              printf("window %p: SelectionNotify (requestor = %ld, target = %ld)\n", data,
   1.180                  xevent.xselection.requestor, xevent.xselection.target);
   1.181  #endif
   1.182 -            videodata->selection_waiting = SDL_FALSE;
   1.183 +            Atom target = xevent.xselection.target;
   1.184 +            if (target == data->xdnd_req) {
   1.185 +
   1.186 +                /* read data */
   1.187 +                SDL_x11Prop p;
   1.188 +                X11_ReadProperty(&p, display, data->xwindow, videodata->PRIMARY);
   1.189 +                
   1.190 +                if(p.format==8) {
   1.191 +                    SDL_bool expect_lf = SDL_FALSE;
   1.192 +                    char *start = NULL;
   1.193 +                    char *scan = (char*)p.data;
   1.194 +                    char *fn;
   1.195 +                    char *uri;
   1.196 +                    int length = 0;
   1.197 +                    while (p.count--) {
   1.198 +                        if (!expect_lf) {
   1.199 +                            if (*scan==0x0D) {
   1.200 +                                expect_lf = SDL_TRUE;
   1.201 +                            } else if(start == NULL) {
   1.202 +                                start = scan;
   1.203 +                                length = 0;
   1.204 +                            }
   1.205 +                            length++;
   1.206 +                        } else {
   1.207 +                            if (*scan==0x0A && length>0) {
   1.208 +                                uri = malloc(length--);
   1.209 +                                memcpy(uri, start, length);
   1.210 +                                uri[length] = 0;
   1.211 +                                fn = X11_URIToLocal(uri);
   1.212 +                                if (fn) SDL_SendDropFile(fn);
   1.213 +                                free(uri);
   1.214 +                            }
   1.215 +                            expect_lf = SDL_FALSE;
   1.216 +                            start = NULL;
   1.217 +                        }
   1.218 +                        scan++;
   1.219 +                    }
   1.220 +                }
   1.221 +                
   1.222 +                XFree(p.data);
   1.223 +                
   1.224 +                /* send reply */
   1.225 +                XClientMessageEvent m;
   1.226 +                memset(&m, 0, sizeof(XClientMessageEvent));
   1.227 +                m.type = ClientMessage;
   1.228 +                m.display = display;
   1.229 +                m.window = data->xdnd_source;
   1.230 +                m.message_type = videodata->XdndFinished;
   1.231 +                m.format=32;
   1.232 +                m.data.l[0] = data->xwindow;
   1.233 +                m.data.l[1] = 1;
   1.234 +                m.data.l[2] = videodata->XdndActionCopy;
   1.235 +                XSendEvent(display, data->xdnd_source, False, NoEventMask, (XEvent*)&m);
   1.236 +                
   1.237 +                XSync(display, False);
   1.238 +        
   1.239 +            } else {
   1.240 +                videodata->selection_waiting = SDL_FALSE;
   1.241 +            }
   1.242 +        
   1.243          }
   1.244          break;
   1.245  
     2.1 --- a/src/video/x11/SDL_x11video.c	Wed Mar 13 09:14:45 2013 -0700
     2.2 +++ b/src/video/x11/SDL_x11video.c	Wed Mar 13 21:41:43 2013 -0700
     2.3 @@ -524,6 +524,15 @@
     2.4      GET_ATOM(_NET_WM_PING);
     2.5      GET_ATOM(_NET_ACTIVE_WINDOW);
     2.6      GET_ATOM(UTF8_STRING);
     2.7 +    GET_ATOM(PRIMARY);
     2.8 +    GET_ATOM(XdndEnter);
     2.9 +    GET_ATOM(XdndPosition);
    2.10 +    GET_ATOM(XdndStatus);
    2.11 +    GET_ATOM(XdndTypeList);
    2.12 +    GET_ATOM(XdndActionCopy);
    2.13 +    GET_ATOM(XdndDrop);
    2.14 +    GET_ATOM(XdndFinished);
    2.15 +    GET_ATOM(XdndSelection);
    2.16  
    2.17      /* Detect the window manager */
    2.18      X11_CheckWindowManager(_this);
     3.1 --- a/src/video/x11/SDL_x11video.h	Wed Mar 13 09:14:45 2013 -0700
     3.2 +++ b/src/video/x11/SDL_x11video.h	Wed Mar 13 21:41:43 2013 -0700
     3.3 @@ -101,7 +101,16 @@
     3.4      Atom _NET_WM_PING;
     3.5      Atom _NET_ACTIVE_WINDOW;
     3.6      Atom UTF8_STRING;
     3.7 -
     3.8 +    Atom PRIMARY;
     3.9 +    Atom XdndEnter;
    3.10 +    Atom XdndPosition;
    3.11 +    Atom XdndStatus;
    3.12 +    Atom XdndTypeList;
    3.13 +    Atom XdndActionCopy;
    3.14 +    Atom XdndDrop;
    3.15 +    Atom XdndFinished;
    3.16 +    Atom XdndSelection;
    3.17 +    
    3.18      SDL_Scancode key_layout[256];
    3.19      SDL_bool selection_waiting;    
    3.20  
     4.1 --- a/src/video/x11/SDL_x11window.c	Wed Mar 13 09:14:45 2013 -0700
     4.2 +++ b/src/video/x11/SDL_x11window.c	Wed Mar 13 21:41:43 2013 -0700
     4.3 @@ -344,6 +344,7 @@
     4.4      Atom _NET_WM_WINDOW_TYPE;
     4.5      Atom _NET_WM_WINDOW_TYPE_NORMAL;
     4.6      Atom _NET_WM_PID;
     4.7 +    Atom XdndAware, xdnd_version = 5;
     4.8      Uint32 fevent = 0;
     4.9  
    4.10  #if SDL_VIDEO_OPENGL_GLX || SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2
    4.11 @@ -567,6 +568,11 @@
    4.12                   PropertyChangeMask | StructureNotifyMask |
    4.13                   KeymapStateMask | fevent));
    4.14  
    4.15 +    XdndAware = XInternAtom(display, "XdndAware", False);
    4.16 +    XChangeProperty(display, w, XdndAware, XA_ATOM, 32,
    4.17 +                 PropModeReplace,
    4.18 +                 (unsigned char*)&xdnd_version, 1); 
    4.19 +
    4.20      XFlush(display);
    4.21  
    4.22      return 0;
     5.1 --- a/src/video/x11/SDL_x11window.h	Wed Mar 13 09:14:45 2013 -0700
     5.2 +++ b/src/video/x11/SDL_x11window.h	Wed Mar 13 21:41:43 2013 -0700
     5.3 @@ -57,6 +57,8 @@
     5.4      Uint32 pending_focus_time;
     5.5      XConfigureEvent last_xconfigure;
     5.6      struct SDL_VideoData *videodata;
     5.7 +    Atom xdnd_req;
     5.8 +    Window xdnd_source;
     5.9  } SDL_WindowData;
    5.10  
    5.11  extern void X11_SetNetWMState(_THIS, Window xwindow, Uint32 flags);