st-drag-n-drop-0.9.2.diff (9044B)
1 diff --git a/config.def.h b/config.def.h 2 index 2cd740a..3045d0a 100644 3 --- a/config.def.h 4 +++ b/config.def.h 5 @@ -93,6 +93,13 @@ char *termname = "st-256color"; 6 */ 7 unsigned int tabspaces = 8; 8 9 +/* 10 + * drag and drop escape characters 11 + * 12 + * this will add a '\' before any characters specified in the string. 13 + */ 14 +char *xdndescchar = " !\"#$&'()*;<>?[\\]^`{|}~"; 15 + 16 /* Terminal colors (16 first used in escape sequence) */ 17 static const char *colorname[] = { 18 /* 8 normal colors */ 19 diff --git a/st.h b/st.h 20 index fd3b0d8..62c7405 100644 21 --- a/st.h 22 +++ b/st.h 23 @@ -20,6 +20,10 @@ 24 #define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b)) 25 #define IS_TRUECOL(x) (1 << 24 & (x)) 26 27 +#define HEX_TO_INT(c) ((c) >= '0' && (c) <= '9' ? (c) - '0' : \ 28 + (c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \ 29 + (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : -1) 30 + 31 enum glyph_attribute { 32 ATTR_NULL = 0, 33 ATTR_BOLD = 1 << 0, 34 diff --git a/x.c b/x.c 35 index d73152b..a152ea8 100644 36 --- a/x.c 37 +++ b/x.c 38 @@ -94,6 +94,12 @@ typedef struct { 39 Drawable buf; 40 GlyphFontSpec *specbuf; /* font spec buffer used for rendering */ 41 Atom xembed, wmdeletewin, netwmname, netwmiconname, netwmpid; 42 + Atom XdndTypeList, XdndSelection, XdndEnter, XdndPosition, XdndStatus, 43 + XdndLeave, XdndDrop, XdndFinished, XdndActionCopy, XdndActionMove, 44 + XdndActionLink, XdndActionAsk, XdndActionPrivate, XtextUriList, 45 + XtextPlain, XdndAware; 46 + int64_t XdndSourceWin, XdndSourceVersion; 47 + int32_t XdndSourceFormat; 48 struct { 49 XIM xim; 50 XIC xic; 51 @@ -169,6 +175,9 @@ static void visibility(XEvent *); 52 static void unmap(XEvent *); 53 static void kpress(XEvent *); 54 static void cmessage(XEvent *); 55 +static void xdndenter(XEvent *); 56 +static void xdndpos(XEvent *); 57 +static void xdnddrop(XEvent *); 58 static void resize(XEvent *); 59 static void focus(XEvent *); 60 static uint buttonmask(uint); 61 @@ -178,6 +187,8 @@ static void bpress(XEvent *); 62 static void bmotion(XEvent *); 63 static void propnotify(XEvent *); 64 static void selnotify(XEvent *); 65 +static void xdndsel(XEvent *); 66 +static void xdndpastedata(char *); 67 static void selclear_(XEvent *); 68 static void selrequest(XEvent *); 69 static void setsel(char *, Time); 70 @@ -220,6 +231,7 @@ static DC dc; 71 static XWindow xw; 72 static XSelection xsel; 73 static TermWindow win; 74 +const char XdndVersion = 5; 75 76 /* Font Ring Cache */ 77 enum { 78 @@ -536,6 +548,11 @@ selnotify(XEvent *e) 79 if (property == None) 80 return; 81 82 + if (property == xw.XdndSelection) { 83 + xdndsel(e); 84 + return; 85 + } 86 + 87 do { 88 if (XGetWindowProperty(xw.dpy, xw.win, property, ofs, 89 BUFSIZ/4, False, AnyPropertyType, 90 @@ -604,6 +621,93 @@ selnotify(XEvent *e) 91 XDeleteProperty(xw.dpy, xw.win, (int)property); 92 } 93 94 +void 95 +xdndsel(XEvent *e) 96 +{ 97 + char* data; 98 + unsigned long result; 99 + 100 + Atom actualType; 101 + int32_t actualFormat; 102 + unsigned long bytesAfter; 103 + XEvent reply = { ClientMessage }; 104 + 105 + reply.xclient.window = xw.XdndSourceWin; 106 + reply.xclient.format = 32; 107 + reply.xclient.data.l[0] = (long) xw.win; 108 + reply.xclient.data.l[2] = 0; 109 + reply.xclient.data.l[3] = 0; 110 + 111 + XGetWindowProperty((Display*) xw.dpy, e->xselection.requestor, 112 + e->xselection.property, 0, LONG_MAX, False, 113 + e->xselection.target, &actualType, &actualFormat, &result, 114 + &bytesAfter, (unsigned char**) &data); 115 + 116 + if (result == 0) 117 + return; 118 + 119 + if (data) { 120 + xdndpastedata(data); 121 + XFree(data); 122 + } 123 + 124 + if (xw.XdndSourceVersion >= 2) { 125 + reply.xclient.message_type = xw.XdndFinished; 126 + reply.xclient.data.l[1] = result; 127 + reply.xclient.data.l[2] = xw.XdndActionCopy; 128 + 129 + XSendEvent((Display*) xw.dpy, xw.XdndSourceWin, False, NoEventMask, 130 + &reply); 131 + XFlush((Display*) xw.dpy); 132 + } 133 +} 134 + 135 +int 136 +xdndurldecode(char *src, char *dest) 137 +{ 138 + char c; 139 + int i = 0; 140 + 141 + while (*src) { 142 + if (*src == '%' && HEX_TO_INT(src[1]) != -1 && HEX_TO_INT(src[2]) != -1) { 143 + /* handle %xx escape sequences in url e.g. %20 == ' ' */ 144 + c = (char)((HEX_TO_INT(src[1]) << 4) | HEX_TO_INT(src[2])); 145 + src += 3; 146 + } else { 147 + c = *src++; 148 + } 149 + if (strchr(xdndescchar, c) != NULL) { 150 + *dest++ = '\\'; 151 + i++; 152 + } 153 + *dest++ = c; 154 + i++; 155 + } 156 + *dest++ = ' '; 157 + *dest = '\0'; 158 + return i + 1; 159 +} 160 + 161 +void 162 +xdndpastedata(char *data) 163 +{ 164 + char *pastedata, *t; 165 + int i = 0; 166 + 167 + pastedata = (char *)malloc(strlen(data) * 2 + 1); 168 + *pastedata = '\0'; 169 + 170 + t = strtok(data, "\n\r"); 171 + while(t != NULL) { 172 + t += 7; /* remove 'file://' prefix */ 173 + i += xdndurldecode(t, pastedata + i); 174 + t = strtok(NULL, "\n\r"); 175 + } 176 + 177 + xsetsel(pastedata); 178 + selpaste(0); 179 +} 180 + 181 void 182 xclipcopy(void) 183 { 184 @@ -1227,6 +1331,26 @@ xinit(int cols, int rows) 185 XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32, 186 PropModeReplace, (uchar *)&thispid, 1); 187 188 + /* Xdnd setup */ 189 + xw.XdndTypeList = XInternAtom(xw.dpy, "XdndTypeList", 0); 190 + xw.XdndSelection = XInternAtom(xw.dpy, "XdndSelection", 0); 191 + xw.XdndEnter = XInternAtom(xw.dpy, "XdndEnter", 0); 192 + xw.XdndPosition = XInternAtom(xw.dpy, "XdndPosition", 0); 193 + xw.XdndStatus = XInternAtom(xw.dpy, "XdndStatus", 0); 194 + xw.XdndLeave = XInternAtom(xw.dpy, "XdndLeave", 0); 195 + xw.XdndDrop = XInternAtom(xw.dpy, "XdndDrop", 0); 196 + xw.XdndFinished = XInternAtom(xw.dpy, "XdndFinished", 0); 197 + xw.XdndActionCopy = XInternAtom(xw.dpy, "XdndActionCopy", 0); 198 + xw.XdndActionMove = XInternAtom(xw.dpy, "XdndActionMove", 0); 199 + xw.XdndActionLink = XInternAtom(xw.dpy, "XdndActionLink", 0); 200 + xw.XdndActionAsk = XInternAtom(xw.dpy, "XdndActionAsk", 0); 201 + xw.XdndActionPrivate = XInternAtom(xw.dpy, "XdndActionPrivate", 0); 202 + xw.XtextUriList = XInternAtom((Display*) xw.dpy, "text/uri-list", 0); 203 + xw.XtextPlain = XInternAtom((Display*) xw.dpy, "text/plain", 0); 204 + xw.XdndAware = XInternAtom(xw.dpy, "XdndAware", 0); 205 + XChangeProperty(xw.dpy, xw.win, xw.XdndAware, 4, 32, PropModeReplace, 206 + &XdndVersion, 1); 207 + 208 win.mode = MODE_NUMLOCK; 209 resettitle(); 210 xhints(); 211 @@ -1908,6 +2032,132 @@ cmessage(XEvent *e) 212 } else if (e->xclient.data.l[0] == xw.wmdeletewin) { 213 ttyhangup(); 214 exit(0); 215 + } else if (e->xclient.message_type == xw.XdndEnter) { 216 + xw.XdndSourceWin = e->xclient.data.l[0]; 217 + xw.XdndSourceVersion = e->xclient.data.l[1] >> 24; 218 + xw.XdndSourceFormat = None; 219 + if (xw.XdndSourceVersion > 5) 220 + return; 221 + xdndenter(e); 222 + } else if (e->xclient.message_type == xw.XdndPosition 223 + && xw.XdndSourceVersion <= 5) { 224 + xdndpos(e); 225 + } else if (e->xclient.message_type == xw.XdndDrop 226 + && xw.XdndSourceVersion <= 5) { 227 + xdnddrop(e); 228 + } 229 +} 230 + 231 +void 232 +xdndenter(XEvent *e) 233 +{ 234 + unsigned long count; 235 + Atom* formats; 236 + Atom real_formats[6]; 237 + Bool list; 238 + Atom actualType; 239 + int32_t actualFormat; 240 + unsigned long bytesAfter; 241 + unsigned long i; 242 + 243 + list = e->xclient.data.l[1] & 1; 244 + 245 + if (list) { 246 + XGetWindowProperty((Display*) xw.dpy, 247 + xw.XdndSourceWin, 248 + xw.XdndTypeList, 249 + 0, 250 + LONG_MAX, 251 + False, 252 + 4, 253 + &actualType, 254 + &actualFormat, 255 + &count, 256 + &bytesAfter, 257 + (unsigned char**) &formats); 258 + } else { 259 + count = 0; 260 + 261 + if (e->xclient.data.l[2] != None) 262 + real_formats[count++] = e->xclient.data.l[2]; 263 + if (e->xclient.data.l[3] != None) 264 + real_formats[count++] = e->xclient.data.l[3]; 265 + if (e->xclient.data.l[4] != None) 266 + real_formats[count++] = e->xclient.data.l[4]; 267 + 268 + formats = real_formats; 269 + } 270 + 271 + for (i = 0; i < count; i++) { 272 + if (formats[i] == xw.XtextUriList || formats[i] == xw.XtextPlain) { 273 + xw.XdndSourceFormat = formats[i]; 274 + break; 275 + } 276 + } 277 + 278 + if (list) 279 + XFree(formats); 280 +} 281 + 282 +void 283 +xdndpos(XEvent *e) 284 +{ 285 + const int32_t xabs = (e->xclient.data.l[2] >> 16) & 0xffff; 286 + const int32_t yabs = (e->xclient.data.l[2]) & 0xffff; 287 + Window dummy; 288 + int32_t xpos, ypos; 289 + XEvent reply = { ClientMessage }; 290 + 291 + reply.xclient.window = xw.XdndSourceWin; 292 + reply.xclient.format = 32; 293 + reply.xclient.data.l[0] = (long) xw.win; 294 + reply.xclient.data.l[2] = 0; 295 + reply.xclient.data.l[3] = 0; 296 + 297 + XTranslateCoordinates((Display*) xw.dpy, 298 + XDefaultRootWindow((Display*) xw.dpy), 299 + (Window) xw.win, 300 + xabs, yabs, 301 + &xpos, &ypos, 302 + &dummy); 303 + 304 + reply.xclient.message_type = xw.XdndStatus; 305 + 306 + if (xw.XdndSourceFormat) { 307 + reply.xclient.data.l[1] = 1; 308 + if (xw.XdndSourceVersion >= 2) 309 + reply.xclient.data.l[4] = xw.XdndActionCopy; 310 + } 311 + 312 + XSendEvent((Display*) xw.dpy, xw.XdndSourceWin, False, NoEventMask, 313 + &reply); 314 + XFlush((Display*) xw.dpy); 315 +} 316 + 317 +void 318 +xdnddrop(XEvent *e) 319 +{ 320 + Time time = CurrentTime; 321 + XEvent reply = { ClientMessage }; 322 + 323 + reply.xclient.window = xw.XdndSourceWin; 324 + reply.xclient.format = 32; 325 + reply.xclient.data.l[0] = (long) xw.win; 326 + reply.xclient.data.l[2] = 0; 327 + reply.xclient.data.l[3] = 0; 328 + 329 + if (xw.XdndSourceFormat) { 330 + if (xw.XdndSourceVersion >= 1) 331 + time = e->xclient.data.l[2]; 332 + 333 + XConvertSelection((Display*) xw.dpy, xw.XdndSelection, 334 + xw.XdndSourceFormat, xw.XdndSelection, (Window) xw.win, time); 335 + } else if (xw.XdndSourceVersion >= 2) { 336 + reply.xclient.message_type = xw.XdndFinished; 337 + 338 + XSendEvent((Display*) xw.dpy, xw.XdndSourceWin, 339 + False, NoEventMask, &reply); 340 + XFlush((Display*) xw.dpy); 341 } 342 } 343