commit deae877f1100aac7044194e63e33b2b5e2fcde3a
parent d3071838997fdedd88389ce9cb209af411e10d6c
Author: vyun <hex0octal@gmail.com>
Date:   Wed,  3 Jan 2024 11:00:26 +0100
[st][patch][st-undercurl] Updated for 0.9
Diffstat:
2 files changed, 610 insertions(+), 3 deletions(-)
diff --git a/st.suckless.org/patches/undercurl/index.md b/st.suckless.org/patches/undercurl/index.md
@@ -1,7 +1,7 @@
 undercurl
 =========
 
-
+
 
 Description
 -----------
@@ -18,12 +18,13 @@ file by editing the `UNDERCURL_STYLE` define.
 Notes
 -----
 These escape codes aren't standard, but they are supported by most other
-terminal emulators, as well as many terminal programs (Such as Vim).
+terminal emulators, as well as many terminal programs (Such as Vim and NeoVim).
 
 Download
 --------
+* [st-undercurl-0.9-20240103.diff](st-undercurl-0.9-20240103.diff)
 * [st-undercurl-0.8.4-20210822.diff](st-undercurl-0.8.4-20210822.diff)
 
 Authors
 -------
-* HexOctal - ([github.com/hexoctal](https://github.com/hexoctal)) <hex0octal@gmail.com>
+* vyun - ([github.com/vyuun](https://github.com/vyuun)) <hex0octal@gmail.com>
diff --git a/st.suckless.org/patches/undercurl/st-undercurl-0.9-20240103.diff b/st.suckless.org/patches/undercurl/st-undercurl-0.9-20240103.diff
@@ -0,0 +1,606 @@
+diff --git a/config.def.h b/config.def.h
+index 6f05dce..7ae1b92 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -470,3 +470,27 @@ static char ascii_printable[] =
+ 	" !\"#$%&'()*+,-./0123456789:;<=>?"
+ 	"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
+ 	"`abcdefghijklmnopqrstuvwxyz{|}~";
++
++/**
++ * Undercurl style. Set UNDERCURL_STYLE to one of the available styles.
++ *
++ * Curly: Dunno how to draw it *shrug*
++ *  _   _   _   _
++ * ( ) ( ) ( ) ( )
++ *	 (_) (_) (_) (_)
++ *
++ * Spiky:
++ * /\  /\   /\	/\
++ *   \/  \/	  \/
++ *
++ * Capped:
++ *	_     _     _
++ * / \   / \   / \
++ *    \_/   \_/
++ */
++// Available styles
++#define UNDERCURL_CURLY 0
++#define UNDERCURL_SPIKY 1
++#define UNDERCURL_CAPPED 2
++// Active style
++#define UNDERCURL_STYLE UNDERCURL_SPIKY
+diff --git a/st.c b/st.c
+index 76b7e0d..542ab3a 100644
+--- a/st.c
++++ b/st.c
+@@ -33,6 +33,7 @@
+ #define UTF_SIZ       4
+ #define ESC_BUF_SIZ   (128*UTF_SIZ)
+ #define ESC_ARG_SIZ   16
++#define CAR_PER_ARG   4
+ #define STR_BUF_SIZ   ESC_BUF_SIZ
+ #define STR_ARG_SIZ   ESC_ARG_SIZ
+ 
+@@ -139,6 +140,7 @@ typedef struct {
+ 	int arg[ESC_ARG_SIZ];
+ 	int narg;              /* nb of args */
+ 	char mode[2];
++	int carg[ESC_ARG_SIZ][CAR_PER_ARG]; /* colon args */
+ } CSIEscape;
+ 
+ /* STR Escape sequence structs */
+@@ -159,7 +161,8 @@ static void ttywriteraw(const char *, size_t);
+ 
+ static void csidump(void);
+ static void csihandle(void);
++static void readcolonargs(char **, int, int[][CAR_PER_ARG]);
+ static void csiparse(void);
+ static void csireset(void);
+ static void osc_color_response(int, int, int);
+ static int eschandle(uchar);
+@@ -1131,6 +1134,28 @@ tnewline(int first_col)
+ 	tmoveto(first_col ? 0 : term.c.x, y);
+ }
+ 
++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++;
++	}
++}
++
+ void
+ csiparse(void)
+ {
+@@ -1153,6 +1178,7 @@ csiparse(void)
+ 			v = -1;
+ 		csiescseq.arg[csiescseq.narg++] = v;
+ 		p = np;
++		readcolonargs(&p, csiescseq.narg-1, csiescseq.carg);
+ 		if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ)
+ 			break;
+ 		p++;
+@@ -1369,6 +1395,10 @@ tsetattr(int *attr, int l)
+ 				ATTR_STRUCK     );
+ 			term.c.attr.fg = defaultfg;
+ 			term.c.attr.bg = defaultbg;
++			term.c.attr.ustyle = -1;
++			term.c.attr.ucolor[0] = -1;
++			term.c.attr.ucolor[1] = -1;
++			term.c.attr.ucolor[2] = -1;
+ 			break;
+ 		case 1:
+ 			term.c.attr.mode |= ATTR_BOLD;
+@@ -1380,7 +1410,14 @@ tsetattr(int *attr, int l)
+ 			term.c.attr.mode |= ATTR_ITALIC;
+ 			break;
+ 		case 4:
+-			term.c.attr.mode |= ATTR_UNDERLINE;
++			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;
+ 			break;
+ 		case 5: /* slow blink */
+ 			/* FALLTHROUGH */
+@@ -1431,6 +1468,18 @@ tsetattr(int *attr, int l)
+ 		case 49:
+ 			term.c.attr.bg = defaultbg;
+ 			break;
++		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;
+ 		default:
+ 			if (BETWEEN(attr[i], 30, 37)) {
+ 				term.c.attr.fg = attr[i] - 30;
+diff --git a/st.h b/st.h
+index 3d351b6..95bdcbd 100644
+--- a/st.h
++++ b/st.h
+@@ -34,6 +34,7 @@ enum glyph_attribute {
+ 	ATTR_WIDE       = 1 << 9,
+ 	ATTR_WDUMMY     = 1 << 10,
+ 	ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
++	ATTR_DIRTYUNDERLINE = 1 << 15,
+ };
+ 
+ enum selection_mode {
+@@ -65,6 +66,8 @@ typedef struct {
+ 	ushort mode;      /* attribute flags */
+ 	uint32_t fg;      /* foreground  */
+ 	uint32_t bg;      /* background  */
++	int ustyle;	  /* underline style */
++	int ucolor[3];    /* underline color */
+ } 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 210f184..3a0e79e 100644
+--- a/x.c
++++ b/x.c
+@@ -45,6 +45,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
+@@ -1339,6 +1347,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)
+ {
+@@ -1461,8 +1514,357 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
+ 
+ 	/* 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) {