st-ligatures-boxdraw-20210824-0.8.4.diff - sites - public wiki contents of suck… | |
git clone git://git.suckless.org/sites | |
Log | |
Files | |
Refs | |
--- | |
st-ligatures-boxdraw-20210824-0.8.4.diff (8360B) | |
--- | |
1 diff --git a/Makefile b/Makefile | |
2 index 6dfa212..2ffd3c8 100644 | |
3 --- a/Makefile | |
4 +++ b/Makefile | |
5 @@ -4,7 +4,7 @@ | |
6 | |
7 include config.mk | |
8 | |
9 -SRC = st.c x.c boxdraw.c | |
10 +SRC = st.c x.c boxdraw.c hb.c | |
11 OBJ = $(SRC:.c=.o) | |
12 | |
13 all: options st | |
14 @@ -22,7 +22,8 @@ config.h: | |
15 $(CC) $(STCFLAGS) -c $< | |
16 | |
17 st.o: config.h st.h win.h | |
18 -x.o: arg.h config.h st.h win.h | |
19 +x.o: arg.h config.h st.h win.h hb.h | |
20 +hb.o: st.h | |
21 boxdraw.o: config.h st.h boxdraw_data.h | |
22 | |
23 $(OBJ): config.h config.mk | |
24 diff --git a/config.mk b/config.mk | |
25 index c070a4a..3d236f0 100644 | |
26 --- a/config.mk | |
27 +++ b/config.mk | |
28 @@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config | |
29 # includes and libs | |
30 INCS = -I$(X11INC) \ | |
31 `$(PKG_CONFIG) --cflags fontconfig` \ | |
32 - `$(PKG_CONFIG) --cflags freetype2` | |
33 + `$(PKG_CONFIG) --cflags freetype2` \ | |
34 + `$(PKG_CONFIG) --cflags harfbuzz` | |
35 LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ | |
36 `$(PKG_CONFIG) --libs fontconfig` \ | |
37 - `$(PKG_CONFIG) --libs freetype2` | |
38 + `$(PKG_CONFIG) --libs freetype2` \ | |
39 + `$(PKG_CONFIG) --libs harfbuzz` | |
40 | |
41 # flags | |
42 STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 | |
43 diff --git a/hb.c b/hb.c | |
44 new file mode 100644 | |
45 index 0000000..a47300e | |
46 --- /dev/null | |
47 +++ b/hb.c | |
48 @@ -0,0 +1,149 @@ | |
49 +#include <stdlib.h> | |
50 +#include <stdio.h> | |
51 +#include <math.h> | |
52 +#include <X11/Xft/Xft.h> | |
53 +#include <hb.h> | |
54 +#include <hb-ft.h> | |
55 + | |
56 +#include "st.h" | |
57 + | |
58 +#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, … | |
59 + | |
60 +void hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoi… | |
61 +hb_font_t *hbfindfont(XftFont *match); | |
62 + | |
63 +typedef struct { | |
64 + XftFont *match; | |
65 + hb_font_t *font; | |
66 +} HbFontMatch; | |
67 + | |
68 +static int hbfontslen = 0; | |
69 +static HbFontMatch *hbfontcache = NULL; | |
70 + | |
71 +/* | |
72 + * Poplulate the array with a list of font features, wrapped in FEATURE… | |
73 + * e. g. | |
74 + * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') | |
75 + */ | |
76 +hb_feature_t features[] = { }; | |
77 + | |
78 +void | |
79 +hbunloadfonts() | |
80 +{ | |
81 + for (int i = 0; i < hbfontslen; i++) { | |
82 + hb_font_destroy(hbfontcache[i].font); | |
83 + XftUnlockFace(hbfontcache[i].match); | |
84 + } | |
85 + | |
86 + if (hbfontcache != NULL) { | |
87 + free(hbfontcache); | |
88 + hbfontcache = NULL; | |
89 + } | |
90 + hbfontslen = 0; | |
91 +} | |
92 + | |
93 +hb_font_t * | |
94 +hbfindfont(XftFont *match) | |
95 +{ | |
96 + for (int i = 0; i < hbfontslen; i++) { | |
97 + if (hbfontcache[i].match == match) | |
98 + return hbfontcache[i].font; | |
99 + } | |
100 + | |
101 + /* Font not found in cache, caching it now. */ | |
102 + hbfontcache = realloc(hbfontcache, sizeof(HbFontMatch) * (hbfon… | |
103 + FT_Face face = XftLockFace(match); | |
104 + hb_font_t *font = hb_ft_font_create(face, NULL); | |
105 + if (font == NULL) | |
106 + die("Failed to load Harfbuzz font."); | |
107 + | |
108 + hbfontcache[hbfontslen].match = match; | |
109 + hbfontcache[hbfontslen].font = font; | |
110 + hbfontslen += 1; | |
111 + | |
112 + return font; | |
113 +} | |
114 + | |
115 +void | |
116 +hbtransform(XftGlyphFontSpec *specs, const Glyph *glyphs, size_t len, i… | |
117 +{ | |
118 + int start = 0, length = 1, gstart = 0; | |
119 + hb_codepoint_t *codepoints = calloc((unsigned int)len, sizeof(h… | |
120 + | |
121 + for (int idx = 1, specidx = 1; idx < len; idx++) { | |
122 + if (glyphs[idx].mode & ATTR_WDUMMY) { | |
123 + length += 1; | |
124 + continue; | |
125 + } | |
126 + | |
127 + if (specs[specidx].font != specs[start].font || ATTRCMP… | |
128 + hbtransformsegment(specs[start].font, glyphs, c… | |
129 + | |
130 + /* Reset the sequence. */ | |
131 + length = 1; | |
132 + start = specidx; | |
133 + gstart = idx; | |
134 + } else { | |
135 + length += 1; | |
136 + } | |
137 + | |
138 + specidx++; | |
139 + } | |
140 + | |
141 + /* EOL. */ | |
142 + hbtransformsegment(specs[start].font, glyphs, codepoints, gstar… | |
143 + | |
144 + /* Apply the transformation to glyph specs. */ | |
145 + for (int i = 0, specidx = 0; i < len; i++) { | |
146 + if (glyphs[i].mode & ATTR_WDUMMY) | |
147 + continue; | |
148 + if (glyphs[i].mode & ATTR_BOXDRAW) { | |
149 + specidx++; | |
150 + continue; | |
151 + } | |
152 + | |
153 + if (codepoints[i] != specs[specidx].glyph) | |
154 + ((Glyph *)glyphs)[i].mode |= ATTR_LIGA; | |
155 + | |
156 + specs[specidx++].glyph = codepoints[i]; | |
157 + } | |
158 + | |
159 + free(codepoints); | |
160 +} | |
161 + | |
162 +void | |
163 +hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t … | |
164 +{ | |
165 + hb_font_t *font = hbfindfont(xfont); | |
166 + if (font == NULL) | |
167 + return; | |
168 + | |
169 + Rune rune; | |
170 + ushort mode = USHRT_MAX; | |
171 + hb_buffer_t *buffer = hb_buffer_create(); | |
172 + hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); | |
173 + | |
174 + /* Fill buffer with codepoints. */ | |
175 + for (int i = start; i < (start+length); i++) { | |
176 + rune = string[i].u; | |
177 + mode = string[i].mode; | |
178 + if (mode & ATTR_WDUMMY) | |
179 + rune = 0x0020; | |
180 + hb_buffer_add_codepoints(buffer, &rune, 1, 0, 1); | |
181 + } | |
182 + | |
183 + /* Shape the segment. */ | |
184 + hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_fea… | |
185 + | |
186 + /* Get new glyph info. */ | |
187 + hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, NULL); | |
188 + | |
189 + /* Write new codepoints. */ | |
190 + for (int i = 0; i < length; i++) { | |
191 + hb_codepoint_t gid = info[i].codepoint; | |
192 + codepoints[start+i] = gid; | |
193 + } | |
194 + | |
195 + /* Cleanup. */ | |
196 + hb_buffer_destroy(buffer); | |
197 +} | |
198 diff --git a/hb.h b/hb.h | |
199 new file mode 100644 | |
200 index 0000000..07888df | |
201 --- /dev/null | |
202 +++ b/hb.h | |
203 @@ -0,0 +1,6 @@ | |
204 +#include <X11/Xft/Xft.h> | |
205 +#include <hb.h> | |
206 +#include <hb-ft.h> | |
207 + | |
208 +void hbunloadfonts(); | |
209 +void hbtransform(XftGlyphFontSpec *, const Glyph *, size_t, int, int); | |
210 diff --git a/st.c b/st.c | |
211 index 248728f..9101ae8 100644 | |
212 --- a/st.c | |
213 +++ b/st.c | |
214 @@ -2584,7 +2584,8 @@ draw(void) | |
215 | |
216 drawregion(0, 0, term.col, term.row); | |
217 xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], | |
218 - term.ocx, term.ocy, term.line[term.ocy][term.oc… | |
219 + term.ocx, term.ocy, term.line[term.ocy][term.oc… | |
220 + term.line[term.ocy], term.col); | |
221 term.ocx = cx; | |
222 term.ocy = term.c.y; | |
223 xfinishdraw(); | |
224 diff --git a/st.h b/st.h | |
225 index 8d26273..133dc08 100644 | |
226 --- a/st.h | |
227 +++ b/st.h | |
228 @@ -11,7 +11,8 @@ | |
229 #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) | |
230 #define DEFAULT(a, b) (a) = (a) ? (a) : (b) | |
231 #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b)… | |
232 -#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg !=… | |
233 +#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP) & (~ATTR… | |
234 + (a).fg != (b).fg || \ | |
235 (a).bg != (b).bg) | |
236 #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ | |
237 (t1.tv_nsec-t2.tv_nsec)/1E6) | |
238 @@ -34,6 +35,7 @@ enum glyph_attribute { | |
239 ATTR_WIDE = 1 << 9, | |
240 ATTR_WDUMMY = 1 << 10, | |
241 ATTR_BOXDRAW = 1 << 11, | |
242 + ATTR_LIGA = 1 << 12, | |
243 ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, | |
244 }; | |
245 | |
246 diff --git a/win.h b/win.h | |
247 index a6ef1b9..bc0d180 100644 | |
248 --- a/win.h | |
249 +++ b/win.h | |
250 @@ -25,7 +25,7 @@ enum win_mode { | |
251 | |
252 void xbell(void); | |
253 void xclipcopy(void); | |
254 -void xdrawcursor(int, int, Glyph, int, int, Glyph); | |
255 +void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int); | |
256 void xdrawline(Line, int, int, int); | |
257 void xfinishdraw(void); | |
258 void xloadcols(void); | |
259 diff --git a/x.c b/x.c | |
260 index 9b29264..af93cad 100644 | |
261 --- a/x.c | |
262 +++ b/x.c | |
263 @@ -19,6 +19,7 @@ char *argv0; | |
264 #include "arg.h" | |
265 #include "st.h" | |
266 #include "win.h" | |
267 +#include "hb.h" | |
268 | |
269 /* types used in config.h */ | |
270 typedef struct { | |
271 @@ -1031,6 +1032,9 @@ xunloadfont(Font *f) | |
272 void | |
273 xunloadfonts(void) | |
274 { | |
275 + /* Clear Harfbuzz font cache. */ | |
276 + hbunloadfonts(); | |
277 + | |
278 /* Free the loaded fonts in the font cache. */ | |
279 while (frclen > 0) | |
280 XftFontClose(xw.dpy, frc[--frclen].font); | |
281 @@ -1231,7 +1235,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const… | |
282 mode = glyphs[i].mode; | |
283 | |
284 /* Skip dummy wide-character spacing. */ | |
285 - if (mode == ATTR_WDUMMY) | |
286 + if (mode & ATTR_WDUMMY) | |
287 continue; | |
288 | |
289 /* Determine font for glyph if different from previous … | |
290 @@ -1343,6 +1347,9 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const… | |
291 numspecs++; | |
292 } | |
293 | |
294 + /* Harfbuzz transformation for ligatures. */ | |
295 + hbtransform(specs, glyphs, len, x, y); | |
296 + | |
297 return numspecs; | |
298 } | |
299 | |
300 @@ -1496,14 +1503,17 @@ xdrawglyph(Glyph g, int x, int y) | |
301 } | |
302 | |
303 void | |
304 -xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) | |
305 +xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line lin… | |
306 { | |
307 Color drawcol; | |
308 | |
309 /* remove the old cursor */ | |
310 if (selected(ox, oy)) | |
311 og.mode ^= ATTR_REVERSE; | |
312 - xdrawglyph(og, ox, oy); | |
313 + | |
314 + /* Redraw the line where cursor was previously. | |
315 + * It will restore the ligatures broken by the cursor. */ | |
316 + xdrawline(line, 0, oy, len); | |
317 | |
318 if (IS_SET(MODE_HIDE)) | |
319 return; |