diff --git a/README.md b/README.md index 5321549..3ecb1ac 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Refer to [https://st.suckless.org/](https://st.suckless.org/) for details on the ### Changelog: -2021-05-08 - Added blinking cursor patch +2021-05-08 - Added blinking cursor and undercurl patches 2021-05-07 - Added xresources reload patch @@ -169,6 +169,9 @@ Refer to [https://st.suckless.org/](https://st.suckless.org/) for details on the - [themed-cursor](https://st.suckless.org/patches/themed_cursor/) - instead of a default X cursor, use the xterm cursor from your cursor theme + - [undercurl](https://st.suckless.org/patches/undercurl/) + - adds support for special underlines, e.g. curly / wavy underlines + - [vertcenter](https://st.suckless.org/patches/vertcenter/) - vertically center lines in the space available if you have set a larger chscale in config.h diff --git a/patches.def.h b/patches.def.h index aa2433e..51e52ba 100644 --- a/patches.def.h +++ b/patches.def.h @@ -258,6 +258,20 @@ */ #define THEMED_CURSOR_PATCH 0 +/* Adds support for special underlines. + * + * Example test command: + * $ echo -e "\e[4:3m\e[58:5:10munderline\e[0m" + * ^ ^ ^ ^ ^- sets terminal color 10 + * | | | \- indicates that terminal colors should be used + * | | \- indicates that underline color is being set + * | \- sets underline style to curvy + * \- set underline + * + * https://st.suckless.org/patches/undercurl/ + */ +#define UNDERCURL_PATCH 0 + /* Vertically center lines in the space available if you have set a larger chscale in config.h * https://st.suckless.org/patches/vertcenter/ */ diff --git a/st.c b/st.c index 3f6040e..6c0f774 100644 --- a/st.c +++ b/st.c @@ -42,6 +42,9 @@ #define UTF_SIZ 4 #define ESC_BUF_SIZ (128*UTF_SIZ) #define ESC_ARG_SIZ 16 +#if UNDERCURL_PATCH +#define CAR_PER_ARG 4 +#endif // UNDERCURL_PATCH #define STR_BUF_SIZ ESC_BUF_SIZ #define STR_ARG_SIZ ESC_ARG_SIZ @@ -126,6 +129,9 @@ typedef struct { int arg[ESC_ARG_SIZ]; int narg; /* nb of args */ char mode[2]; + #if UNDERCURL_PATCH + int carg[ESC_ARG_SIZ][CAR_PER_ARG]; /* colon args */ + #endif // UNDERCURL_PATCH } CSIEscape; /* STR Escape sequence structs */ @@ -146,6 +152,9 @@ static void ttywriteraw(const char *, size_t); static void csidump(void); static void csihandle(void); +#if UNDERCURL_PATCH +static void readcolonargs(char **, int, int[][CAR_PER_ARG]); +#endif // UNDERCURL_PATCH static void csiparse(void); static void csireset(void); static int eschandle(uchar); @@ -1286,6 +1295,30 @@ tnewline(int first_col) tmoveto(first_col ? 0 : term.c.x, y); } +#if UNDERCURL_PATCH +void +readcolonargs(char **p, int cursor, int params[][CAR_PER_ARG]) +{ + int i = 0; + for (; i < CAR_PER_ARG; i++) + params[cursor][i] = -1; + + if (**p != ':') + return; + + char *np = NULL; + i = 0; + + while (**p == ':' && i < CAR_PER_ARG) { + while (**p == ':') + (*p)++; + params[cursor][i] = strtol(*p, &np, 10); + *p = np; + i++; + } +} +#endif // UNDERCURL_PATCH + void csiparse(void) { @@ -1308,6 +1341,9 @@ csiparse(void) v = -1; csiescseq.arg[csiescseq.narg++] = v; p = np; + #if UNDERCURL_PATCH + readcolonargs(&p, csiescseq.narg-1, csiescseq.carg); + #endif // UNDERCURL_PATCH if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ) break; p++; @@ -1537,6 +1573,12 @@ tsetattr(int *attr, int l) ATTR_STRUCK ); term.c.attr.fg = defaultfg; term.c.attr.bg = defaultbg; + #if UNDERCURL_PATCH + term.c.attr.ustyle = -1; + term.c.attr.ucolor[0] = -1; + term.c.attr.ucolor[1] = -1; + term.c.attr.ucolor[2] = -1; + #endif // UNDERCURL_PATCH break; case 1: term.c.attr.mode |= ATTR_BOLD; @@ -1548,7 +1590,18 @@ tsetattr(int *attr, int l) term.c.attr.mode |= ATTR_ITALIC; break; case 4: + #if UNDERCURL_PATCH + term.c.attr.ustyle = csiescseq.carg[i][0]; + + if (term.c.attr.ustyle != 0) + term.c.attr.mode |= ATTR_UNDERLINE; + else + term.c.attr.mode &= ~ATTR_UNDERLINE; + + term.c.attr.mode ^= ATTR_DIRTYUNDERLINE; + #else term.c.attr.mode |= ATTR_UNDERLINE; + #endif // UNDERCURL_PATCH break; case 5: /* slow blink */ /* FALLTHROUGH */ @@ -1607,6 +1660,20 @@ tsetattr(int *attr, int l) case 49: term.c.attr.bg = defaultbg; break; + #if UNDERCURL_PATCH + case 58: + term.c.attr.ucolor[0] = csiescseq.carg[i][1]; + term.c.attr.ucolor[1] = csiescseq.carg[i][2]; + term.c.attr.ucolor[2] = csiescseq.carg[i][3]; + term.c.attr.mode ^= ATTR_DIRTYUNDERLINE; + break; + case 59: + term.c.attr.ucolor[0] = -1; + term.c.attr.ucolor[1] = -1; + term.c.attr.ucolor[2] = -1; + term.c.attr.mode ^= ATTR_DIRTYUNDERLINE; + break; + #endif // UNDERCURL_PATCH default: if (BETWEEN(attr[i], 30, 37)) { #if MONOCHROME_PATCH diff --git a/st.h b/st.h index 345a257..d0f97f0 100644 --- a/st.h +++ b/st.h @@ -59,6 +59,9 @@ enum glyph_attribute { ATTR_SIXEL = 1 << 13, #endif // SIXEL_PATCH ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, + #if UNDERCURL_PATCH + ATTR_DIRTYUNDERLINE = 1 << 15, + #endif // UNDERCURL_PATCH }; #if SIXEL_PATCH @@ -115,6 +118,10 @@ typedef struct { ushort mode; /* attribute flags */ uint32_t fg; /* foreground */ uint32_t bg; /* background */ + #if UNDERCURL_PATCH + int ustyle; /* underline style */ + int ucolor[3]; /* underline color */ + #endif // UNDERCURL_PATCH } Glyph; typedef Glyph *Line; diff --git a/st.info b/st.info index 8201ad6..659878c 100644 --- a/st.info +++ b/st.info @@ -1,4 +1,5 @@ st-mono| simpleterm monocolor, + Su, acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, am, bce, diff --git a/x.c b/x.c index e61a1fa..fcbba40 100644 --- a/x.c +++ b/x.c @@ -1713,7 +1713,100 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i /* Render underline and strikethrough. */ if (base.mode & ATTR_UNDERLINE) { - #if VERTCENTER_PATCH + #if UNDERCURL_PATCH + // Underline Color + int wlw = 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 VERTCENTER_PATCH + wy += win.cyo; + #endif // VERTCENTER_PATCH + + // 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); + } + + XFreeGC(xw.dpy, ugc); + #elif VERTCENTER_PATCH XftDrawRect(xw.draw, fg, winx, winy + win.cyo + dc.font.ascent + 1, width, 1); #else