st-undercurl-0.9-20240103.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.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) {