st-ligatures-20251007-0.9.3.diff - sites - public wiki contents of suckless.org
(HTM) git clone git://git.suckless.org/sites
(DIR) Log
(DIR) Files
(DIR) Refs
---
st-ligatures-20251007-0.9.3.diff (14223B)
---
1 diff --git a/Makefile b/Makefile
2 index 15db421..dfcea0f 100644
3 --- a/Makefile
4 +++ b/Makefile
5 @@ -4,7 +4,7 @@
6
7 include config.mk
8
9 -SRC = st.c x.c
10 +SRC = st.c x.c hb.c
11 OBJ = $(SRC:.c=.o)
12
13 all: st
14 @@ -16,7 +16,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
22 $(OBJ): config.h config.mk
23
24 diff --git a/config.mk b/config.mk
25 index 2fc854e..ec69d1a 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 @@ -29,7 +31,8 @@ STLDFLAGS = $(LIBS) $(LDFLAGS)
44 #CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE
45 #LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \
46 # `$(PKG_CONFIG) --libs fontconfig` \
47 -# `$(PKG_CONFIG) --libs freetype2`
48 +# `$(PKG_CONFIG) --libs freetype2` \
49 +# `$(PKG_CONFIG) --libs harfbuzz`
50 #MANPREFIX = ${PREFIX}/man
51
52 # compiler and linker
53 diff --git a/st.c b/st.c
54 index 8e57991..9641c85 100644
55 --- a/st.c
56 +++ b/st.c
57 @@ -2685,7 +2685,8 @@ draw(void)
58
59 drawregion(0, 0, term.col, term.row);
60 xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
61 - term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
62 + term.ocx, term.ocy, term.line[term.ocy][term.ocx],
63 + term.line[term.ocy], term.col);
64 term.ocx = cx;
65 term.ocy = term.c.y;
66 xfinishdraw();
67 diff --git a/st.h b/st.h
68 index fd3b0d8..142fdfe 100644
69 --- a/st.h
70 +++ b/st.h
71 @@ -11,7 +11,8 @@
72 #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
73 #define DEFAULT(a, b) (a) = (a) ? (a) : (b)
74 #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
75 -#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \
76 +#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode & (~ATTR_WRAP)) || \
77 + (a).fg != (b).fg || \
78 (a).bg != (b).bg)
79 #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
80 (t1.tv_nsec-t2.tv_nsec)/1E6)
81 diff --git a/win.h b/win.h
82 index 6de960d..94679e4 100644
83 --- a/win.h
84 +++ b/win.h
85 @@ -25,7 +25,7 @@ enum win_mode {
86
87 void xbell(void);
88 void xclipcopy(void);
89 -void xdrawcursor(int, int, Glyph, int, int, Glyph);
90 +void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int);
91 void xdrawline(Line, int, int, int);
92 void xfinishdraw(void);
93 void xloadcols(void);
94 diff --git a/x.c b/x.c
95 index d73152b..4d05b9f 100644
96 --- a/x.c
97 +++ b/x.c
98 @@ -19,6 +19,7 @@ char *argv0;
99 #include "arg.h"
100 #include "st.h"
101 #include "win.h"
102 +#include "hb.h"
103
104 /* types used in config.h */
105 typedef struct {
106 @@ -141,8 +142,9 @@ typedef struct {
107 } DC;
108
109 static inline ushort sixd_to_16bit(int);
110 +static void xresetfontsettings(ushort mode, Font **font, int *frcflags);
111 static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int);
112 -static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int);
113 +static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int, int);
114 static void xdrawglyph(Glyph, int, int);
115 static void xclear(int, int, int, int);
116 static int xgeommasktogravity(int);
117 @@ -757,7 +759,7 @@ xresize(int col, int row)
118 xclear(0, 0, win.w, win.h);
119
120 /* resize to new width */
121 - xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
122 + xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
123 }
124
125 ushort
126 @@ -1062,6 +1064,9 @@ xunloadfont(Font *f)
127 void
128 xunloadfonts(void)
129 {
130 + /* Clear Harfbuzz font cache. */
131 + hbunloadfonts();
132 +
133 /* Free the loaded fonts in the font cache. */
134 while (frclen > 0)
135 XftFontClose(xw.dpy, frc[--frclen].font);
136 @@ -1188,7 +1193,7 @@ xinit(int cols, int rows)
137 XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
138
139 /* font spec buffer */
140 - xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
141 + xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
142
143 /* Xft rendering context */
144 xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
145 @@ -1242,142 +1247,153 @@ xinit(int cols, int rows)
146 xsel.xtarget = XA_STRING;
147 }
148
149 +void
150 +xresetfontsettings(ushort mode, Font **font, int *frcflags)
151 +{
152 + *font = &dc.font;
153 + if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
154 + *font = &dc.ibfont;
155 + *frcflags = FRC_ITALICBOLD;
156 + } else if (mode & ATTR_ITALIC) {
157 + *font = &dc.ifont;
158 + *frcflags = FRC_ITALIC;
159 + } else if (mode & ATTR_BOLD) {
160 + *font = &dc.bfont;
161 + *frcflags = FRC_BOLD;
162 + }
163 +}
164 +
165 int
166 xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
167 {
168 float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp;
169 - ushort mode, prevmode = USHRT_MAX;
170 + ushort mode = glyphs[0].mode & ~ATTR_WRAP;
171 Font *font = &dc.font;
172 int frcflags = FRC_NORMAL;
173 - float runewidth = win.cw;
174 + float runewidth = win.cw * ((glyphs[0].mode & ATTR_WIDE) ? 2.0f : 1.0f);
175 Rune rune;
176 FT_UInt glyphidx;
177 FcResult fcres;
178 FcPattern *fcpattern, *fontpattern;
179 FcFontSet *fcsets[] = { NULL };
180 FcCharSet *fccharset;
181 - int i, f, numspecs = 0;
182 + int f, code_idx, numspecs = 0;
183 + float cluster_xp = xp, cluster_yp = yp;
184 + HbTransformData shaped = { 0 };
185
186 - for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
187 - /* Fetch rune and mode for current glyph. */
188 - rune = glyphs[i].u;
189 - mode = glyphs[i].mode;
190 + /* Initial values. */
191 + xresetfontsettings(mode, &font, &frcflags);
192
193 - /* Skip dummy wide-character spacing. */
194 - if (mode == ATTR_WDUMMY)
195 + /* Shape the segment. */
196 + hbtransform(&shaped, font->match, glyphs, 0, len);
197 + xp = winx; yp = winy + font->ascent;
198 + cluster_xp = xp; cluster_yp = yp;
199 +
200 + for (code_idx = 0; code_idx < shaped.count; code_idx++) {
201 + int idx = shaped.glyphs[code_idx].cluster;
202 +
203 + if (glyphs[idx].mode & ATTR_WDUMMY)
204 continue;
205
206 - /* Determine font for glyph if different from previous glyph. */
207 - if (prevmode != mode) {
208 - prevmode = mode;
209 - font = &dc.font;
210 - frcflags = FRC_NORMAL;
211 - runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f);
212 - if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
213 - font = &dc.ibfont;
214 - frcflags = FRC_ITALICBOLD;
215 - } else if (mode & ATTR_ITALIC) {
216 - font = &dc.ifont;
217 - frcflags = FRC_ITALIC;
218 - } else if (mode & ATTR_BOLD) {
219 - font = &dc.bfont;
220 - frcflags = FRC_BOLD;
221 - }
222 - yp = winy + font->ascent;
223 + /* Advance the drawing cursor if we've moved to a new cluster */
224 + if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluster) {
225 + xp += runewidth * (idx - shaped.glyphs[code_idx - 1].cluster);
226 + cluster_xp = xp;
227 + cluster_yp = yp;
228 }
229
230 - /* Lookup character index with default font. */
231 - glyphidx = XftCharIndex(xw.dpy, font->match, rune);
232 - if (glyphidx) {
233 + if (shaped.glyphs[code_idx].codepoint != 0) {
234 + /* If symbol is found, put it into the specs. */
235 specs[numspecs].font = font->match;
236 - specs[numspecs].glyph = glyphidx;
237 - specs[numspecs].x = (short)xp;
238 - specs[numspecs].y = (short)yp;
239 - xp += runewidth;
240 + specs[numspecs].glyph = shaped.glyphs[code_idx].codepoint;
241 + specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.);
242 + specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.);
243 + cluster_xp += shaped.positions[code_idx].x_advance / 64.;
244 + cluster_yp += shaped.positions[code_idx].y_advance / 64.;
245 numspecs++;
246 - continue;
247 - }
248 -
249 - /* Fallback on font cache, search the font cache for match. */
250 - for (f = 0; f < frclen; f++) {
251 - glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
252 - /* Everything correct. */
253 - if (glyphidx && frc[f].flags == frcflags)
254 - break;
255 - /* We got a default font for a not found glyph. */
256 - if (!glyphidx && frc[f].flags == frcflags
257 - && frc[f].unicodep == rune) {
258 - break;
259 + } else {
260 + /* If it's not found, try to fetch it through the font cache. */
261 + rune = glyphs[idx].u;
262 + for (f = 0; f < frclen; f++) {
263 + glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
264 + /* Everything correct. */
265 + if (glyphidx && frc[f].flags == frcflags)
266 + break;
267 + /* We got a default font for a not found glyph. */
268 + if (!glyphidx && frc[f].flags == frcflags
269 + && frc[f].unicodep == rune) {
270 + break;
271 + }
272 }
273 - }
274 -
275 - /* Nothing was found. Use fontconfig to find matching font. */
276 - if (f >= frclen) {
277 - if (!font->set)
278 - font->set = FcFontSort(0, font->pattern,
279 - 1, 0, &fcres);
280 - fcsets[0] = font->set;
281
282 - /*
283 - * Nothing was found in the cache. Now use
284 - * some dozen of Fontconfig calls to get the
285 - * font for one single character.
286 - *
287 - * Xft and fontconfig are design failures.
288 - */
289 - fcpattern = FcPatternDuplicate(font->pattern);
290 - fccharset = FcCharSetCreate();
291 -
292 - FcCharSetAddChar(fccharset, rune);
293 - FcPatternAddCharSet(fcpattern, FC_CHARSET,
294 - fccharset);
295 - FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
296 -
297 - FcConfigSubstitute(0, fcpattern,
298 - FcMatchPattern);
299 - FcDefaultSubstitute(fcpattern);
300 -
301 - fontpattern = FcFontSetMatch(0, fcsets, 1,
302 - fcpattern, &fcres);
303 -
304 - /* Allocate memory for the new cache entry. */
305 - if (frclen >= frccap) {
306 - frccap += 16;
307 - frc = xrealloc(frc, frccap * sizeof(Fontcache));
308 + /* Nothing was found. Use fontconfig to find matching font. */
309 + if (f >= frclen) {
310 + if (!font->set)
311 + font->set = FcFontSort(0, font->pattern,
312 + 1, 0, &fcres);
313 + fcsets[0] = font->set;
314 +
315 + /*
316 + * Nothing was found in the cache. Now use
317 + * some dozen of Fontconfig calls to get the
318 + * font for one single character.
319 + *
320 + * Xft and fontconfig are design failures.
321 + */
322 + fcpattern = FcPatternDuplicate(font->pattern);
323 + fccharset = FcCharSetCreate();
324 +
325 + FcCharSetAddChar(fccharset, rune);
326 + FcPatternAddCharSet(fcpattern, FC_CHARSET,
327 + fccharset);
328 + FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
329 +
330 + FcConfigSubstitute(0, fcpattern,
331 + FcMatchPattern);
332 + FcDefaultSubstitute(fcpattern);
333 +
334 + fontpattern = FcFontSetMatch(0, fcsets, 1,
335 + fcpattern, &fcres);
336 +
337 + /* Allocate memory for the new cache entry. */
338 + if (frclen >= frccap) {
339 + frccap += 16;
340 + frc = xrealloc(frc, frccap * sizeof(Fontcache));
341 + }
342 +
343 + frc[frclen].font = XftFontOpenPattern(xw.dpy,
344 + fontpattern);
345 + if (!frc[frclen].font)
346 + die("XftFontOpenPattern failed seeking fallback font: %s\n",
347 + strerror(errno));
348 + frc[frclen].flags = frcflags;
349 + frc[frclen].unicodep = rune;
350 +
351 + glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
352 +
353 + f = frclen;
354 + frclen++;
355 +
356 + FcPatternDestroy(fcpattern);
357 + FcCharSetDestroy(fccharset);
358 }
359
360 - frc[frclen].font = XftFontOpenPattern(xw.dpy,
361 - fontpattern);
362 - if (!frc[frclen].font)
363 - die("XftFontOpenPattern failed seeking fallback font: %s\n",
364 - strerror(errno));
365 - frc[frclen].flags = frcflags;
366 - frc[frclen].unicodep = rune;
367 -
368 - glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
369 -
370 - f = frclen;
371 - frclen++;
372 -
373 - FcPatternDestroy(fcpattern);
374 - FcCharSetDestroy(fccharset);
375 + specs[numspecs].font = frc[f].font;
376 + specs[numspecs].glyph = glyphidx;
377 + specs[numspecs].x = (short)xp;
378 + specs[numspecs].y = (short)yp;
379 + numspecs++;
380 }
381 -
382 - specs[numspecs].font = frc[f].font;
383 - specs[numspecs].glyph = glyphidx;
384 - specs[numspecs].x = (short)xp;
385 - specs[numspecs].y = (short)yp;
386 - xp += runewidth;
387 - numspecs++;
388 }
389
390 + /* Cleanup and get ready for next segment. */
391 + hbcleanup(&shaped);
392 return numspecs;
393 }
394
395 void
396 -xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
397 +xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y, int charlen)
398 {
399 - int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
400 int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
401 width = charlen * win.cw;
402 Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
403 @@ -1513,21 +1529,24 @@ void
404 xdrawglyph(Glyph g, int x, int y)
405 {
406 int numspecs;
407 - XftGlyphFontSpec spec;
408 + XftGlyphFontSpec *specs = xw.specbuf;
409
410 - numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
411 - xdrawglyphfontspecs(&spec, g, numspecs, x, y);
412 + numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y);
413 + xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? 2 : 1);
414 }
415
416 void
417 -xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
418 +xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int len)
419 {
420 Color drawcol;
421
422 /* remove the old cursor */
423 if (selected(ox, oy))
424 og.mode ^= ATTR_REVERSE;
425 - xdrawglyph(og, ox, oy);
426 +
427 + /* Redraw the line where cursor was previously.
428 + * It will restore the ligatures broken by the cursor. */
429 + xdrawline(line, 0, oy, len);
430
431 if (IS_SET(MODE_HIDE))
432 return;
433 @@ -1661,18 +1680,16 @@ xdrawline(Line line, int x1, int y1, int x2)
434 Glyph base, new;
435 XftGlyphFontSpec *specs = xw.specbuf;
436
437 - numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
438 i = ox = 0;
439 - for (x = x1; x < x2 && i < numspecs; x++) {
440 + for (x = x1; x < x2; x++) {
441 new = line[x];
442 if (new.mode == ATTR_WDUMMY)
443 continue;
444 if (selected(x, y1))
445 new.mode ^= ATTR_REVERSE;
446 - if (i > 0 && ATTRCMP(base, new)) {
447 - xdrawglyphfontspecs(specs, base, i, ox, y1);
448 - specs += i;
449 - numspecs -= i;
450 + if ((i > 0) && ATTRCMP(base, new)) {
451 + numspecs = xmakeglyphfontspecs(specs, &line[ox], x - ox, ox, y1);
452 + xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x - ox);
453 i = 0;
454 }
455 if (i == 0) {
456 @@ -1681,8 +1698,10 @@ xdrawline(Line line, int x1, int y1, int x2)
457 }
458 i++;
459 }
460 - if (i > 0)
461 - xdrawglyphfontspecs(specs, base, i, ox, y1);
462 + if (i > 0) {
463 + numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox, y1);
464 + xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox);
465 + }
466 }
467
468 void