aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Crute <mike@crute.us>2020-12-22 16:09:42 -0800
committerMike Crute <mike@crute.us>2023-04-25 20:45:27 -0700
commit49af5d59d8bed15678c634d11e86364c8801dc82 (patch)
tree31acbd7d321a0a8303fee0d6b6b65f05100439df
parent0ddcd0455171f2c52ae68d92bbddecb834350522 (diff)
downloadst-patched-49af5d59d8bed15678c634d11e86364c8801dc82.tar.bz2
st-patched-49af5d59d8bed15678c634d11e86364c8801dc82.tar.xz
st-patched-49af5d59d8bed15678c634d11e86364c8801dc82.zip
Apply st-ligatures-boxdraw-20200430-0.8.3
-rw-r--r--Makefile5
-rw-r--r--config.mk6
-rw-r--r--hb.c140
-rw-r--r--hb.h7
-rw-r--r--st.c3
-rw-r--r--st.h4
-rw-r--r--win.h2
-rw-r--r--x.c16
8 files changed, 173 insertions, 10 deletions
diff --git a/Makefile b/Makefile
index 6dfa212..2ffd3c8 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@
4 4
5include config.mk 5include config.mk
6 6
7SRC = st.c x.c boxdraw.c 7SRC = st.c x.c boxdraw.c hb.c
8OBJ = $(SRC:.c=.o) 8OBJ = $(SRC:.c=.o)
9 9
10all: options st 10all: options st
@@ -22,7 +22,8 @@ config.h:
22 $(CC) $(STCFLAGS) -c $< 22 $(CC) $(STCFLAGS) -c $<
23 23
24st.o: config.h st.h win.h 24st.o: config.h st.h win.h
25x.o: arg.h config.h st.h win.h 25x.o: arg.h config.h st.h win.h hb.h
26hb.o: st.h
26boxdraw.o: config.h st.h boxdraw_data.h 27boxdraw.o: config.h st.h boxdraw_data.h
27 28
28$(OBJ): config.h config.mk 29$(OBJ): config.h config.mk
diff --git a/config.mk b/config.mk
index 1e306f8..3e13e53 100644
--- a/config.mk
+++ b/config.mk
@@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config
15# includes and libs 15# includes and libs
16INCS = -I$(X11INC) \ 16INCS = -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`
19LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ 20LIBS = -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
24STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 26STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
diff --git a/hb.c b/hb.c
new file mode 100644
index 0000000..467bcac
--- /dev/null
+++ b/hb.c
@@ -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
10void hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t *codepoints, int start, int length);
11hb_font_t *hbfindfont(XftFont *match);
12
13typedef struct {
14 XftFont *match;
15 hb_font_t *font;
16} HbFontMatch;
17
18static int hbfontslen = 0;
19static HbFontMatch *hbfontcache = NULL;
20
21void
22hbunloadfonts()
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
36hb_font_t *
37hbfindfont(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
58void
59hbtransform(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
105void
106hbtransformsegment(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}
diff --git a/hb.h b/hb.h
new file mode 100644
index 0000000..b3e02d0
--- /dev/null
+++ b/hb.h
@@ -0,0 +1,7 @@
1#include <X11/Xft/Xft.h>
2#include <hb.h>
3#include <hb-ft.h>
4
5void hbunloadfonts();
6void hbtransform(XftGlyphFontSpec *, const Glyph *, size_t, int, int);
7
diff --git a/st.c b/st.c
index 398bb98..7154f00 100644
--- a/st.c
+++ b/st.c
@@ -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();
diff --git a/st.h b/st.h
index 808f5f7..b889a88 100644
--- a/st.h
+++ b/st.h
@@ -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
diff --git a/win.h b/win.h
index 6de960d..94679e4 100644
--- a/win.h
+++ b/win.h
@@ -25,7 +25,7 @@ enum win_mode {
25 25
26void xbell(void); 26void xbell(void);
27void xclipcopy(void); 27void xclipcopy(void);
28void xdrawcursor(int, int, Glyph, int, int, Glyph); 28void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int);
29void xdrawline(Line, int, int, int); 29void xdrawline(Line, int, int, int);
30void xfinishdraw(void); 30void xfinishdraw(void);
31void xloadcols(void); 31void xloadcols(void);
diff --git a/x.c b/x.c
index f425641..deb8ea5 100644
--- a/x.c
+++ b/x.c
@@ -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 */
25typedef struct { 26typedef struct {
@@ -1083,6 +1084,9 @@ xunloadfont(Font *f)
1083void 1084void
1084xunloadfonts(void) 1085xunloadfonts(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
1549void 1556void
1550xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) 1557xdrawcursor(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;