diff options
author | Mike Crute <mike@crute.us> | 2020-12-22 16:09:42 -0800 |
---|---|---|
committer | Mike Crute <mike@crute.us> | 2023-04-25 20:45:27 -0700 |
commit | 49af5d59d8bed15678c634d11e86364c8801dc82 (patch) | |
tree | 31acbd7d321a0a8303fee0d6b6b65f05100439df | |
parent | 0ddcd0455171f2c52ae68d92bbddecb834350522 (diff) | |
download | st-patched-49af5d59d8bed15678c634d11e86364c8801dc82.tar.bz2 st-patched-49af5d59d8bed15678c634d11e86364c8801dc82.tar.xz st-patched-49af5d59d8bed15678c634d11e86364c8801dc82.zip |
Apply st-ligatures-boxdraw-20200430-0.8.3
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | config.mk | 6 | ||||
-rw-r--r-- | hb.c | 140 | ||||
-rw-r--r-- | hb.h | 7 | ||||
-rw-r--r-- | st.c | 3 | ||||
-rw-r--r-- | st.h | 4 | ||||
-rw-r--r-- | win.h | 2 | ||||
-rw-r--r-- | x.c | 16 |
8 files changed, 173 insertions, 10 deletions
@@ -4,7 +4,7 @@ | |||
4 | 4 | ||
5 | include config.mk | 5 | include config.mk |
6 | 6 | ||
7 | SRC = st.c x.c boxdraw.c | 7 | SRC = st.c x.c boxdraw.c hb.c |
8 | OBJ = $(SRC:.c=.o) | 8 | OBJ = $(SRC:.c=.o) |
9 | 9 | ||
10 | all: options st | 10 | all: options st |
@@ -22,7 +22,8 @@ config.h: | |||
22 | $(CC) $(STCFLAGS) -c $< | 22 | $(CC) $(STCFLAGS) -c $< |
23 | 23 | ||
24 | st.o: config.h st.h win.h | 24 | st.o: config.h st.h win.h |
25 | x.o: arg.h config.h st.h win.h | 25 | x.o: arg.h config.h st.h win.h hb.h |
26 | hb.o: st.h | ||
26 | boxdraw.o: config.h st.h boxdraw_data.h | 27 | boxdraw.o: config.h st.h boxdraw_data.h |
27 | 28 | ||
28 | $(OBJ): config.h config.mk | 29 | $(OBJ): config.h config.mk |
@@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config | |||
15 | # includes and libs | 15 | # includes and libs |
16 | INCS = -I$(X11INC) \ | 16 | INCS = -I$(X11INC) \ |
17 | `$(PKG_CONFIG) --cflags fontconfig` \ | 17 | `$(PKG_CONFIG) --cflags fontconfig` \ |
18 | `$(PKG_CONFIG) --cflags freetype2` | 18 | `$(PKG_CONFIG) --cflags freetype2` \ |
19 | `$(PKG_CONFIG) --cflags harfbuzz` | ||
19 | LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ | 20 | LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ |
20 | `$(PKG_CONFIG) --libs fontconfig` \ | 21 | `$(PKG_CONFIG) --libs fontconfig` \ |
21 | `$(PKG_CONFIG) --libs freetype2` | 22 | `$(PKG_CONFIG) --libs freetype2` \ |
23 | `$(PKG_CONFIG) --libs harfbuzz` | ||
22 | 24 | ||
23 | # flags | 25 | # flags |
24 | STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 | 26 | STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 |
@@ -0,0 +1,140 @@ | |||
1 | #include <stdlib.h> | ||
2 | #include <stdio.h> | ||
3 | #include <math.h> | ||
4 | #include <X11/Xft/Xft.h> | ||
5 | #include <hb.h> | ||
6 | #include <hb-ft.h> | ||
7 | |||
8 | #include "st.h" | ||
9 | |||
10 | void hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t *codepoints, int start, int length); | ||
11 | hb_font_t *hbfindfont(XftFont *match); | ||
12 | |||
13 | typedef struct { | ||
14 | XftFont *match; | ||
15 | hb_font_t *font; | ||
16 | } HbFontMatch; | ||
17 | |||
18 | static int hbfontslen = 0; | ||
19 | static HbFontMatch *hbfontcache = NULL; | ||
20 | |||
21 | void | ||
22 | hbunloadfonts() | ||
23 | { | ||
24 | for (int i = 0; i < hbfontslen; i++) { | ||
25 | hb_font_destroy(hbfontcache[i].font); | ||
26 | XftUnlockFace(hbfontcache[i].match); | ||
27 | } | ||
28 | |||
29 | if (hbfontcache != NULL) { | ||
30 | free(hbfontcache); | ||
31 | hbfontcache = NULL; | ||
32 | } | ||
33 | hbfontslen = 0; | ||
34 | } | ||
35 | |||
36 | hb_font_t * | ||
37 | hbfindfont(XftFont *match) | ||
38 | { | ||
39 | for (int i = 0; i < hbfontslen; i++) { | ||
40 | if (hbfontcache[i].match == match) | ||
41 | return hbfontcache[i].font; | ||
42 | } | ||
43 | |||
44 | /* Font not found in cache, caching it now. */ | ||
45 | hbfontcache = realloc(hbfontcache, sizeof(HbFontMatch) * (hbfontslen + 1)); | ||
46 | FT_Face face = XftLockFace(match); | ||
47 | hb_font_t *font = hb_ft_font_create(face, NULL); | ||
48 | if (font == NULL) | ||
49 | die("Failed to load Harfbuzz font."); | ||
50 | |||
51 | hbfontcache[hbfontslen].match = match; | ||
52 | hbfontcache[hbfontslen].font = font; | ||
53 | hbfontslen += 1; | ||
54 | |||
55 | return font; | ||
56 | } | ||
57 | |||
58 | void | ||
59 | hbtransform(XftGlyphFontSpec *specs, const Glyph *glyphs, size_t len, int x, int y) | ||
60 | { | ||
61 | int start = 0, length = 1, gstart = 0; | ||
62 | hb_codepoint_t *codepoints = calloc(len, sizeof(hb_codepoint_t)); | ||
63 | |||
64 | for (int idx = 1, specidx = 1; idx < len; idx++) { | ||
65 | if (glyphs[idx].mode & ATTR_WDUMMY) { | ||
66 | length += 1; | ||
67 | continue; | ||
68 | } | ||
69 | |||
70 | if (specs[specidx].font != specs[start].font || ATTRCMP(glyphs[gstart], glyphs[idx]) || selected(x + idx, y) != selected(x + gstart, y)) { | ||
71 | hbtransformsegment(specs[start].font, glyphs, codepoints, gstart, length); | ||
72 | |||
73 | /* Reset the sequence. */ | ||
74 | length = 1; | ||
75 | start = specidx; | ||
76 | gstart = idx; | ||
77 | } else { | ||
78 | length += 1; | ||
79 | } | ||
80 | |||
81 | specidx++; | ||
82 | } | ||
83 | |||
84 | /* EOL. */ | ||
85 | hbtransformsegment(specs[start].font, glyphs, codepoints, gstart, length); | ||
86 | |||
87 | /* Apply the transformation to glyph specs. */ | ||
88 | for (int i = 0, specidx = 0; i < len; i++) { | ||
89 | if (glyphs[i].mode & ATTR_WDUMMY) | ||
90 | continue; | ||
91 | if (glyphs[i].mode & ATTR_BOXDRAW) { | ||
92 | specidx++; | ||
93 | continue; | ||
94 | } | ||
95 | |||
96 | if (codepoints[i] != specs[specidx].glyph) | ||
97 | ((Glyph *)glyphs)[i].mode |= ATTR_LIGA; | ||
98 | |||
99 | specs[specidx++].glyph = codepoints[i]; | ||
100 | } | ||
101 | |||
102 | free(codepoints); | ||
103 | } | ||
104 | |||
105 | void | ||
106 | hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t *codepoints, int start, int length) | ||
107 | { | ||
108 | hb_font_t *font = hbfindfont(xfont); | ||
109 | if (font == NULL) | ||
110 | return; | ||
111 | |||
112 | Rune rune; | ||
113 | ushort mode = USHRT_MAX; | ||
114 | hb_buffer_t *buffer = hb_buffer_create(); | ||
115 | hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); | ||
116 | |||
117 | /* Fill buffer with codepoints. */ | ||
118 | for (int i = start; i < (start+length); i++) { | ||
119 | rune = string[i].u; | ||
120 | mode = string[i].mode; | ||
121 | if (mode & ATTR_WDUMMY) | ||
122 | rune = 0x0020; | ||
123 | hb_buffer_add_codepoints(buffer, &rune, 1, 0, 1); | ||
124 | } | ||
125 | |||
126 | /* Shape the segment. */ | ||
127 | hb_shape(font, buffer, NULL, 0); | ||
128 | |||
129 | /* Get new glyph info. */ | ||
130 | hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, NULL); | ||
131 | |||
132 | /* Write new codepoints. */ | ||
133 | for (int i = 0; i < length; i++) { | ||
134 | hb_codepoint_t gid = info[i].codepoint; | ||
135 | codepoints[start+i] = gid; | ||
136 | } | ||
137 | |||
138 | /* Cleanup. */ | ||
139 | hb_buffer_destroy(buffer); | ||
140 | } | ||
@@ -0,0 +1,7 @@ | |||
1 | #include <X11/Xft/Xft.h> | ||
2 | #include <hb.h> | ||
3 | #include <hb-ft.h> | ||
4 | |||
5 | void hbunloadfonts(); | ||
6 | void hbtransform(XftGlyphFontSpec *, const Glyph *, size_t, int, int); | ||
7 | |||
@@ -2655,7 +2655,8 @@ draw(void) | |||
2655 | 2655 | ||
2656 | drawregion(0, 0, term.col, term.row); | 2656 | drawregion(0, 0, term.col, term.row); |
2657 | xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], | 2657 | xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], |
2658 | term.ocx, term.ocy, term.line[term.ocy][term.ocx]); | 2658 | term.ocx, term.ocy, term.line[term.ocy][term.ocx], |
2659 | term.line[term.ocy], term.col); | ||
2659 | term.ocx = cx; | 2660 | term.ocx = cx; |
2660 | term.ocy = term.c.y; | 2661 | term.ocy = term.c.y; |
2661 | xfinishdraw(); | 2662 | xfinishdraw(); |
@@ -11,7 +11,8 @@ | |||
11 | #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) | 11 | #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) |
12 | #define DEFAULT(a, b) (a) = (a) ? (a) : (b) | 12 | #define DEFAULT(a, b) (a) = (a) ? (a) : (b) |
13 | #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) | 13 | #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) |
14 | #define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ | 14 | #define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP) & (~ATTR_LIGA)) != ((b).mode & (~ATTR_WRAP) & (~ATTR_LIGA)) || \ |
15 | (a).fg != (b).fg || \ | ||
15 | (a).bg != (b).bg) | 16 | (a).bg != (b).bg) |
16 | #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ | 17 | #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ |
17 | (t1.tv_nsec-t2.tv_nsec)/1E6) | 18 | (t1.tv_nsec-t2.tv_nsec)/1E6) |
@@ -34,6 +35,7 @@ enum glyph_attribute { | |||
34 | ATTR_WIDE = 1 << 9, | 35 | ATTR_WIDE = 1 << 9, |
35 | ATTR_WDUMMY = 1 << 10, | 36 | ATTR_WDUMMY = 1 << 10, |
36 | ATTR_BOXDRAW = 1 << 11, | 37 | ATTR_BOXDRAW = 1 << 11, |
38 | ATTR_LIGA = 1 << 12, | ||
37 | ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, | 39 | ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, |
38 | }; | 40 | }; |
39 | 41 | ||
@@ -25,7 +25,7 @@ enum win_mode { | |||
25 | 25 | ||
26 | void xbell(void); | 26 | void xbell(void); |
27 | void xclipcopy(void); | 27 | void xclipcopy(void); |
28 | void xdrawcursor(int, int, Glyph, int, int, Glyph); | 28 | void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int); |
29 | void xdrawline(Line, int, int, int); | 29 | void xdrawline(Line, int, int, int); |
30 | void xfinishdraw(void); | 30 | void xfinishdraw(void); |
31 | void xloadcols(void); | 31 | void xloadcols(void); |
@@ -20,6 +20,7 @@ char *argv0; | |||
20 | #include "arg.h" | 20 | #include "arg.h" |
21 | #include "st.h" | 21 | #include "st.h" |
22 | #include "win.h" | 22 | #include "win.h" |
23 | #include "hb.h" | ||
23 | 24 | ||
24 | /* types used in config.h */ | 25 | /* types used in config.h */ |
25 | typedef struct { | 26 | typedef struct { |
@@ -1083,6 +1084,9 @@ xunloadfont(Font *f) | |||
1083 | void | 1084 | void |
1084 | xunloadfonts(void) | 1085 | xunloadfonts(void) |
1085 | { | 1086 | { |
1087 | /* Clear Harfbuzz font cache. */ | ||
1088 | hbunloadfonts(); | ||
1089 | |||
1086 | /* Free the loaded fonts in the font cache. */ | 1090 | /* Free the loaded fonts in the font cache. */ |
1087 | while (frclen > 0) | 1091 | while (frclen > 0) |
1088 | XftFontClose(xw.dpy, frc[--frclen].font); | 1092 | XftFontClose(xw.dpy, frc[--frclen].font); |
@@ -1282,7 +1286,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x | |||
1282 | mode = glyphs[i].mode; | 1286 | mode = glyphs[i].mode; |
1283 | 1287 | ||
1284 | /* Skip dummy wide-character spacing. */ | 1288 | /* Skip dummy wide-character spacing. */ |
1285 | if (mode == ATTR_WDUMMY) | 1289 | if (mode & ATTR_WDUMMY) |
1286 | continue; | 1290 | continue; |
1287 | 1291 | ||
1288 | /* Determine font for glyph if different from previous glyph. */ | 1292 | /* Determine font for glyph if different from previous glyph. */ |
@@ -1394,6 +1398,9 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x | |||
1394 | numspecs++; | 1398 | numspecs++; |
1395 | } | 1399 | } |
1396 | 1400 | ||
1401 | /* Harfbuzz transformation for ligatures. */ | ||
1402 | hbtransform(specs, glyphs, len, x, y); | ||
1403 | |||
1397 | return numspecs; | 1404 | return numspecs; |
1398 | } | 1405 | } |
1399 | 1406 | ||
@@ -1547,14 +1554,17 @@ xdrawglyph(Glyph g, int x, int y) | |||
1547 | } | 1554 | } |
1548 | 1555 | ||
1549 | void | 1556 | void |
1550 | xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) | 1557 | xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int len) |
1551 | { | 1558 | { |
1552 | Color drawcol; | 1559 | Color drawcol; |
1553 | 1560 | ||
1554 | /* remove the old cursor */ | 1561 | /* remove the old cursor */ |
1555 | if (selected(ox, oy)) | 1562 | if (selected(ox, oy)) |
1556 | og.mode ^= ATTR_REVERSE; | 1563 | og.mode ^= ATTR_REVERSE; |
1557 | xdrawglyph(og, ox, oy); | 1564 | |
1565 | /* Redraw the line where cursor was previously. | ||
1566 | * It will restore the ligatures broken by the cursor. */ | ||
1567 | xdrawline(line, 0, oy, len); | ||
1558 | 1568 | ||
1559 | if (IS_SET(MODE_HIDE)) | 1569 | if (IS_SET(MODE_HIDE)) |
1560 | return; | 1570 | return; |