Introduction
Introduction Statistics Contact Development Disclaimer Help
add drag and drop patch for st - sites - public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log
Files
Refs
---
commit b8d567921ea57e1d1089b8e0c14aff77bce1d93e
parent 77456c4698f33511b0652c9dfd6f2904b4797584
Author: Tim Keller <tjkeller.xyz>
Date: Sat, 11 Jan 2025 15:03:31 -0600
add drag and drop patch for st
Diffstat:
A st.suckless.org/patches/drag-n-dro… | 24 ++++++++++++++++++++++++
A st.suckless.org/patches/drag-n-dro… | 343 +++++++++++++++++++++++++++…
2 files changed, 367 insertions(+), 0 deletions(-)
---
diff --git a/st.suckless.org/patches/drag-n-drop/index.md b/st.suckless.org/pat…
@@ -0,0 +1,24 @@
+XDND drag-n-drop
+================
+This patch adds
+[XDND Drag-and-Drop](https://www.freedesktop.org/wiki/Specifications/XDND/)
+support for st.
+
+Description
+-----------
+Dragging a file onto the st window from another XDND enabled window, such as a
+graphical file manager, will insert the file path at your cursor. This behavior
+is common in other modern terminal emulators. Multiple files are supported at
+once.
+
+Special characters in the file path (e.g. `space`, `&`, `'`, etc.) are also
+escaped using the `\` character. The full list of escaped characters are stored
+as a string `xdndescchar` in `config.h`.
+
+Download
+--------
+* [st-drag-n-drop-0.9.2.diff](st-drag-n-drop-0.9.2.diff)
+
+Authors
+-------
+* Tim Keller - <[email protected]>
diff --git a/st.suckless.org/patches/drag-n-drop/st-drag-n-drop-0.9.2.diff b/st…
@@ -0,0 +1,343 @@
+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 : -…
++
+ enum glyph_attribute {
+ ATTR_NULL = 0,
+ ATTR_BOLD = 1 << 0,
+diff --git a/x.c b/x.c
+index d73152b..a152ea8 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, XdndActionMov…
++ 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,93 @@ 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, &re…
++ &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, NoEven…
++ &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…
++ /* handle %xx escape sequences in url e.g. %20 == ' '…
++ c = (char)((HEX_TO_INT(src[1]) << 4) | HEX_TO_INT(src…
++ 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) {
++ t += 7; /* remove 'file://' prefix */
++ i += xdndurldecode(t, pastedata + i);
++ t = strtok(NULL, "\n\r");
++ }
++
++ xsetsel(pastedata);
++ selpaste(0);
++}
++
+ void
+ xclipcopy(void)
+ {
+@@ -1227,6 +1331,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 +2032,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.XtextPl…
++ 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, (Windo…
++ } else if (xw.XdndSourceVersion >= 2) {
++ reply.xclient.message_type = xw.XdndFinished;
++
++ XSendEvent((Display*) xw.dpy, xw.XdndSourceWin,
++ False, NoEventMask, &reply);
++ XFlush((Display*) xw.dpy);
+ }
+ }
+
You are viewing proxied material from suckless.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.