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