st-undercurl-0.8.4-20210822.diff - sites - public wiki contents of suckless.org
(HTM) git clone git://git.suckless.org/sites
(DIR) Log
(DIR) Files
(DIR) Refs
---
st-undercurl-0.8.4-20210822.diff (16126B)
---
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,6 +161,7 @@ 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 int eschandle(uchar);
61 @@ -1131,6 +1134,28 @@ tnewline(int first_col)
62 tmoveto(first_col ? 0 : term.c.x, y);
63 }
64
65 +void
66 +readcolonargs(char **p, int cursor, int params[][CAR_PER_ARG])
67 +{
68 + int i = 0;
69 + for (; i < CAR_PER_ARG; i++)
70 + params[cursor][i] = -1;
71 +
72 + if (**p != ':')
73 + return;
74 +
75 + char *np = NULL;
76 + i = 0;
77 +
78 + while (**p == ':' && i < CAR_PER_ARG) {
79 + while (**p == ':')
80 + (*p)++;
81 + params[cursor][i] = strtol(*p, &np, 10);
82 + *p = np;
83 + i++;
84 + }
85 +}
86 +
87 void
88 csiparse(void)
89 {
90 @@ -1153,6 +1178,7 @@ csiparse(void)
91 v = -1;
92 csiescseq.arg[csiescseq.narg++] = v;
93 p = np;
94 + readcolonargs(&p, csiescseq.narg-1, csiescseq.carg);
95 if (*p != ';' || csiescseq.narg == ESC_ARG_SIZ)
96 break;
97 p++;
98 @@ -1369,6 +1395,10 @@ tsetattr(int *attr, int l)
99 ATTR_STRUCK );
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;
106 break;
107 case 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;
111 break;
112 case 4:
113 - term.c.attr.mode |= ATTR_UNDERLINE;
114 + term.c.attr.ustyle = csiescseq.carg[i][0];
115 +
116 + if (term.c.attr.ustyle != 0)
117 + term.c.attr.mode |= ATTR_UNDERLINE;
118 + else
119 + term.c.attr.mode &= ~ATTR_UNDERLINE;
120 +
121 + term.c.attr.mode ^= ATTR_DIRTYUNDERLINE;
122 break;
123 case 5: /* slow blink */
124 /* FALLTHROUGH */
125 @@ -1431,6 +1468,18 @@ tsetattr(int *attr, int l)
126 case 49:
127 term.c.attr.bg = defaultbg;
128 break;
129 + case 58:
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;
134 + break;
135 + case 59:
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;
140 + break;
141 default:
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
146 --- a/st.h
147 +++ b/st.h
148 @@ -34,6 +34,7 @@ enum glyph_attribute {
149 ATTR_WIDE = 1 << 9,
150 ATTR_WDUMMY = 1 << 10,
151 ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
152 + ATTR_DIRTYUNDERLINE = 1 << 15,
153 };
154
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 */
162 } Glyph;
163
164 typedef Glyph *Line;
165 diff --git a/st.info b/st.info
166 index 8201ad6..659878c 100644
167 --- a/st.info
168 +++ b/st.info
169 @@ -1,4 +1,5 @@
170 st-mono| simpleterm monocolor,
171 + Su,
172 acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
173 am,
174 bce,
175 diff --git a/x.c b/x.c
176 index 210f184..3a0e79e 100644
177 --- a/x.c
178 +++ b/x.c
179 @@ -45,6 +45,14 @@ typedef struct {
180 signed char appcursor; /* application cursor */
181 } Key;
182
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
189 +};
190 +
191 /* X modifiers */
192 #define XK_ANY_MOD UINT_MAX
193 #define XK_NO_MOD 0
194 @@ -1339,6 +1347,51 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
195 return numspecs;
196 }
197
198 +static int isSlopeRising (int x, int iPoint, int waveWidth)
199 +{
200 + // . . . .
201 + // / \ / \ / \ / \
202 + // / \ / \ / \ / \
203 + // . . . . .
204 +
205 + // Find absolute `x` of point
206 + x += iPoint * (waveWidth/2);
207 +
208 + // Find index of absolute wave
209 + int absSlope = x / ((float)waveWidth/2);
210 +
211 + return (absSlope % 2);
212 +}
213 +
214 +static int getSlope (int x, int iPoint, int waveWidth)
215 +{
216 + // Sizes: Caps are half width of slopes
217 + // 1_2 1_2 1_2 1_2
218 + // / \ / \ / \ / \
219 + // / \ / \ / \ / \
220 + // 0 3_0 3_0 3_0 3_
221 + // <2-> <1> <---6---->
222 +
223 + // Find type of first point
224 + int firstType;
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;
232 + else
233 + firstType = UNDERCURL_SLOPE_BOTTOM_CAP;
234 +
235 + // Find type of given point
236 + int pointType = (iPoint % 4);
237 + pointType += firstType;
238 + pointType %= 4;
239 +
240 + return pointType;
241 +}
242 +
243 void
244 xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
245 {
246 @@ -1461,8 +1514,357 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
247
248 /* Render underline and strikethrough. */
249 if (base.mode & ATTR_UNDERLINE) {
250 - XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1,
251 - width, 1);
252 + // Underline Color
253 + const int widthThreshold = 28; // +1 width every widthThreshold px of font
254 + int wlw = (win.ch / widthThreshold) + 1; // Wave Line Width
255 + int linecolor;
256 + if ((base.ucolor[0] >= 0) &&
257 + !(base.mode & ATTR_BLINK && win.mode & MODE_BLINK) &&
258 + !(base.mode & ATTR_INVISIBLE)
259 + ) {
260 + // Special color for underline
261 + // Index
262 + if (base.ucolor[1] < 0) {
263 + linecolor = dc.col[base.ucolor[0]].pixel;
264 + }
265 + // RGB
266 + else {
267 + XColor lcolor;
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;
274 + }
275 + } else {
276 + // Foreground color for underline
277 + linecolor = fg->pixel;
278 + }
279 +
280 + XGCValues ugcv = {
281 + .foreground = linecolor,
282 + .line_width = wlw,
283 + .line_style = LineSolid,
284 + .cap_style = CapNotLast
285 + };
286 +
287 + GC ugc = XCreateGC(xw.dpy, XftDrawDrawable(xw.draw),
288 + GCForeground | GCLineWidth | GCLineStyle | GCCapStyle,
289 + &ugcv);
290 +
291 + // Underline Style
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;
299 + int wx = winx;
300 + int wy = winy + win.ch - dc.font.descent;
301 +
302 +#if UNDERCURL_STYLE == UNDERCURL_CURLY
303 + // Draw waves
304 + int narcs = charlen * 2 + 1;
305 + XArc *arcs = xmalloc(sizeof(XArc) * narcs);
306 +
307 + int i = 0;
308 + for (i = 0; i < charlen-1; i++) {
309 + arcs[i*2] = (XArc) {
310 + .x = wx + win.cw * i + ww / 4,
311 + .y = wy,
312 + .width = win.cw / 2,
313 + .height = wh,
314 + .angle1 = 0,
315 + .angle2 = 180 * 64
316 + };
317 + arcs[i*2+1] = (XArc) {
318 + .x = wx + win.cw * i + ww * 0.75,
319 + .y = wy,
320 + .width = win.cw/2,
321 + .height = wh,
322 + .angle1 = 180 * 64,
323 + .angle2 = 180 * 64
324 + };
325 + }
326 + // Last wave
327 + arcs[i*2] = (XArc) {wx + ww * i + ww / 4, wy, ww / 2, wh,
328 + 0, 180 * 64 };
329 + // Last wave tail
330 + arcs[i*2+1] = (XArc) {wx + ww * i + ww * 0.75, wy, ceil(ww / 2.),
331 + wh, 180 * 64, 90 * 64};
332 + // First wave tail
333 + i++;
334 + arcs[i*2] = (XArc) {wx - ww/4 - 1, wy, ceil(ww / 2.), wh, 270 * 64,
335 + 90 * 64 };
336 +
337 + XDrawArcs(xw.dpy, XftDrawDrawable(xw.draw), ugc, arcs, narcs);
338 +
339 + free(arcs);
340 +#elif UNDERCURL_STYLE == UNDERCURL_SPIKY
341 + // Make the underline corridor larger
342 + /*
343 + wy -= wh;
344 + */
345 + wh *= 2;
346 +
347 + // Set the angle of the slope to 45°
348 + ww = wh;
349 +
350 + // Position of wave is independent of word, it's absolute
351 + wx = (wx / (ww/2)) * (ww/2);
352 +
353 + int marginStart = winx - wx;
354 +
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
360 +
361 + // Total length of underline
362 + float waveLength = 0;
363 +
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));
367 +
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)
374 + };
375 +
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)
382 + };
383 + waveLength += (ww/2.f) - marginStart;
384 +
385 + // The rest of the points
386 + for (int i = 2; i < npoints-1; i++) {
387 + points[i] = (XPoint) {
388 + .x = ww/2,
389 + .y = (isSlopeRising(wx, i, ww))
390 + ? wh/2
391 + : -wh/2
392 + };
393 + waveLength += ww/2;
394 + }
395 +
396 + // Last point
397 + points[npoints-1] = (XPoint) {
398 + .x = ww/2,
399 + .y = (isSlopeRising(wx, npoints-1, ww))
400 + ? wh/2
401 + : -wh/2
402 + };
403 + waveLength += ww/2;
404 +
405 + // End
406 + if (waveLength < width) { // Add a bonus point?
407 + int marginEnd = width - waveLength;
408 + points[npoints] = (XPoint) {
409 + .x = marginEnd,
410 + .y = (isSlopeRising(wx, npoints, ww))
411 + ? (marginEnd)
412 + : (-marginEnd)
413 + };
414 +
415 + npoints++;
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);
421 + else
422 + points[npoints-1].y += (marginEnd);
423 + }
424 +
425 + // Draw the lines
426 + XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints,
427 + CoordModePrevious);
428 +
429 + // Draw a second underline with an offset of 1 pixel
430 + if ( ((win.ch / (widthThreshold/2)) % 2)) {
431 + points[0].x++;
432 +
433 + XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points,
434 + npoints, CoordModePrevious);
435 + }
436 +
437 + // Free resources
438 + free(points);
439 + }
440 +#else // UNDERCURL_CAPPED
441 + // Cap is half of wave width
442 + float capRatio = 0.5f;
443 +
444 + // Make the underline corridor larger
445 + wh *= 2;
446 +
447 + // Set the angle of the slope to 45°
448 + ww = wh;
449 + ww *= 1 + capRatio; // Add a bit of width for the cap
450 +
451 + // Position of wave is independent of word, it's absolute
452 + wx = (wx / ww) * ww;
453 +
454 + float marginStart;
455 + switch(getSlope(winx, 0, ww)) {
456 + case UNDERCURL_SLOPE_ASCENDING:
457 + marginStart = winx - wx;
458 + break;
459 + case UNDERCURL_SLOPE_TOP_CAP:
460 + marginStart = winx - (wx + (ww * (2.f/6.f)));
461 + break;
462 + case UNDERCURL_SLOPE_DESCENDING:
463 + marginStart = winx - (wx + (ww * (3.f/6.f)));
464 + break;
465 + case UNDERCURL_SLOPE_BOTTOM_CAP:
466 + marginStart = winx - (wx + (ww * (5.f/6.f)));
467 + break;
468 + }
469 +
470 + // Calculate number of points with floating precision
471 + float n = width; // Width of word in pixels
472 + // ._.
473 + n = (n / ww) * 4; // Number of points (./ \.)
474 + n += 2; // Add two last points
475 + int npoints = n; // Convert to int
476 +
477 + // Position of the pen to draw the lines
478 + float penX = 0;
479 + float penY = 0;
480 +
481 + if (npoints >= 3) {
482 + XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1));
483 +
484 + // First point (Starts with the word bounds)
485 + penX = winx;
486 + switch (getSlope(winx, 0, ww)) {
487 + case UNDERCURL_SLOPE_ASCENDING:
488 + penY = wy + wh/2.f - marginStart;
489 + break;
490 + case UNDERCURL_SLOPE_TOP_CAP:
491 + penY = wy;
492 + break;
493 + case UNDERCURL_SLOPE_DESCENDING:
494 + penY = wy + marginStart;
495 + break;
496 + case UNDERCURL_SLOPE_BOTTOM_CAP:
497 + penY = wy + wh/2.f;
498 + break;
499 + }
500 + points[0].x = penX;
501 + points[0].y = penY;
502 +
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;
507 + penY += 0;
508 + break;
509 + case UNDERCURL_SLOPE_TOP_CAP:
510 + penX += ww * (2.f/6.f) - marginStart;
511 + penY += -wh/2.f + marginStart;
512 + break;
513 + case UNDERCURL_SLOPE_DESCENDING:
514 + penX += ww * (1.f/6.f) - marginStart;
515 + penY += 0;
516 + break;
517 + case UNDERCURL_SLOPE_BOTTOM_CAP:
518 + penX += ww * (2.f/6.f) - marginStart;
519 + penY += -marginStart + wh/2.f;
520 + break;
521 + }
522 + points[1].x = penX;
523 + points[1].y = penY;
524 +
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);
531 + penY += 0;
532 + break;
533 + case UNDERCURL_SLOPE_TOP_CAP:
534 + penX += ww * (2.f/6.f);
535 + penY += -wh / 2.f;
536 + break;
537 + case UNDERCURL_SLOPE_BOTTOM_CAP:
538 + penX += ww * (2.f/6.f);
539 + penY += wh / 2.f;
540 + break;
541 + }
542 + points[i].x = penX;
543 + points[i].y = penY;
544 + }
545 +
546 + // End
547 + float waveLength = penX - winx;
548 + if (waveLength < width) { // Add a bonus point?
549 + int marginEnd = width - waveLength;
550 + penX += marginEnd;
551 + switch(getSlope(winx, npoints, ww)) {
552 + case UNDERCURL_SLOPE_ASCENDING:
553 + case UNDERCURL_SLOPE_DESCENDING:
554 + //penY += 0;
555 + break;
556 + case UNDERCURL_SLOPE_TOP_CAP:
557 + penY += -marginEnd;
558 + break;
559 + case UNDERCURL_SLOPE_BOTTOM_CAP:
560 + penY += marginEnd;
561 + break;
562 + }
563 +
564 + points[npoints].x = penX;
565 + points[npoints].y = penY;
566 +
567 + npoints++;
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;
574 + break;
575 + case UNDERCURL_SLOPE_BOTTOM_CAP:
576 + points[npoints-1].y -= marginEnd;
577 + break;
578 + default:
579 + break;
580 + }
581 + }
582 +
583 + // Draw the lines
584 + XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints,
585 + CoordModeOrigin);
586 +
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++)
590 + points[i].x++;
591 +
592 + XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points,
593 + npoints, CoordModeOrigin);
594 + }
595 +
596 + // Free resources
597 + free(points);
598 + }
599 +#endif
600 + }
601 +
602 + XFreeGC(xw.dpy, ugc);
603 }
604
605 if (base.mode & ATTR_STRUCK) {