sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | Refs

st-undercurl-0.9-20240103.diff (16184B)


      1 diff --git a/config.def.h b/config.def.h
      2 index 6f05dce..7ae1b92 100644
      3 --- a/config.def.h
      4 +++ b/config.def.h
      5 @@ -470,3 +470,27 @@ static char ascii_printable[] =
      6  	" !\"#$%&'()*+,-./0123456789:;<=>?"
      7  	"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
      8  	"`abcdefghijklmnopqrstuvwxyz{|}~";
      9 +
     10 +/**
     11 + * Undercurl style. Set UNDERCURL_STYLE to one of the available styles.
     12 + *
     13 + * Curly: Dunno how to draw it *shrug*
     14 + *  _   _   _   _
     15 + * ( ) ( ) ( ) ( )
     16 + *	 (_) (_) (_) (_)
     17 + *
     18 + * Spiky:
     19 + * /\  /\   /\	/\
     20 + *   \/  \/	  \/
     21 + *
     22 + * Capped:
     23 + *	_     _     _
     24 + * / \   / \   / \
     25 + *    \_/   \_/
     26 + */
     27 +// Available styles
     28 +#define UNDERCURL_CURLY 0
     29 +#define UNDERCURL_SPIKY 1
     30 +#define UNDERCURL_CAPPED 2
     31 +// Active style
     32 +#define UNDERCURL_STYLE UNDERCURL_SPIKY
     33 diff --git a/st.c b/st.c
     34 index 76b7e0d..542ab3a 100644
     35 --- a/st.c
     36 +++ b/st.c
     37 @@ -33,6 +33,7 @@
     38  #define UTF_SIZ       4
     39  #define ESC_BUF_SIZ   (128*UTF_SIZ)
     40  #define ESC_ARG_SIZ   16
     41 +#define CAR_PER_ARG   4
     42  #define STR_BUF_SIZ   ESC_BUF_SIZ
     43  #define STR_ARG_SIZ   ESC_ARG_SIZ
     44  
     45 @@ -139,6 +140,7 @@ typedef struct {
     46  	int arg[ESC_ARG_SIZ];
     47  	int narg;              /* nb of args */
     48  	char mode[2];
     49 +	int carg[ESC_ARG_SIZ][CAR_PER_ARG]; /* colon args */
     50  } CSIEscape;
     51  
     52  /* STR Escape sequence structs */
     53 @@ -159,7 +161,8 @@ static void ttywriteraw(const char *, size_t);
     54  
     55  static void csidump(void);
     56  static void csihandle(void);
     57 +static void readcolonargs(char **, int, int[][CAR_PER_ARG]);
     58  static void csiparse(void);
     59  static void csireset(void);
     60  static void osc_color_response(int, int, int);
     61  static int eschandle(uchar);
     62 @@ -1131,6 +1134,28 @@ tnewline(int first_col)
     63  	tmoveto(first_col ? 0 : term.c.x, y);
     64  }
     65  
     66 +void
     67 +readcolonargs(char **p, int cursor, int params[][CAR_PER_ARG])
     68 +{
     69 +	int i = 0;
     70 +	for (; i < CAR_PER_ARG; i++)
     71 +		params[cursor][i] = -1;
     72 +
     73 +	if (**p != ':')
     74 +		return;
     75 +
     76 +	char *np = NULL;
     77 +	i = 0;
     78 +
     79 +	while (**p == ':' && i < CAR_PER_ARG) {
     80 +		while (**p == ':')
     81 +			(*p)++;
     82 +		params[cursor][i] = strtol(*p, &np, 10);
     83 +		*p = np;
     84 +		i++;
     85 +	}
     86 +}
     87 +
     88  void
     89  csiparse(void)
     90  {
     91 @@ -1153,6 +1178,7 @@ csiparse(void)
     92  			v = -1;
     93  		csiescseq.arg[csiescseq.narg++] = v;
     94  		p = np;
     95 +		readcolonargs(&p, csiescseq.narg-1, csiescseq.carg);
     96  		if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ)
     97  			break;
     98  		p++;
     99 @@ -1369,6 +1395,10 @@ tsetattr(int *attr, int l)
    100  				ATTR_STRUCK     );
    101  			term.c.attr.fg = defaultfg;
    102  			term.c.attr.bg = defaultbg;
    103 +			term.c.attr.ustyle = -1;
    104 +			term.c.attr.ucolor[0] = -1;
    105 +			term.c.attr.ucolor[1] = -1;
    106 +			term.c.attr.ucolor[2] = -1;
    107  			break;
    108  		case 1:
    109  			term.c.attr.mode |= ATTR_BOLD;
    110 @@ -1380,7 +1410,14 @@ tsetattr(int *attr, int l)
    111  			term.c.attr.mode |= ATTR_ITALIC;
    112  			break;
    113  		case 4:
    114 -			term.c.attr.mode |= ATTR_UNDERLINE;
    115 +			term.c.attr.ustyle = csiescseq.carg[i][0];
    116 +
    117 +			if (term.c.attr.ustyle != 0)
    118 +				term.c.attr.mode |= ATTR_UNDERLINE;
    119 +			else
    120 +				term.c.attr.mode &= ~ATTR_UNDERLINE;
    121 +
    122 +			term.c.attr.mode ^= ATTR_DIRTYUNDERLINE;
    123  			break;
    124  		case 5: /* slow blink */
    125  			/* FALLTHROUGH */
    126 @@ -1431,6 +1468,18 @@ tsetattr(int *attr, int l)
    127  		case 49:
    128  			term.c.attr.bg = defaultbg;
    129  			break;
    130 +		case 58:
    131 +			term.c.attr.ucolor[0] = csiescseq.carg[i][1];
    132 +			term.c.attr.ucolor[1] = csiescseq.carg[i][2];
    133 +			term.c.attr.ucolor[2] = csiescseq.carg[i][3];
    134 +			term.c.attr.mode ^= ATTR_DIRTYUNDERLINE;
    135 +			break;
    136 +		case 59:
    137 +			term.c.attr.ucolor[0] = -1;
    138 +			term.c.attr.ucolor[1] = -1;
    139 +			term.c.attr.ucolor[2] = -1;
    140 +			term.c.attr.mode ^= ATTR_DIRTYUNDERLINE;
    141 +			break;
    142  		default:
    143  			if (BETWEEN(attr[i], 30, 37)) {
    144  				term.c.attr.fg = attr[i] - 30;
    145 diff --git a/st.h b/st.h
    146 index 3d351b6..95bdcbd 100644
    147 --- a/st.h
    148 +++ b/st.h
    149 @@ -34,6 +34,7 @@ enum glyph_attribute {
    150  	ATTR_WIDE       = 1 << 9,
    151  	ATTR_WDUMMY     = 1 << 10,
    152  	ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
    153 +	ATTR_DIRTYUNDERLINE = 1 << 15,
    154  };
    155  
    156  enum selection_mode {
    157 @@ -65,6 +66,8 @@ typedef struct {
    158  	ushort mode;      /* attribute flags */
    159  	uint32_t fg;      /* foreground  */
    160  	uint32_t bg;      /* background  */
    161 +	int ustyle;	  /* underline style */
    162 +	int ucolor[3];    /* underline color */
    163  } Glyph;
    164  
    165  typedef Glyph *Line;
    166 diff --git a/st.info b/st.info
    167 index 8201ad6..659878c 100644
    168 --- a/st.info
    169 +++ b/st.info
    170 @@ -1,4 +1,5 @@
    171  st-mono| simpleterm monocolor,
    172 +	Su,
    173  	acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
    174  	am,
    175  	bce,
    176 diff --git a/x.c b/x.c
    177 index 210f184..3a0e79e 100644
    178 --- a/x.c
    179 +++ b/x.c
    180 @@ -45,6 +45,14 @@ typedef struct {
    181  	signed char appcursor; /* application cursor */
    182  } Key;
    183  
    184 +/* Undercurl slope types */
    185 +enum undercurl_slope_type {
    186 +	UNDERCURL_SLOPE_ASCENDING = 0,
    187 +	UNDERCURL_SLOPE_TOP_CAP = 1,
    188 +	UNDERCURL_SLOPE_DESCENDING = 2,
    189 +	UNDERCURL_SLOPE_BOTTOM_CAP = 3
    190 +};
    191 +
    192  /* X modifiers */
    193  #define XK_ANY_MOD    UINT_MAX
    194  #define XK_NO_MOD     0
    195 @@ -1339,6 +1347,51 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
    196  	return numspecs;
    197  }
    198  
    199 +static int isSlopeRising (int x, int iPoint, int waveWidth)
    200 +{
    201 +	//    .     .     .     .
    202 +	//   / \   / \   / \   / \
    203 +	//  /   \ /   \ /   \ /   \
    204 +	// .     .     .     .     .
    205 +
    206 +	// Find absolute `x` of point
    207 +	x += iPoint * (waveWidth/2);
    208 +
    209 +	// Find index of absolute wave
    210 +	int absSlope = x / ((float)waveWidth/2);
    211 +
    212 +	return (absSlope % 2);
    213 +}
    214 +
    215 +static int getSlope (int x, int iPoint, int waveWidth)
    216 +{
    217 +	// Sizes: Caps are half width of slopes
    218 +	//    1_2       1_2       1_2      1_2
    219 +	//   /   \     /   \     /   \    /   \
    220 +	//  /     \   /     \   /     \  /     \
    221 +	// 0       3_0       3_0      3_0       3_
    222 +	// <2->    <1>         <---6---->
    223 +
    224 +	// Find type of first point
    225 +	int firstType;
    226 +	x -= (x / waveWidth) * waveWidth;
    227 +	if (x < (waveWidth * (2.f/6.f)))
    228 +		firstType = UNDERCURL_SLOPE_ASCENDING;
    229 +	else if (x < (waveWidth * (3.f/6.f)))
    230 +		firstType = UNDERCURL_SLOPE_TOP_CAP;
    231 +	else if (x < (waveWidth * (5.f/6.f)))
    232 +		firstType = UNDERCURL_SLOPE_DESCENDING;
    233 +	else
    234 +		firstType = UNDERCURL_SLOPE_BOTTOM_CAP;
    235 +
    236 +	// Find type of given point
    237 +	int pointType = (iPoint % 4);
    238 +	pointType += firstType;
    239 +	pointType %= 4;
    240 +
    241 +	return pointType;
    242 +}
    243 +
    244  void
    245  xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
    246  {
    247 @@ -1461,8 +1514,357 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
    248  
    249  	/* Render underline and strikethrough. */
    250  	if (base.mode & ATTR_UNDERLINE) {
    251 -		XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent * chscale + 1,
    252 -				width, 1);
    253 +		// Underline Color
    254 +		const int widthThreshold  = 28; // +1 width every widthThreshold px of font
    255 +		int wlw = (win.ch / widthThreshold) + 1; // Wave Line Width
    256 +		int linecolor;
    257 +		if ((base.ucolor[0] >= 0) &&
    258 +			!(base.mode & ATTR_BLINK && win.mode & MODE_BLINK) &&
    259 +			!(base.mode & ATTR_INVISIBLE)
    260 +		) {
    261 +			// Special color for underline
    262 +			// Index
    263 +			if (base.ucolor[1] < 0) {
    264 +				linecolor = dc.col[base.ucolor[0]].pixel;
    265 +			}
    266 +			// RGB
    267 +			else {
    268 +				XColor lcolor;
    269 +				lcolor.red = base.ucolor[0] * 257;
    270 +				lcolor.green = base.ucolor[1] * 257;
    271 +				lcolor.blue = base.ucolor[2] * 257;
    272 +				lcolor.flags = DoRed | DoGreen | DoBlue;
    273 +				XAllocColor(xw.dpy, xw.cmap, &lcolor);
    274 +				linecolor = lcolor.pixel;
    275 +			}
    276 +		} else {
    277 +			// Foreground color for underline
    278 +			linecolor = fg->pixel;
    279 +		}
    280 +
    281 +		XGCValues ugcv = {
    282 +			.foreground = linecolor,
    283 +			.line_width = wlw,
    284 +			.line_style = LineSolid,
    285 +			.cap_style = CapNotLast
    286 +		};
    287 +
    288 +		GC ugc = XCreateGC(xw.dpy, XftDrawDrawable(xw.draw),
    289 +			GCForeground | GCLineWidth | GCLineStyle | GCCapStyle,
    290 +			&ugcv);
    291 +
    292 +		// Underline Style
    293 +		if (base.ustyle != 3) {
    294 +			//XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, width, 1);
    295 +			XFillRectangle(xw.dpy, XftDrawDrawable(xw.draw), ugc, winx,
    296 +				winy + dc.font.ascent + 1, width, wlw);
    297 +		} else if (base.ustyle == 3) {
    298 +			int ww = win.cw;//width;
    299 +			int wh = dc.font.descent - wlw/2 - 1;//r.height/7;
    300 +			int wx = winx;
    301 +			int wy = winy + win.ch - dc.font.descent;
    302 +
    303 +#if UNDERCURL_STYLE == UNDERCURL_CURLY
    304 +			// Draw waves
    305 +			int narcs = charlen * 2 + 1;
    306 +			XArc *arcs = xmalloc(sizeof(XArc) * narcs);
    307 +
    308 +			int i = 0;
    309 +			for (i = 0; i < charlen-1; i++) {
    310 +				arcs[i*2] = (XArc) {
    311 +					.x = wx + win.cw * i + ww / 4,
    312 +					.y = wy,
    313 +					.width = win.cw / 2,
    314 +					.height = wh,
    315 +					.angle1 = 0,
    316 +					.angle2 = 180 * 64
    317 +				};
    318 +				arcs[i*2+1] = (XArc) {
    319 +					.x = wx + win.cw * i + ww * 0.75,
    320 +					.y = wy,
    321 +					.width = win.cw/2,
    322 +					.height = wh,
    323 +					.angle1 = 180 * 64,
    324 +					.angle2 = 180 * 64
    325 +				};
    326 +			}
    327 +			// Last wave
    328 +			arcs[i*2] = (XArc) {wx + ww * i + ww / 4, wy, ww / 2, wh,
    329 +			0, 180 * 64 };
    330 +			// Last wave tail
    331 +			arcs[i*2+1] = (XArc) {wx + ww * i + ww * 0.75, wy, ceil(ww / 2.),
    332 +			wh, 180 * 64, 90 * 64};
    333 +			// First wave tail
    334 +			i++;
    335 +			arcs[i*2] = (XArc) {wx - ww/4 - 1, wy, ceil(ww / 2.), wh, 270 * 64,
    336 +			90 * 64 };
    337 +
    338 +			XDrawArcs(xw.dpy, XftDrawDrawable(xw.draw), ugc, arcs, narcs);
    339 +
    340 +			free(arcs);
    341 +#elif UNDERCURL_STYLE == UNDERCURL_SPIKY
    342 +			// Make the underline corridor larger
    343 +			/*
    344 +			wy -= wh;
    345 +			*/
    346 +			wh *= 2;
    347 +
    348 +			// Set the angle of the slope to 45°
    349 +			ww = wh;
    350 +
    351 +			// Position of wave is independent of word, it's absolute
    352 +			wx = (wx / (ww/2)) * (ww/2);
    353 +
    354 +			int marginStart = winx - wx;
    355 +
    356 +			// Calculate number of points with floating precision
    357 +			float n = width;					// Width of word in pixels
    358 +			n = (n / ww) * 2;					// Number of slopes (/ or \)
    359 +			n += 2;								// Add two last points
    360 +			int npoints = n;					// Convert to int
    361 +
    362 +			// Total length of underline
    363 +			float waveLength = 0;
    364 +
    365 +			if (npoints >= 3) {
    366 +				// We add an aditional slot in case we use a bonus point
    367 +				XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1));
    368 +
    369 +				// First point (Starts with the word bounds)
    370 +				points[0] = (XPoint) {
    371 +					.x = wx + marginStart,
    372 +					.y = (isSlopeRising(wx, 0, ww))
    373 +						? (wy - marginStart + ww/2.f)
    374 +						: (wy + marginStart)
    375 +				};
    376 +
    377 +				// Second point (Goes back to the absolute point coordinates)
    378 +				points[1] = (XPoint) {
    379 +					.x = (ww/2.f) - marginStart,
    380 +					.y = (isSlopeRising(wx, 1, ww))
    381 +						? (ww/2.f - marginStart)
    382 +						: (-ww/2.f + marginStart)
    383 +				};
    384 +				waveLength += (ww/2.f) - marginStart;
    385 +
    386 +				// The rest of the points
    387 +				for (int i = 2; i < npoints-1; i++) {
    388 +					points[i] = (XPoint) {
    389 +						.x = ww/2,
    390 +						.y = (isSlopeRising(wx, i, ww))
    391 +							? wh/2
    392 +							: -wh/2
    393 +					};
    394 +					waveLength += ww/2;
    395 +				}
    396 +
    397 +				// Last point
    398 +				points[npoints-1] = (XPoint) {
    399 +					.x = ww/2,
    400 +					.y = (isSlopeRising(wx, npoints-1, ww))
    401 +						? wh/2
    402 +						: -wh/2
    403 +				};
    404 +				waveLength += ww/2;
    405 +
    406 +				// End
    407 +				if (waveLength < width) { // Add a bonus point?
    408 +					int marginEnd = width - waveLength;
    409 +					points[npoints] = (XPoint) {
    410 +						.x = marginEnd,
    411 +						.y = (isSlopeRising(wx, npoints, ww))
    412 +							? (marginEnd)
    413 +							: (-marginEnd)
    414 +					};
    415 +
    416 +					npoints++;
    417 +				} else if (waveLength > width) { // Is last point too far?
    418 +					int marginEnd = waveLength - width;
    419 +					points[npoints-1].x -= marginEnd;
    420 +					if (isSlopeRising(wx, npoints-1, ww))
    421 +						points[npoints-1].y -= (marginEnd);
    422 +					else
    423 +						points[npoints-1].y += (marginEnd);
    424 +				}
    425 +
    426 +				// Draw the lines
    427 +				XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints,
    428 +						CoordModePrevious);
    429 +
    430 +				// Draw a second underline with an offset of 1 pixel
    431 +				if ( ((win.ch / (widthThreshold/2)) % 2)) {
    432 +					points[0].x++;
    433 +
    434 +					XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points,
    435 +							npoints, CoordModePrevious);
    436 +				}
    437 +
    438 +				// Free resources
    439 +				free(points);
    440 +			}
    441 +#else // UNDERCURL_CAPPED
    442 +			// Cap is half of wave width
    443 +			float capRatio = 0.5f;
    444 +
    445 +			// Make the underline corridor larger
    446 +			wh *= 2;
    447 +
    448 +			// Set the angle of the slope to 45°
    449 +			ww = wh;
    450 +			ww *= 1 + capRatio; // Add a bit of width for the cap
    451 +
    452 +			// Position of wave is independent of word, it's absolute
    453 +			wx = (wx / ww) * ww;
    454 +
    455 +			float marginStart;
    456 +			switch(getSlope(winx, 0, ww)) {
    457 +				case UNDERCURL_SLOPE_ASCENDING:
    458 +					marginStart = winx - wx;
    459 +					break;
    460 +				case UNDERCURL_SLOPE_TOP_CAP:
    461 +					marginStart = winx - (wx + (ww * (2.f/6.f)));
    462 +					break;
    463 +				case UNDERCURL_SLOPE_DESCENDING:
    464 +					marginStart = winx - (wx + (ww * (3.f/6.f)));
    465 +					break;
    466 +				case UNDERCURL_SLOPE_BOTTOM_CAP:
    467 +					marginStart = winx - (wx + (ww * (5.f/6.f)));
    468 +					break;
    469 +			}
    470 +
    471 +			// Calculate number of points with floating precision
    472 +			float n = width;					// Width of word in pixels
    473 +												//					   ._.
    474 +			n = (n / ww) * 4;					// Number of points (./   \.)
    475 +			n += 2;								// Add two last points
    476 +			int npoints = n;					// Convert to int
    477 +
    478 +			// Position of the pen to draw the lines
    479 +			float penX = 0;
    480 +			float penY = 0;
    481 +
    482 +			if (npoints >= 3) {
    483 +				XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1));
    484 +
    485 +				// First point (Starts with the word bounds)
    486 +				penX = winx;
    487 +				switch (getSlope(winx, 0, ww)) {
    488 +					case UNDERCURL_SLOPE_ASCENDING:
    489 +						penY = wy + wh/2.f - marginStart;
    490 +						break;
    491 +					case UNDERCURL_SLOPE_TOP_CAP:
    492 +						penY = wy;
    493 +						break;
    494 +					case UNDERCURL_SLOPE_DESCENDING:
    495 +						penY = wy + marginStart;
    496 +						break;
    497 +					case UNDERCURL_SLOPE_BOTTOM_CAP:
    498 +						penY = wy + wh/2.f;
    499 +						break;
    500 +				}
    501 +				points[0].x = penX;
    502 +				points[0].y = penY;
    503 +
    504 +				// Second point (Goes back to the absolute point coordinates)
    505 +				switch (getSlope(winx, 1, ww)) {
    506 +					case UNDERCURL_SLOPE_ASCENDING:
    507 +						penX += ww * (1.f/6.f) - marginStart;
    508 +						penY += 0;
    509 +						break;
    510 +					case UNDERCURL_SLOPE_TOP_CAP:
    511 +						penX += ww * (2.f/6.f) - marginStart;
    512 +						penY += -wh/2.f + marginStart;
    513 +						break;
    514 +					case UNDERCURL_SLOPE_DESCENDING:
    515 +						penX += ww * (1.f/6.f) - marginStart;
    516 +						penY += 0;
    517 +						break;
    518 +					case UNDERCURL_SLOPE_BOTTOM_CAP:
    519 +						penX += ww * (2.f/6.f) - marginStart;
    520 +						penY += -marginStart + wh/2.f;
    521 +						break;
    522 +				}
    523 +				points[1].x = penX;
    524 +				points[1].y = penY;
    525 +
    526 +				// The rest of the points
    527 +				for (int i = 2; i < npoints; i++) {
    528 +					switch (getSlope(winx, i, ww)) {
    529 +						case UNDERCURL_SLOPE_ASCENDING:
    530 +						case UNDERCURL_SLOPE_DESCENDING:
    531 +							penX += ww * (1.f/6.f);
    532 +							penY += 0;
    533 +							break;
    534 +						case UNDERCURL_SLOPE_TOP_CAP:
    535 +							penX += ww * (2.f/6.f);
    536 +							penY += -wh / 2.f;
    537 +							break;
    538 +						case UNDERCURL_SLOPE_BOTTOM_CAP:
    539 +							penX += ww * (2.f/6.f);
    540 +							penY += wh / 2.f;
    541 +							break;
    542 +					}
    543 +					points[i].x = penX;
    544 +					points[i].y = penY;
    545 +				}
    546 +
    547 +				// End
    548 +				float waveLength = penX - winx;
    549 +				if (waveLength < width) { // Add a bonus point?
    550 +					int marginEnd = width - waveLength;
    551 +					penX += marginEnd;
    552 +					switch(getSlope(winx, npoints, ww)) {
    553 +						case UNDERCURL_SLOPE_ASCENDING:
    554 +						case UNDERCURL_SLOPE_DESCENDING:
    555 +							//penY += 0;
    556 +							break;
    557 +						case UNDERCURL_SLOPE_TOP_CAP:
    558 +							penY += -marginEnd;
    559 +							break;
    560 +						case UNDERCURL_SLOPE_BOTTOM_CAP:
    561 +							penY += marginEnd;
    562 +							break;
    563 +					}
    564 +
    565 +					points[npoints].x = penX;
    566 +					points[npoints].y = penY;
    567 +
    568 +					npoints++;
    569 +				} else if (waveLength > width) { // Is last point too far?
    570 +					int marginEnd = waveLength - width;
    571 +					points[npoints-1].x -= marginEnd;
    572 +					switch(getSlope(winx, npoints-1, ww)) {
    573 +						case UNDERCURL_SLOPE_TOP_CAP:
    574 +							points[npoints-1].y += marginEnd;
    575 +							break;
    576 +						case UNDERCURL_SLOPE_BOTTOM_CAP:
    577 +							points[npoints-1].y -= marginEnd;
    578 +							break;
    579 +						default:
    580 +							break;
    581 +					}
    582 +				}
    583 +
    584 +				// Draw the lines
    585 +				XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints,
    586 +						CoordModeOrigin);
    587 +
    588 +				// Draw a second underline with an offset of 1 pixel
    589 +				if ( ((win.ch / (widthThreshold/2)) % 2)) {
    590 +					for (int i = 0; i < npoints; i++)
    591 +						points[i].x++;
    592 +
    593 +					XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points,
    594 +							npoints, CoordModeOrigin);
    595 +				}
    596 +
    597 +				// Free resources
    598 +				free(points);
    599 +			}
    600 +#endif
    601 +		}
    602 +
    603 +		XFreeGC(xw.dpy, ugc);
    604  	}
    605  
    606  	if (base.mode & ATTR_STRUCK) {