1 diff --git a/config.def.h b/config.def.h
2 index 6f05dce..7ae1b92 100644
5 @@ -470,3 +470,27 @@ static char ascii_printable[] =
6 " !\"#$%&'()*+,-./0123456789:;<=>?"
7 "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
8 "`abcdefghijklmnopqrstuvwxyz{|}~";
11 + * Undercurl style. Set UNDERCURL_STYLE to one of the available styles.
13 + * Curly: Dunno how to draw it *shrug*
28 +#define UNDERCURL_CURLY 0
29 +#define UNDERCURL_SPIKY 1
30 +#define UNDERCURL_CAPPED 2
32 +#define UNDERCURL_STYLE UNDERCURL_SPIKY
33 diff --git a/st.c b/st.c
34 index 76b7e0d..542ab3a 100644
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
45 @@ -139,6 +140,7 @@ typedef struct {
47 int narg; /* nb of args */
49 + int carg[ESC_ARG_SIZ][CAR_PER_ARG]; /* colon args */
52 /* STR Escape sequence structs */
53 @@ -159,6 +161,7 @@ static void ttywriteraw(const char *, size_t);
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 int eschandle(uchar);
61 @@ -1131,6 +1134,28 @@ tnewline(int first_col)
62 tmoveto(first_col ? 0 : term.c.x, y);
66 +readcolonargs(char **p, int cursor, int params[][CAR_PER_ARG])
69 + for (; i < CAR_PER_ARG; i++)
70 + params[cursor][i] = -1;
78 + while (**p == ':' && i < CAR_PER_ARG) {
81 + params[cursor][i] = strtol(*p, &np, 10);
90 @@ -1153,6 +1178,7 @@ csiparse(void)
92 csiescseq.arg[csiescseq.narg++] = v;
94 + readcolonargs(&p, csiescseq.narg-1, csiescseq.carg);
95 if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ)
98 @@ -1369,6 +1395,10 @@ tsetattr(int *attr, int l)
100 term.c.attr.fg = defaultfg;
101 term.c.attr.bg = defaultbg;
102 + term.c.attr.ustyle = -1;
103 + term.c.attr.ucolor[0] = -1;
104 + term.c.attr.ucolor[1] = -1;
105 + term.c.attr.ucolor[2] = -1;
108 term.c.attr.mode |= ATTR_BOLD;
109 @@ -1380,7 +1410,14 @@ tsetattr(int *attr, int l)
110 term.c.attr.mode |= ATTR_ITALIC;
113 - term.c.attr.mode |= ATTR_UNDERLINE;
114 + term.c.attr.ustyle = csiescseq.carg[i][0];
116 + if (term.c.attr.ustyle != 0)
117 + term.c.attr.mode |= ATTR_UNDERLINE;
119 + term.c.attr.mode &= ~ATTR_UNDERLINE;
121 + term.c.attr.mode ^= ATTR_DIRTYUNDERLINE;
123 case 5: /* slow blink */
125 @@ -1431,6 +1468,18 @@ tsetattr(int *attr, int l)
127 term.c.attr.bg = defaultbg;
130 + term.c.attr.ucolor[0] = csiescseq.carg[i][1];
131 + term.c.attr.ucolor[1] = csiescseq.carg[i][2];
132 + term.c.attr.ucolor[2] = csiescseq.carg[i][3];
133 + term.c.attr.mode ^= ATTR_DIRTYUNDERLINE;
136 + term.c.attr.ucolor[0] = -1;
137 + term.c.attr.ucolor[1] = -1;
138 + term.c.attr.ucolor[2] = -1;
139 + term.c.attr.mode ^= ATTR_DIRTYUNDERLINE;
142 if (BETWEEN(attr[i], 30, 37)) {
143 term.c.attr.fg = attr[i] - 30;
144 diff --git a/st.h b/st.h
145 index 3d351b6..95bdcbd 100644
148 @@ -34,6 +34,7 @@ enum glyph_attribute {
150 ATTR_WDUMMY = 1 << 10,
151 ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
152 + ATTR_DIRTYUNDERLINE = 1 << 15,
155 enum selection_mode {
156 @@ -65,6 +66,8 @@ typedef struct {
157 ushort mode; /* attribute flags */
158 uint32_t fg; /* foreground */
159 uint32_t bg; /* background */
160 + int ustyle; /* underline style */
161 + int ucolor[3]; /* underline color */
165 diff --git a/st.info b/st.info
166 index 8201ad6..659878c 100644
170 st-mono| simpleterm monocolor,
172 acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
175 diff --git a/x.c b/x.c
176 index 210f184..3a0e79e 100644
179 @@ -45,6 +45,14 @@ typedef struct {
180 signed char appcursor; /* application cursor */
183 +/* Undercurl slope types */
184 +enum undercurl_slope_type {
185 + UNDERCURL_SLOPE_ASCENDING = 0,
186 + UNDERCURL_SLOPE_TOP_CAP = 1,
187 + UNDERCURL_SLOPE_DESCENDING = 2,
188 + UNDERCURL_SLOPE_BOTTOM_CAP = 3
192 #define XK_ANY_MOD UINT_MAX
194 @@ -1339,6 +1347,51 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
198 +static int isSlopeRising (int x, int iPoint, int waveWidth)
205 + // Find absolute `x` of point
206 + x += iPoint * (waveWidth/2);
208 + // Find index of absolute wave
209 + int absSlope = x / ((float)waveWidth/2);
211 + return (absSlope % 2);
214 +static int getSlope (int x, int iPoint, int waveWidth)
216 + // Sizes: Caps are half width of slopes
220 + // 0 3_0 3_0 3_0 3_
221 + // <2-> <1> <---6---->
223 + // Find type of first point
225 + x -= (x / waveWidth) * waveWidth;
226 + if (x < (waveWidth * (2.f/6.f)))
227 + firstType = UNDERCURL_SLOPE_ASCENDING;
228 + else if (x < (waveWidth * (3.f/6.f)))
229 + firstType = UNDERCURL_SLOPE_TOP_CAP;
230 + else if (x < (waveWidth * (5.f/6.f)))
231 + firstType = UNDERCURL_SLOPE_DESCENDING;
233 + firstType = UNDERCURL_SLOPE_BOTTOM_CAP;
235 + // Find type of given point
236 + int pointType = (iPoint % 4);
237 + pointType += firstType;
244 xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
246 @@ -1461,8 +1514,357 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
248 /* Render underline and strikethrough. */
249 if (base.mode & ATTR_UNDERLINE) {
250 - XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1,
253 + const int widthThreshold = 28; // +1 width every widthThreshold px of font
254 + int wlw = (win.ch / widthThreshold) + 1; // Wave Line Width
256 + if ((base.ucolor[0] >= 0) &&
257 + !(base.mode & ATTR_BLINK && win.mode & MODE_BLINK) &&
258 + !(base.mode & ATTR_INVISIBLE)
260 + // Special color for underline
262 + if (base.ucolor[1] < 0) {
263 + linecolor = dc.col[base.ucolor[0]].pixel;
268 + lcolor.red = base.ucolor[0] * 257;
269 + lcolor.green = base.ucolor[1] * 257;
270 + lcolor.blue = base.ucolor[2] * 257;
271 + lcolor.flags = DoRed | DoGreen | DoBlue;
272 + XAllocColor(xw.dpy, xw.cmap, &lcolor);
273 + linecolor = lcolor.pixel;
276 + // Foreground color for underline
277 + linecolor = fg->pixel;
281 + .foreground = linecolor,
283 + .line_style = LineSolid,
284 + .cap_style = CapNotLast
287 + GC ugc = XCreateGC(xw.dpy, XftDrawDrawable(xw.draw),
288 + GCForeground | GCLineWidth | GCLineStyle | GCCapStyle,
292 + if (base.ustyle != 3) {
293 + //XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, width, 1);
294 + XFillRectangle(xw.dpy, XftDrawDrawable(xw.draw), ugc, winx,
295 + winy + dc.font.ascent + 1, width, wlw);
296 + } else if (base.ustyle == 3) {
297 + int ww = win.cw;//width;
298 + int wh = dc.font.descent - wlw/2 - 1;//r.height/7;
300 + int wy = winy + win.ch - dc.font.descent;
302 +#if UNDERCURL_STYLE == UNDERCURL_CURLY
304 + int narcs = charlen * 2 + 1;
305 + XArc *arcs = xmalloc(sizeof(XArc) * narcs);
308 + for (i = 0; i < charlen-1; i++) {
309 + arcs[i*2] = (XArc) {
310 + .x = wx + win.cw * i + ww / 4,
312 + .width = win.cw / 2,
317 + arcs[i*2+1] = (XArc) {
318 + .x = wx + win.cw * i + ww * 0.75,
322 + .angle1 = 180 * 64,
327 + arcs[i*2] = (XArc) {wx + ww * i + ww / 4, wy, ww / 2, wh,
330 + arcs[i*2+1] = (XArc) {wx + ww * i + ww * 0.75, wy, ceil(ww / 2.),
331 + wh, 180 * 64, 90 * 64};
334 + arcs[i*2] = (XArc) {wx - ww/4 - 1, wy, ceil(ww / 2.), wh, 270 * 64,
337 + XDrawArcs(xw.dpy, XftDrawDrawable(xw.draw), ugc, arcs, narcs);
340 +#elif UNDERCURL_STYLE == UNDERCURL_SPIKY
341 + // Make the underline corridor larger
347 + // Set the angle of the slope to 45°
350 + // Position of wave is independent of word, it's absolute
351 + wx = (wx / (ww/2)) * (ww/2);
353 + int marginStart = winx - wx;
355 + // Calculate number of points with floating precision
356 + float n = width; // Width of word in pixels
357 + n = (n / ww) * 2; // Number of slopes (/ or \)
358 + n += 2; // Add two last points
359 + int npoints = n; // Convert to int
361 + // Total length of underline
362 + float waveLength = 0;
364 + if (npoints >= 3) {
365 + // We add an aditional slot in case we use a bonus point
366 + XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1));
368 + // First point (Starts with the word bounds)
369 + points[0] = (XPoint) {
370 + .x = wx + marginStart,
371 + .y = (isSlopeRising(wx, 0, ww))
372 + ? (wy - marginStart + ww/2.f)
373 + : (wy + marginStart)
376 + // Second point (Goes back to the absolute point coordinates)
377 + points[1] = (XPoint) {
378 + .x = (ww/2.f) - marginStart,
379 + .y = (isSlopeRising(wx, 1, ww))
380 + ? (ww/2.f - marginStart)
381 + : (-ww/2.f + marginStart)
383 + waveLength += (ww/2.f) - marginStart;
385 + // The rest of the points
386 + for (int i = 2; i < npoints-1; i++) {
387 + points[i] = (XPoint) {
389 + .y = (isSlopeRising(wx, i, ww))
393 + waveLength += ww/2;
397 + points[npoints-1] = (XPoint) {
399 + .y = (isSlopeRising(wx, npoints-1, ww))
403 + waveLength += ww/2;
406 + if (waveLength < width) { // Add a bonus point?
407 + int marginEnd = width - waveLength;
408 + points[npoints] = (XPoint) {
410 + .y = (isSlopeRising(wx, npoints, ww))
416 + } else if (waveLength > width) { // Is last point too far?
417 + int marginEnd = waveLength - width;
418 + points[npoints-1].x -= marginEnd;
419 + if (isSlopeRising(wx, npoints-1, ww))
420 + points[npoints-1].y -= (marginEnd);
422 + points[npoints-1].y += (marginEnd);
426 + XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints,
427 + CoordModePrevious);
429 + // Draw a second underline with an offset of 1 pixel
430 + if ( ((win.ch / (widthThreshold/2)) % 2)) {
433 + XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points,
434 + npoints, CoordModePrevious);
440 +#else // UNDERCURL_CAPPED
441 + // Cap is half of wave width
442 + float capRatio = 0.5f;
444 + // Make the underline corridor larger
447 + // Set the angle of the slope to 45°
449 + ww *= 1 + capRatio; // Add a bit of width for the cap
451 + // Position of wave is independent of word, it's absolute
452 + wx = (wx / ww) * ww;
455 + switch(getSlope(winx, 0, ww)) {
456 + case UNDERCURL_SLOPE_ASCENDING:
457 + marginStart = winx - wx;
459 + case UNDERCURL_SLOPE_TOP_CAP:
460 + marginStart = winx - (wx + (ww * (2.f/6.f)));
462 + case UNDERCURL_SLOPE_DESCENDING:
463 + marginStart = winx - (wx + (ww * (3.f/6.f)));
465 + case UNDERCURL_SLOPE_BOTTOM_CAP:
466 + marginStart = winx - (wx + (ww * (5.f/6.f)));
470 + // Calculate number of points with floating precision
471 + float n = width; // Width of word in pixels
473 + n = (n / ww) * 4; // Number of points (./ \.)
474 + n += 2; // Add two last points
475 + int npoints = n; // Convert to int
477 + // Position of the pen to draw the lines
481 + if (npoints >= 3) {
482 + XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1));
484 + // First point (Starts with the word bounds)
486 + switch (getSlope(winx, 0, ww)) {
487 + case UNDERCURL_SLOPE_ASCENDING:
488 + penY = wy + wh/2.f - marginStart;
490 + case UNDERCURL_SLOPE_TOP_CAP:
493 + case UNDERCURL_SLOPE_DESCENDING:
494 + penY = wy + marginStart;
496 + case UNDERCURL_SLOPE_BOTTOM_CAP:
497 + penY = wy + wh/2.f;
500 + points[0].x = penX;
501 + points[0].y = penY;
503 + // Second point (Goes back to the absolute point coordinates)
504 + switch (getSlope(winx, 1, ww)) {
505 + case UNDERCURL_SLOPE_ASCENDING:
506 + penX += ww * (1.f/6.f) - marginStart;
509 + case UNDERCURL_SLOPE_TOP_CAP:
510 + penX += ww * (2.f/6.f) - marginStart;
511 + penY += -wh/2.f + marginStart;
513 + case UNDERCURL_SLOPE_DESCENDING:
514 + penX += ww * (1.f/6.f) - marginStart;
517 + case UNDERCURL_SLOPE_BOTTOM_CAP:
518 + penX += ww * (2.f/6.f) - marginStart;
519 + penY += -marginStart + wh/2.f;
522 + points[1].x = penX;
523 + points[1].y = penY;
525 + // The rest of the points
526 + for (int i = 2; i < npoints; i++) {
527 + switch (getSlope(winx, i, ww)) {
528 + case UNDERCURL_SLOPE_ASCENDING:
529 + case UNDERCURL_SLOPE_DESCENDING:
530 + penX += ww * (1.f/6.f);
533 + case UNDERCURL_SLOPE_TOP_CAP:
534 + penX += ww * (2.f/6.f);
537 + case UNDERCURL_SLOPE_BOTTOM_CAP:
538 + penX += ww * (2.f/6.f);
542 + points[i].x = penX;
543 + points[i].y = penY;
547 + float waveLength = penX - winx;
548 + if (waveLength < width) { // Add a bonus point?
549 + int marginEnd = width - waveLength;
551 + switch(getSlope(winx, npoints, ww)) {
552 + case UNDERCURL_SLOPE_ASCENDING:
553 + case UNDERCURL_SLOPE_DESCENDING:
556 + case UNDERCURL_SLOPE_TOP_CAP:
557 + penY += -marginEnd;
559 + case UNDERCURL_SLOPE_BOTTOM_CAP:
564 + points[npoints].x = penX;
565 + points[npoints].y = penY;
568 + } else if (waveLength > width) { // Is last point too far?
569 + int marginEnd = waveLength - width;
570 + points[npoints-1].x -= marginEnd;
571 + switch(getSlope(winx, npoints-1, ww)) {
572 + case UNDERCURL_SLOPE_TOP_CAP:
573 + points[npoints-1].y += marginEnd;
575 + case UNDERCURL_SLOPE_BOTTOM_CAP:
576 + points[npoints-1].y -= marginEnd;
584 + XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints,
587 + // Draw a second underline with an offset of 1 pixel
588 + if ( ((win.ch / (widthThreshold/2)) % 2)) {
589 + for (int i = 0; i < npoints; i++)
592 + XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points,
593 + npoints, CoordModeOrigin);
602 + XFreeGC(xw.dpy, ugc);
605 if (base.mode & ATTR_STRUCK) {