aboutsummaryrefslogtreecommitdiff
path: root/x.c
diff options
context:
space:
mode:
authorTim Keller <tjkeller.xyz>2025-01-11 18:12:17 -0600
committerTim Keller <tjkeller.xyz>2025-01-11 18:12:17 -0600
commit6ca34464cfbdcbcf541862c92dcdcc1f2f7d3eda (patch)
tree0a95fbb8801f23c59872d939cec83e2f29556543 /x.c
parent3f8366fe353fe7d161821c282a135fb9d6184c35 (diff)
downloadst-6ca34464cfbdcbcf541862c92dcdcc1f2f7d3eda.tar.xz
st-6ca34464cfbdcbcf541862c92dcdcc1f2f7d3eda.zip
add drag and drop patch
Diffstat (limited to 'x.c')
-rw-r--r--x.c250
1 files changed, 250 insertions, 0 deletions
diff --git a/x.c b/x.c
index f6e066b..7e361d5 100644
--- a/x.c
+++ b/x.c
@@ -106,6 +106,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;
@@ -181,6 +187,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);
@@ -190,6 +199,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);
@@ -234,6 +245,7 @@ static XSelection xsel;
static TermWindow win;
static int tstki; /* title stack index */
static char *titlestack[TITLESTACKSIZE]; /* title stack */
+const char XdndVersion = 5;
/* Font Ring Cache */
enum {
@@ -550,6 +562,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,
@@ -619,6 +636,93 @@ selnotify(XEvent *e)
}
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) {
+ t += 7; /* remove 'file://' prefix */
+ i += xdndurldecode(t, pastedata + i);
+ t = strtok(NULL, "\n\r");
+ }
+
+ xsetsel(pastedata);
+ selpaste(0);
+}
+
+void
xclipcopy(void)
{
clipcopy(NULL);
@@ -1229,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();
@@ -2347,6 +2471,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);
}
}