diff options
Diffstat (limited to 'x.c')
| -rw-r--r-- | x.c | 757 |
1 files changed, 732 insertions, 25 deletions
@@ -5,6 +5,7 @@ #include <locale.h> #include <signal.h> #include <sys/select.h> +#include <sys/wait.h> #include <time.h> #include <unistd.h> #include <libgen.h> @@ -14,6 +15,7 @@ #include <X11/keysym.h> #include <X11/Xft/Xft.h> #include <X11/XKBlib.h> +#include <X11/Xcursor/Xcursor.h> char *argv0; #include "arg.h" @@ -45,6 +47,14 @@ typedef struct { signed char appcursor; /* application cursor */ } Key; +/* Undercurl slope types */ +enum undercurl_slope_type { + UNDERCURL_SLOPE_ASCENDING = 0, + UNDERCURL_SLOPE_TOP_CAP = 1, + UNDERCURL_SLOPE_DESCENDING = 2, + UNDERCURL_SLOPE_BOTTOM_CAP = 3 +}; + /* X modifiers */ #define XK_ANY_MOD UINT_MAX #define XK_NO_MOD 0 @@ -55,6 +65,7 @@ static void clipcopy(const Arg *); static void clippaste(const Arg *); static void numlock(const Arg *); static void selpaste(const Arg *); +static void selopen(const Arg *); static void zoom(const Arg *); static void zoomabs(const Arg *); static void zoomreset(const Arg *); @@ -63,6 +74,9 @@ static void ttysend(const Arg *); /* config.h for applying patches and the configuration. */ #include "config.h" +/* size of title stack */ +#define TITLESTACKSIZE 8 + /* XEMBED messages */ #define XEMBED_FOCUS_IN 4 #define XEMBED_FOCUS_OUT 5 @@ -94,6 +108,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 +189,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 +201,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 +245,9 @@ static DC dc; static XWindow xw; 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 { @@ -287,6 +315,20 @@ selpaste(const Arg *dummy) } void +selopen(const Arg *dummy) +{ + pid_t chpid; + + if ((chpid = fork()) == 0) { + if (fork() == 0) + execlp("xdg-open", "xdg-open", getsel(), NULL); + exit(1); + } + if (chpid > 0) + waitpid(chpid, NULL, 0); +} + +void numlock(const Arg *dummy) { win.mode ^= MODE_NUMLOCK; @@ -536,6 +578,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, @@ -605,6 +652,95 @@ 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) { + /* 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) { clipcopy(NULL); @@ -686,6 +822,8 @@ setsel(char *str, Time t) XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t); if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win) selclear(); + + xclipcopy(); } void @@ -869,8 +1007,8 @@ xhints(void) sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize; sizeh->height = win.h; sizeh->width = win.w; - sizeh->height_inc = win.ch; - sizeh->width_inc = win.cw; + sizeh->height_inc = 1; + sizeh->width_inc = 1; sizeh->base_height = 2 * borderpx; sizeh->base_width = 2 * borderpx; sizeh->min_height = win.ch + 2 * borderpx; @@ -1200,23 +1338,9 @@ xinit(int cols, int rows) } /* white cursor, black outline */ - cursor = XCreateFontCursor(xw.dpy, mouseshape); + cursor = XcursorLibraryLoadCursor(xw.dpy, mouseshape); XDefineCursor(xw.dpy, xw.win, cursor); - if (XParseColor(xw.dpy, xw.cmap, colorname[mousefg], &xmousefg) == 0) { - xmousefg.red = 0xffff; - xmousefg.green = 0xffff; - xmousefg.blue = 0xffff; - } - - if (XParseColor(xw.dpy, xw.cmap, colorname[mousebg], &xmousebg) == 0) { - xmousebg.red = 0x0000; - xmousebg.green = 0x0000; - xmousebg.blue = 0x0000; - } - - XRecolorCursor(xw.dpy, cursor, &xmousefg, &xmousebg); - xw.xembed = XInternAtom(xw.dpy, "_XEMBED", False); xw.wmdeletewin = XInternAtom(xw.dpy, "WM_DELETE_WINDOW", False); xw.netwmname = XInternAtom(xw.dpy, "_NET_WM_NAME", False); @@ -1227,6 +1351,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(); @@ -1240,6 +1384,8 @@ xinit(int cols, int rows) xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); if (xsel.xtarget == None) xsel.xtarget = XA_STRING; + + boxdraw_xinit(xw.dpy, xw.cmap, xw.draw, xw.vis); } int @@ -1287,7 +1433,13 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x } /* Lookup character index with default font. */ - glyphidx = XftCharIndex(xw.dpy, font->match, rune); + if (mode & ATTR_BOXDRAW) { + /* minor shoehorning: boxdraw uses only this ushort */ + glyphidx = boxdrawindex(&glyphs[i]); + } else { + /* Lookup character index with default font. */ + glyphidx = XftCharIndex(xw.dpy, font->match, rune); + } if (glyphidx) { specs[numspecs].font = font->match; specs[numspecs].glyph = glyphidx; @@ -1374,6 +1526,51 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x return numspecs; } +static int isSlopeRising (int x, int iPoint, int waveWidth) +{ + // . . . . + // / \ / \ / \ / \ + // / \ / \ / \ / \ + // . . . . . + + // Find absolute `x` of point + x += iPoint * (waveWidth/2); + + // Find index of absolute wave + int absSlope = x / ((float)waveWidth/2); + + return (absSlope % 2); +} + +static int getSlope (int x, int iPoint, int waveWidth) +{ + // Sizes: Caps are half width of slopes + // 1_2 1_2 1_2 1_2 + // / \ / \ / \ / \ + // / \ / \ / \ / \ + // 0 3_0 3_0 3_0 3_ + // <2-> <1> <---6----> + + // Find type of first point + int firstType; + x -= (x / waveWidth) * waveWidth; + if (x < (waveWidth * (2.f/6.f))) + firstType = UNDERCURL_SLOPE_ASCENDING; + else if (x < (waveWidth * (3.f/6.f))) + firstType = UNDERCURL_SLOPE_TOP_CAP; + else if (x < (waveWidth * (5.f/6.f))) + firstType = UNDERCURL_SLOPE_DESCENDING; + else + firstType = UNDERCURL_SLOPE_BOTTOM_CAP; + + // Find type of given point + int pointType = (iPoint % 4); + pointType += firstType; + pointType %= 4; + + return pointType; +} + void xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) { @@ -1492,12 +1689,366 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1); /* Render the glyphs. */ - XftDrawGlyphFontSpec(xw.draw, fg, specs, len); + if (base.mode & ATTR_BOXDRAW) { + drawboxes(winx, winy, width / len, win.ch, fg, bg, specs, len); + } else { + /* Render the glyphs. */ + XftDrawGlyphFontSpec(xw.draw, fg, specs, len); + } /* Render underline and strikethrough. */ if (base.mode & ATTR_UNDERLINE) { - XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent * chscale + 1, - width, 1); + // Underline Color + const int widthThreshold = 28; // +1 width every widthThreshold px of font + int wlw = (win.ch / widthThreshold) + 1; // Wave Line Width + int linecolor; + if ((base.ucolor[0] >= 0) && + !(base.mode & ATTR_BLINK && win.mode & MODE_BLINK) && + !(base.mode & ATTR_INVISIBLE) + ) { + // Special color for underline + // Index + if (base.ucolor[1] < 0) { + linecolor = dc.col[base.ucolor[0]].pixel; + } + // RGB + else { + XColor lcolor; + lcolor.red = base.ucolor[0] * 257; + lcolor.green = base.ucolor[1] * 257; + lcolor.blue = base.ucolor[2] * 257; + lcolor.flags = DoRed | DoGreen | DoBlue; + XAllocColor(xw.dpy, xw.cmap, &lcolor); + linecolor = lcolor.pixel; + } + } else { + // Foreground color for underline + linecolor = fg->pixel; + } + + XGCValues ugcv = { + .foreground = linecolor, + .line_width = wlw, + .line_style = LineSolid, + .cap_style = CapNotLast + }; + + GC ugc = XCreateGC(xw.dpy, XftDrawDrawable(xw.draw), + GCForeground | GCLineWidth | GCLineStyle | GCCapStyle, + &ugcv); + + // Underline Style + if (base.ustyle != 3) { + //XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, width, 1); + XFillRectangle(xw.dpy, XftDrawDrawable(xw.draw), ugc, winx, + winy + dc.font.ascent + 1, width, wlw); + } else if (base.ustyle == 3) { + int ww = win.cw;//width; + int wh = dc.font.descent - wlw/2 - 1;//r.height/7; + int wx = winx; + int wy = winy + win.ch - dc.font.descent; + +#if UNDERCURL_STYLE == UNDERCURL_CURLY + // Draw waves + int narcs = charlen * 2 + 1; + XArc *arcs = xmalloc(sizeof(XArc) * narcs); + + int i = 0; + for (i = 0; i < charlen-1; i++) { + arcs[i*2] = (XArc) { + .x = wx + win.cw * i + ww / 4, + .y = wy, + .width = win.cw / 2, + .height = wh, + .angle1 = 0, + .angle2 = 180 * 64 + }; + arcs[i*2+1] = (XArc) { + .x = wx + win.cw * i + ww * 0.75, + .y = wy, + .width = win.cw/2, + .height = wh, + .angle1 = 180 * 64, + .angle2 = 180 * 64 + }; + } + // Last wave + arcs[i*2] = (XArc) {wx + ww * i + ww / 4, wy, ww / 2, wh, + 0, 180 * 64 }; + // Last wave tail + arcs[i*2+1] = (XArc) {wx + ww * i + ww * 0.75, wy, ceil(ww / 2.), + wh, 180 * 64, 90 * 64}; + // First wave tail + i++; + arcs[i*2] = (XArc) {wx - ww/4 - 1, wy, ceil(ww / 2.), wh, 270 * 64, + 90 * 64 }; + + XDrawArcs(xw.dpy, XftDrawDrawable(xw.draw), ugc, arcs, narcs); + + free(arcs); +#elif UNDERCURL_STYLE == UNDERCURL_SPIKY + // Make the underline corridor larger + /* + wy -= wh; + */ + wh *= 2; + + // Set the angle of the slope to 45° + ww = wh; + + // Position of wave is independent of word, it's absolute + wx = (wx / (ww/2)) * (ww/2); + + int marginStart = winx - wx; + + // Calculate number of points with floating precision + float n = width; // Width of word in pixels + n = (n / ww) * 2; // Number of slopes (/ or \) + n += 2; // Add two last points + int npoints = n; // Convert to int + + // Total length of underline + float waveLength = 0; + + if (npoints >= 3) { + // We add an aditional slot in case we use a bonus point + XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1)); + + // First point (Starts with the word bounds) + points[0] = (XPoint) { + .x = wx + marginStart, + .y = (isSlopeRising(wx, 0, ww)) + ? (wy - marginStart + ww/2.f) + : (wy + marginStart) + }; + + // Second point (Goes back to the absolute point coordinates) + points[1] = (XPoint) { + .x = (ww/2.f) - marginStart, + .y = (isSlopeRising(wx, 1, ww)) + ? (ww/2.f - marginStart) + : (-ww/2.f + marginStart) + }; + waveLength += (ww/2.f) - marginStart; + + // The rest of the points + for (int i = 2; i < npoints-1; i++) { + points[i] = (XPoint) { + .x = ww/2, + .y = (isSlopeRising(wx, i, ww)) + ? wh/2 + : -wh/2 + }; + waveLength += ww/2; + } + + // Last point + points[npoints-1] = (XPoint) { + .x = ww/2, + .y = (isSlopeRising(wx, npoints-1, ww)) + ? wh/2 + : -wh/2 + }; + waveLength += ww/2; + + // End + if (waveLength < width) { // Add a bonus point? + int marginEnd = width - waveLength; + points[npoints] = (XPoint) { + .x = marginEnd, + .y = (isSlopeRising(wx, npoints, ww)) + ? (marginEnd) + : (-marginEnd) + }; + + npoints++; + } else if (waveLength > width) { // Is last point too far? + int marginEnd = waveLength - width; + points[npoints-1].x -= marginEnd; + if (isSlopeRising(wx, npoints-1, ww)) + points[npoints-1].y -= (marginEnd); + else + points[npoints-1].y += (marginEnd); + } + + // Draw the lines + XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints, + CoordModePrevious); + + // Draw a second underline with an offset of 1 pixel + if ( ((win.ch / (widthThreshold/2)) % 2)) { + points[0].x++; + + XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, + npoints, CoordModePrevious); + } + + // Free resources + free(points); + } +#else // UNDERCURL_CAPPED + // Cap is half of wave width + float capRatio = 0.5f; + + // Make the underline corridor larger + wh *= 2; + + // Set the angle of the slope to 45° + ww = wh; + ww *= 1 + capRatio; // Add a bit of width for the cap + + // Position of wave is independent of word, it's absolute + wx = (wx / ww) * ww; + + float marginStart; + switch(getSlope(winx, 0, ww)) { + case UNDERCURL_SLOPE_ASCENDING: + marginStart = winx - wx; + break; + case UNDERCURL_SLOPE_TOP_CAP: + marginStart = winx - (wx + (ww * (2.f/6.f))); + break; + case UNDERCURL_SLOPE_DESCENDING: + marginStart = winx - (wx + (ww * (3.f/6.f))); + break; + case UNDERCURL_SLOPE_BOTTOM_CAP: + marginStart = winx - (wx + (ww * (5.f/6.f))); + break; + } + + // Calculate number of points with floating precision + float n = width; // Width of word in pixels + // ._. + n = (n / ww) * 4; // Number of points (./ \.) + n += 2; // Add two last points + int npoints = n; // Convert to int + + // Position of the pen to draw the lines + float penX = 0; + float penY = 0; + + if (npoints >= 3) { + XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1)); + + // First point (Starts with the word bounds) + penX = winx; + switch (getSlope(winx, 0, ww)) { + case UNDERCURL_SLOPE_ASCENDING: + penY = wy + wh/2.f - marginStart; + break; + case UNDERCURL_SLOPE_TOP_CAP: + penY = wy; + break; + case UNDERCURL_SLOPE_DESCENDING: + penY = wy + marginStart; + break; + case UNDERCURL_SLOPE_BOTTOM_CAP: + penY = wy + wh/2.f; + break; + } + points[0].x = penX; + points[0].y = penY; + + // Second point (Goes back to the absolute point coordinates) + switch (getSlope(winx, 1, ww)) { + case UNDERCURL_SLOPE_ASCENDING: + penX += ww * (1.f/6.f) - marginStart; + penY += 0; + break; + case UNDERCURL_SLOPE_TOP_CAP: + penX += ww * (2.f/6.f) - marginStart; + penY += -wh/2.f + marginStart; + break; + case UNDERCURL_SLOPE_DESCENDING: + penX += ww * (1.f/6.f) - marginStart; + penY += 0; + break; + case UNDERCURL_SLOPE_BOTTOM_CAP: + penX += ww * (2.f/6.f) - marginStart; + penY += -marginStart + wh/2.f; + break; + } + points[1].x = penX; + points[1].y = penY; + + // The rest of the points + for (int i = 2; i < npoints; i++) { + switch (getSlope(winx, i, ww)) { + case UNDERCURL_SLOPE_ASCENDING: + case UNDERCURL_SLOPE_DESCENDING: + penX += ww * (1.f/6.f); + penY += 0; + break; + case UNDERCURL_SLOPE_TOP_CAP: + penX += ww * (2.f/6.f); + penY += -wh / 2.f; + break; + case UNDERCURL_SLOPE_BOTTOM_CAP: + penX += ww * (2.f/6.f); + penY += wh / 2.f; + break; + } + points[i].x = penX; + points[i].y = penY; + } + + // End + float waveLength = penX - winx; + if (waveLength < width) { // Add a bonus point? + int marginEnd = width - waveLength; + penX += marginEnd; + switch(getSlope(winx, npoints, ww)) { + case UNDERCURL_SLOPE_ASCENDING: + case UNDERCURL_SLOPE_DESCENDING: + //penY += 0; + break; + case UNDERCURL_SLOPE_TOP_CAP: + penY += -marginEnd; + break; + case UNDERCURL_SLOPE_BOTTOM_CAP: + penY += marginEnd; + break; + } + + points[npoints].x = penX; + points[npoints].y = penY; + + npoints++; + } else if (waveLength > width) { // Is last point too far? + int marginEnd = waveLength - width; + points[npoints-1].x -= marginEnd; + switch(getSlope(winx, npoints-1, ww)) { + case UNDERCURL_SLOPE_TOP_CAP: + points[npoints-1].y += marginEnd; + break; + case UNDERCURL_SLOPE_BOTTOM_CAP: + points[npoints-1].y -= marginEnd; + break; + default: + break; + } + } + + // Draw the lines + XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints, + CoordModeOrigin); + + // Draw a second underline with an offset of 1 pixel + if ( ((win.ch / (widthThreshold/2)) % 2)) { + for (int i = 0; i < npoints; i++) + points[i].x++; + + XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, + npoints, CoordModeOrigin); + } + + // Free resources + free(points); + } +#endif + } + + XFreeGC(xw.dpy, ugc); } if (base.mode & ATTR_STRUCK) { @@ -1535,7 +2086,7 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) /* * Select the right color for the right mode. */ - g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; + g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE|ATTR_BOXDRAW; if (IS_SET(MODE_REVERSE)) { g.mode |= ATTR_REVERSE; @@ -1632,10 +2183,30 @@ xseticontitle(char *p) } void -xsettitle(char *p) +xfreetitlestack(void) { - XTextProperty prop; - DEFAULT(p, opt_title); + for (int i = 0; i < LEN(titlestack); i++) { + free(titlestack[i]); + titlestack[i] = NULL; + } +} + +void +xsettitle(char *p, int pop) +{ + XTextProperty prop; + + free(titlestack[tstki]); + if (pop) { + titlestack[tstki] = NULL; + tstki = (tstki - 1 + TITLESTACKSIZE) % TITLESTACKSIZE; + p = titlestack[tstki] ? titlestack[tstki] : opt_title; + } else if (p) { + titlestack[tstki] = xstrdup(p); + } else { + titlestack[tstki] = NULL; + p = opt_title; + } if (p[0] == '\0') p = opt_title; @@ -1648,6 +2219,16 @@ xsettitle(char *p) XFree(prop.value); } +void +xpushtitle(void) +{ + int tstkin = (tstki + 1) % TITLESTACKSIZE; + + free(titlestack[tstkin]); + titlestack[tstkin] = titlestack[tstki] ? xstrdup(titlestack[tstki]) : NULL; + tstki = tstkin; +} + int xstartdraw(void) { @@ -1908,6 +2489,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); } } |
