346 lines
8.9 KiB
Diff
346 lines
8.9 KiB
Diff
diff --git a/config.def.h b/config.def.h
|
|
index 2cd740a..3045d0a 100644
|
|
--- a/config.def.h
|
|
+++ b/config.def.h
|
|
@@ -93,6 +93,13 @@ char *termname = "st-256color";
|
|
*/
|
|
unsigned int tabspaces = 8;
|
|
|
|
+/*
|
|
+ * drag and drop escape characters
|
|
+ *
|
|
+ * this will add a '\' before any characters specified in the string.
|
|
+ */
|
|
+char *xdndescchar = " !\"#$&'()*;<>?[\\]^`{|}~";
|
|
+
|
|
/* Terminal colors (16 first used in escape sequence) */
|
|
static const char *colorname[] = {
|
|
/* 8 normal colors */
|
|
diff --git a/st.h b/st.h
|
|
index fd3b0d8..62c7405 100644
|
|
--- a/st.h
|
|
+++ b/st.h
|
|
@@ -20,6 +20,10 @@
|
|
#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b))
|
|
#define IS_TRUECOL(x) (1 << 24 & (x))
|
|
|
|
+#define HEX_TO_INT(c) ((c) >= '0' && (c) <= '9' ? (c) - '0' : \
|
|
+ (c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \
|
|
+ (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : -1)
|
|
+
|
|
enum glyph_attribute {
|
|
ATTR_NULL = 0,
|
|
ATTR_BOLD = 1 << 0,
|
|
diff --git a/x.c b/x.c
|
|
index d73152b..1c4b9aa 100644
|
|
--- a/x.c
|
|
+++ b/x.c
|
|
@@ -94,6 +94,12 @@ typedef struct {
|
|
Drawable buf;
|
|
GlyphFontSpec *specbuf; /* font spec buffer used for rendering */
|
|
Atom xembed, wmdeletewin, netwmname, netwmiconname, netwmpid;
|
|
+ Atom XdndTypeList, XdndSelection, XdndEnter, XdndPosition, XdndStatus,
|
|
+ XdndLeave, XdndDrop, XdndFinished, XdndActionCopy, XdndActionMove,
|
|
+ XdndActionLink, XdndActionAsk, XdndActionPrivate, XtextUriList,
|
|
+ XtextPlain, XdndAware;
|
|
+ int64_t XdndSourceWin, XdndSourceVersion;
|
|
+ int32_t XdndSourceFormat;
|
|
struct {
|
|
XIM xim;
|
|
XIC xic;
|
|
@@ -169,6 +175,9 @@ static void visibility(XEvent *);
|
|
static void unmap(XEvent *);
|
|
static void kpress(XEvent *);
|
|
static void cmessage(XEvent *);
|
|
+static void xdndenter(XEvent *);
|
|
+static void xdndpos(XEvent *);
|
|
+static void xdnddrop(XEvent *);
|
|
static void resize(XEvent *);
|
|
static void focus(XEvent *);
|
|
static uint buttonmask(uint);
|
|
@@ -178,6 +187,8 @@ static void bpress(XEvent *);
|
|
static void bmotion(XEvent *);
|
|
static void propnotify(XEvent *);
|
|
static void selnotify(XEvent *);
|
|
+static void xdndsel(XEvent *);
|
|
+static void xdndpastedata(char *);
|
|
static void selclear_(XEvent *);
|
|
static void selrequest(XEvent *);
|
|
static void setsel(char *, Time);
|
|
@@ -220,6 +231,7 @@ static DC dc;
|
|
static XWindow xw;
|
|
static XSelection xsel;
|
|
static TermWindow win;
|
|
+const char XdndVersion = 5;
|
|
|
|
/* Font Ring Cache */
|
|
enum {
|
|
@@ -536,6 +548,11 @@ selnotify(XEvent *e)
|
|
if (property == None)
|
|
return;
|
|
|
|
+ if (property == xw.XdndSelection) {
|
|
+ xdndsel(e);
|
|
+ return;
|
|
+ }
|
|
+
|
|
do {
|
|
if (XGetWindowProperty(xw.dpy, xw.win, property, ofs,
|
|
BUFSIZ/4, False, AnyPropertyType,
|
|
@@ -604,6 +621,95 @@ selnotify(XEvent *e)
|
|
XDeleteProperty(xw.dpy, xw.win, (int)property);
|
|
}
|
|
|
|
+void
|
|
+xdndsel(XEvent *e)
|
|
+{
|
|
+ char* data;
|
|
+ unsigned long result;
|
|
+
|
|
+ Atom actualType;
|
|
+ int32_t actualFormat;
|
|
+ unsigned long bytesAfter;
|
|
+ XEvent reply = { ClientMessage };
|
|
+
|
|
+ reply.xclient.window = xw.XdndSourceWin;
|
|
+ reply.xclient.format = 32;
|
|
+ reply.xclient.data.l[0] = (long) xw.win;
|
|
+ reply.xclient.data.l[2] = 0;
|
|
+ reply.xclient.data.l[3] = 0;
|
|
+
|
|
+ XGetWindowProperty((Display*) xw.dpy, e->xselection.requestor,
|
|
+ e->xselection.property, 0, LONG_MAX, False,
|
|
+ e->xselection.target, &actualType, &actualFormat, &result,
|
|
+ &bytesAfter, (unsigned char**) &data);
|
|
+
|
|
+ if (result == 0)
|
|
+ return;
|
|
+
|
|
+ if (data) {
|
|
+ xdndpastedata(data);
|
|
+ XFree(data);
|
|
+ }
|
|
+
|
|
+ if (xw.XdndSourceVersion >= 2) {
|
|
+ reply.xclient.message_type = xw.XdndFinished;
|
|
+ reply.xclient.data.l[1] = result;
|
|
+ reply.xclient.data.l[2] = xw.XdndActionCopy;
|
|
+
|
|
+ XSendEvent((Display*) xw.dpy, xw.XdndSourceWin, False, NoEventMask,
|
|
+ &reply);
|
|
+ XFlush((Display*) xw.dpy);
|
|
+ }
|
|
+}
|
|
+
|
|
+int
|
|
+xdndurldecode(char *src, char *dest)
|
|
+{
|
|
+ char c;
|
|
+ int i = 0;
|
|
+
|
|
+ while (*src) {
|
|
+ if (*src == '%' && HEX_TO_INT(src[1]) != -1 && HEX_TO_INT(src[2]) != -1) {
|
|
+ /* handle %xx escape sequences in url e.g. %20 == ' ' */
|
|
+ c = (char)((HEX_TO_INT(src[1]) << 4) | HEX_TO_INT(src[2]));
|
|
+ src += 3;
|
|
+ } else {
|
|
+ c = *src++;
|
|
+ }
|
|
+ if (strchr(xdndescchar, c) != NULL) {
|
|
+ *dest++ = '\\';
|
|
+ i++;
|
|
+ }
|
|
+ *dest++ = c;
|
|
+ i++;
|
|
+ }
|
|
+ *dest++ = ' ';
|
|
+ *dest = '\0';
|
|
+ return i + 1;
|
|
+}
|
|
+
|
|
+void
|
|
+xdndpastedata(char *data)
|
|
+{
|
|
+ char *pastedata, *t;
|
|
+ int i = 0;
|
|
+
|
|
+ pastedata = (char *)malloc(strlen(data) * 2 + 1);
|
|
+ *pastedata = '\0';
|
|
+
|
|
+ t = strtok(data, "\n\r");
|
|
+ while(t != NULL) {
|
|
+ /* remove 'file://' prefix if it exists */
|
|
+ if (strncmp(data, "file://", 7) == 0)
|
|
+ t += 7;
|
|
+ i += xdndurldecode(t, pastedata + i);
|
|
+ t = strtok(NULL, "\n\r");
|
|
+ }
|
|
+
|
|
+ xsetsel(pastedata);
|
|
+ selpaste(0);
|
|
+}
|
|
+
|
|
void
|
|
xclipcopy(void)
|
|
{
|
|
@@ -1227,6 +1333,26 @@ xinit(int cols, int rows)
|
|
XChangeProperty(xw.dpy, xw.win, xw.netwmpid, XA_CARDINAL, 32,
|
|
PropModeReplace, (uchar *)&thispid, 1);
|
|
|
|
+ /* Xdnd setup */
|
|
+ xw.XdndTypeList = XInternAtom(xw.dpy, "XdndTypeList", 0);
|
|
+ xw.XdndSelection = XInternAtom(xw.dpy, "XdndSelection", 0);
|
|
+ xw.XdndEnter = XInternAtom(xw.dpy, "XdndEnter", 0);
|
|
+ xw.XdndPosition = XInternAtom(xw.dpy, "XdndPosition", 0);
|
|
+ xw.XdndStatus = XInternAtom(xw.dpy, "XdndStatus", 0);
|
|
+ xw.XdndLeave = XInternAtom(xw.dpy, "XdndLeave", 0);
|
|
+ xw.XdndDrop = XInternAtom(xw.dpy, "XdndDrop", 0);
|
|
+ xw.XdndFinished = XInternAtom(xw.dpy, "XdndFinished", 0);
|
|
+ xw.XdndActionCopy = XInternAtom(xw.dpy, "XdndActionCopy", 0);
|
|
+ xw.XdndActionMove = XInternAtom(xw.dpy, "XdndActionMove", 0);
|
|
+ xw.XdndActionLink = XInternAtom(xw.dpy, "XdndActionLink", 0);
|
|
+ xw.XdndActionAsk = XInternAtom(xw.dpy, "XdndActionAsk", 0);
|
|
+ xw.XdndActionPrivate = XInternAtom(xw.dpy, "XdndActionPrivate", 0);
|
|
+ xw.XtextUriList = XInternAtom((Display*) xw.dpy, "text/uri-list", 0);
|
|
+ xw.XtextPlain = XInternAtom((Display*) xw.dpy, "text/plain", 0);
|
|
+ xw.XdndAware = XInternAtom(xw.dpy, "XdndAware", 0);
|
|
+ XChangeProperty(xw.dpy, xw.win, xw.XdndAware, 4, 32, PropModeReplace,
|
|
+ &XdndVersion, 1);
|
|
+
|
|
win.mode = MODE_NUMLOCK;
|
|
resettitle();
|
|
xhints();
|
|
@@ -1908,6 +2034,132 @@ cmessage(XEvent *e)
|
|
} else if (e->xclient.data.l[0] == xw.wmdeletewin) {
|
|
ttyhangup();
|
|
exit(0);
|
|
+ } else if (e->xclient.message_type == xw.XdndEnter) {
|
|
+ xw.XdndSourceWin = e->xclient.data.l[0];
|
|
+ xw.XdndSourceVersion = e->xclient.data.l[1] >> 24;
|
|
+ xw.XdndSourceFormat = None;
|
|
+ if (xw.XdndSourceVersion > 5)
|
|
+ return;
|
|
+ xdndenter(e);
|
|
+ } else if (e->xclient.message_type == xw.XdndPosition
|
|
+ && xw.XdndSourceVersion <= 5) {
|
|
+ xdndpos(e);
|
|
+ } else if (e->xclient.message_type == xw.XdndDrop
|
|
+ && xw.XdndSourceVersion <= 5) {
|
|
+ xdnddrop(e);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+xdndenter(XEvent *e)
|
|
+{
|
|
+ unsigned long count;
|
|
+ Atom* formats;
|
|
+ Atom real_formats[6];
|
|
+ Bool list;
|
|
+ Atom actualType;
|
|
+ int32_t actualFormat;
|
|
+ unsigned long bytesAfter;
|
|
+ unsigned long i;
|
|
+
|
|
+ list = e->xclient.data.l[1] & 1;
|
|
+
|
|
+ if (list) {
|
|
+ XGetWindowProperty((Display*) xw.dpy,
|
|
+ xw.XdndSourceWin,
|
|
+ xw.XdndTypeList,
|
|
+ 0,
|
|
+ LONG_MAX,
|
|
+ False,
|
|
+ 4,
|
|
+ &actualType,
|
|
+ &actualFormat,
|
|
+ &count,
|
|
+ &bytesAfter,
|
|
+ (unsigned char**) &formats);
|
|
+ } else {
|
|
+ count = 0;
|
|
+
|
|
+ if (e->xclient.data.l[2] != None)
|
|
+ real_formats[count++] = e->xclient.data.l[2];
|
|
+ if (e->xclient.data.l[3] != None)
|
|
+ real_formats[count++] = e->xclient.data.l[3];
|
|
+ if (e->xclient.data.l[4] != None)
|
|
+ real_formats[count++] = e->xclient.data.l[4];
|
|
+
|
|
+ formats = real_formats;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < count; i++) {
|
|
+ if (formats[i] == xw.XtextUriList || formats[i] == xw.XtextPlain) {
|
|
+ xw.XdndSourceFormat = formats[i];
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (list)
|
|
+ XFree(formats);
|
|
+}
|
|
+
|
|
+void
|
|
+xdndpos(XEvent *e)
|
|
+{
|
|
+ const int32_t xabs = (e->xclient.data.l[2] >> 16) & 0xffff;
|
|
+ const int32_t yabs = (e->xclient.data.l[2]) & 0xffff;
|
|
+ Window dummy;
|
|
+ int32_t xpos, ypos;
|
|
+ XEvent reply = { ClientMessage };
|
|
+
|
|
+ reply.xclient.window = xw.XdndSourceWin;
|
|
+ reply.xclient.format = 32;
|
|
+ reply.xclient.data.l[0] = (long) xw.win;
|
|
+ reply.xclient.data.l[2] = 0;
|
|
+ reply.xclient.data.l[3] = 0;
|
|
+
|
|
+ XTranslateCoordinates((Display*) xw.dpy,
|
|
+ XDefaultRootWindow((Display*) xw.dpy),
|
|
+ (Window) xw.win,
|
|
+ xabs, yabs,
|
|
+ &xpos, &ypos,
|
|
+ &dummy);
|
|
+
|
|
+ reply.xclient.message_type = xw.XdndStatus;
|
|
+
|
|
+ if (xw.XdndSourceFormat) {
|
|
+ reply.xclient.data.l[1] = 1;
|
|
+ if (xw.XdndSourceVersion >= 2)
|
|
+ reply.xclient.data.l[4] = xw.XdndActionCopy;
|
|
+ }
|
|
+
|
|
+ XSendEvent((Display*) xw.dpy, xw.XdndSourceWin, False, NoEventMask,
|
|
+ &reply);
|
|
+ XFlush((Display*) xw.dpy);
|
|
+}
|
|
+
|
|
+void
|
|
+xdnddrop(XEvent *e)
|
|
+{
|
|
+ Time time = CurrentTime;
|
|
+ XEvent reply = { ClientMessage };
|
|
+
|
|
+ reply.xclient.window = xw.XdndSourceWin;
|
|
+ reply.xclient.format = 32;
|
|
+ reply.xclient.data.l[0] = (long) xw.win;
|
|
+ reply.xclient.data.l[2] = 0;
|
|
+ reply.xclient.data.l[3] = 0;
|
|
+
|
|
+ if (xw.XdndSourceFormat) {
|
|
+ if (xw.XdndSourceVersion >= 1)
|
|
+ time = e->xclient.data.l[2];
|
|
+
|
|
+ XConvertSelection((Display*) xw.dpy, xw.XdndSelection,
|
|
+ xw.XdndSourceFormat, xw.XdndSelection, (Window) xw.win, time);
|
|
+ } else if (xw.XdndSourceVersion >= 2) {
|
|
+ reply.xclient.message_type = xw.XdndFinished;
|
|
+
|
|
+ XSendEvent((Display*) xw.dpy, xw.XdndSourceWin,
|
|
+ False, NoEventMask, &reply);
|
|
+ XFlush((Display*) xw.dpy);
|
|
}
|
|
}
|
|
|