st-ligatures-boxdraw-20241226-0.9.2.diff (19317B)
1 diff --git a/Makefile b/Makefile 2 index a64b4c2..05124bf 100644 3 --- a/Makefile 4 +++ b/Makefile 5 @@ -3,9 +3,9 @@ 6 .POSIX: 7 8 include config.mk 9 10 -SRC = st.c x.c boxdraw.c 11 +SRC = st.c x.c boxdraw.c hb.c 12 OBJ = $(SRC:.c=.o) 13 14 all: st 15 16 @@ -15,10 +15,11 @@ config.h: 17 .c.o: 18 $(CC) $(STCFLAGS) -c $< 19 20 st.o: config.h st.h win.h 21 -x.o: arg.h config.h st.h win.h 22 +x.o: arg.h config.h st.h win.h hb.h 23 boxdraw.o: config.h st.h boxdraw_data.h 24 +hb.o: st.h 25 26 $(OBJ): config.h config.mk 27 28 st: $(OBJ) 29 diff --git a/config.mk b/config.mk 30 index fdc29a7..6833b3b 100644 31 --- a/config.mk 32 +++ b/config.mk 33 @@ -14,12 +14,14 @@ PKG_CONFIG = pkg-config 34 35 # includes and libs 36 INCS = -I$(X11INC) \ 37 `$(PKG_CONFIG) --cflags fontconfig` \ 38 - `$(PKG_CONFIG) --cflags freetype2` 39 + `$(PKG_CONFIG) --cflags freetype2` \ 40 + `$(PKG_CONFIG) --cflags harfbuzz` 41 LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ 42 `$(PKG_CONFIG) --libs fontconfig` \ 43 - `$(PKG_CONFIG) --libs freetype2` 44 + `$(PKG_CONFIG) --libs freetype2` \ 45 + `$(PKG_CONFIG) --libs harfbuzz` 46 47 # flags 48 STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 49 STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS) 50 @@ -28,9 +30,10 @@ STLDFLAGS = $(LIBS) $(LDFLAGS) 51 # OpenBSD: 52 #CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE 53 #LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \ 54 # `$(PKG_CONFIG) --libs fontconfig` \ 55 -# `$(PKG_CONFIG) --libs freetype2` 56 +# `$(PKG_CONFIG) --libs freetype2` \ 57 +# `$(PKG_CONFIG) --libs harfbuzz` 58 #MANPREFIX = ${PREFIX}/man 59 60 # compiler and linker 61 # CC = c99 62 diff --git a/hb.c b/hb.c 63 new file mode 100644 64 index 0000000..99412c8 65 --- /dev/null 66 +++ b/hb.c 67 @@ -0,0 +1,125 @@ 68 +#include <stdlib.h> 69 +#include <stdio.h> 70 +#include <math.h> 71 +#include <X11/Xft/Xft.h> 72 +#include <X11/cursorfont.h> 73 +#include <hb.h> 74 +#include <hb-ft.h> 75 + 76 +#include "st.h" 77 +#include "hb.h" 78 + 79 +#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start = HB_FEATURE_GLOBAL_START, .end = HB_FEATURE_GLOBAL_END } 80 +#define BUFFER_STEP 256 81 + 82 +hb_font_t *hbfindfont(XftFont *match); 83 + 84 +typedef struct { 85 + XftFont *match; 86 + hb_font_t *font; 87 +} HbFontMatch; 88 + 89 +typedef struct { 90 + size_t capacity; 91 + HbFontMatch *fonts; 92 +} HbFontCache; 93 + 94 +static HbFontCache hbfontcache = { 0, NULL }; 95 + 96 +typedef struct { 97 + size_t capacity; 98 + Rune *runes; 99 +} RuneBuffer; 100 + 101 +static RuneBuffer hbrunebuffer = { 0, NULL }; 102 + 103 +/* 104 + * Poplulate the array with a list of font features, wrapped in FEATURE macro, 105 + * e. g. 106 + * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') 107 + */ 108 +hb_feature_t features[] = { }; 109 + 110 +void 111 +hbunloadfonts() 112 +{ 113 + for (int i = 0; i < hbfontcache.capacity; i++) { 114 + hb_font_destroy(hbfontcache.fonts[i].font); 115 + XftUnlockFace(hbfontcache.fonts[i].match); 116 + } 117 + 118 + if (hbfontcache.fonts != NULL) { 119 + free(hbfontcache.fonts); 120 + hbfontcache.fonts = NULL; 121 + } 122 + hbfontcache.capacity = 0; 123 +} 124 + 125 +hb_font_t * 126 +hbfindfont(XftFont *match) 127 +{ 128 + for (int i = 0; i < hbfontcache.capacity; i++) { 129 + if (hbfontcache.fonts[i].match == match) 130 + return hbfontcache.fonts[i].font; 131 + } 132 + 133 + /* Font not found in cache, caching it now. */ 134 + hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * (hbfontcache.capacity + 1)); 135 + FT_Face face = XftLockFace(match); 136 + hb_font_t *font = hb_ft_font_create(face, NULL); 137 + if (font == NULL) 138 + die("Failed to load Harfbuzz font."); 139 + 140 + hbfontcache.fonts[hbfontcache.capacity].match = match; 141 + hbfontcache.fonts[hbfontcache.capacity].font = font; 142 + hbfontcache.capacity += 1; 143 + 144 + return font; 145 +} 146 + 147 +void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, int start, int length) { 148 + ushort mode = USHRT_MAX; 149 + unsigned int glyph_count; 150 + int rune_idx, glyph_idx, end = start + length; 151 + 152 + hb_font_t *font = hbfindfont(xfont); 153 + if (font == NULL) 154 + return; 155 + 156 + hb_buffer_t *buffer = hb_buffer_create(); 157 + hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); 158 + hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); 159 + 160 + /* Resize the buffer if required length is larger. */ 161 + if (hbrunebuffer.capacity < length) { 162 + hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_STEP; 163 + hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer.capacity * sizeof(Rune)); 164 + } 165 + 166 + /* Fill buffer with codepoints. */ 167 + for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, rune_idx++) { 168 + hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u; 169 + mode = glyphs[glyph_idx].mode; 170 + if (mode & ATTR_WDUMMY) 171 + hbrunebuffer.runes[rune_idx] = 0x0020; 172 + } 173 + hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, length); 174 + 175 + /* Shape the segment. */ 176 + hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t)); 177 + 178 + /* Get new glyph info. */ 179 + hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_count); 180 + hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &glyph_count); 181 + 182 + /* Fill the output. */ 183 + data->buffer = buffer; 184 + data->glyphs = info; 185 + data->positions = pos; 186 + data->count = glyph_count; 187 +} 188 + 189 +void hbcleanup(HbTransformData *data) { 190 + hb_buffer_destroy(data->buffer); 191 + memset(data, 0, sizeof(HbTransformData)); 192 +} 193 diff --git a/hb.h b/hb.h 194 new file mode 100644 195 index 0000000..3b0ef44 196 --- /dev/null 197 +++ b/hb.h 198 @@ -0,0 +1,14 @@ 199 +#include <X11/Xft/Xft.h> 200 +#include <hb.h> 201 +#include <hb-ft.h> 202 + 203 +typedef struct { 204 + hb_buffer_t *buffer; 205 + hb_glyph_info_t *glyphs; 206 + hb_glyph_position_t *positions; 207 + unsigned int count; 208 +} HbTransformData; 209 + 210 +void hbunloadfonts(); 211 +void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int); 212 +void hbcleanup(HbTransformData *); 213 diff --git a/st.c b/st.c 214 index ec6fbf3..1385f77 100644 215 --- a/st.c 216 +++ b/st.c 217 @@ -2661,9 +2661,10 @@ draw(void) 218 cx--; 219 220 drawregion(0, 0, term.col, term.row); 221 xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], 222 - term.ocx, term.ocy, term.line[term.ocy][term.ocx]); 223 + term.ocx, term.ocy, term.line[term.ocy][term.ocx], 224 + term.line[term.ocy], term.col); 225 term.ocx = cx; 226 term.ocy = term.c.y; 227 xfinishdraw(); 228 if (ocx != term.ocx || ocy != term.ocy) 229 diff --git a/st.h b/st.h 230 index 808f5f7..ae41368 100644 231 --- a/st.h 232 +++ b/st.h 233 @@ -10,9 +10,10 @@ 234 #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) 235 #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) 236 #define DEFAULT(a, b) (a) = (a) ? (a) : (b) 237 #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) 238 -#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ 239 +#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode & (~ATTR_WRAP)) || \ 240 + (a).fg != (b).fg || \ 241 (a).bg != (b).bg) 242 #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ 243 (t1.tv_nsec-t2.tv_nsec)/1E6) 244 #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) 245 diff --git a/win.h b/win.h 246 index 6de960d..94679e4 100644 247 --- a/win.h 248 +++ b/win.h 249 @@ -24,9 +24,9 @@ enum win_mode { 250 }; 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 int xsetcolorname(int, const char *); 260 diff --git a/x.c b/x.c 261 index 978a8fc..c2d9993 100644 262 --- a/x.c 263 +++ b/x.c 264 @@ -18,8 +18,9 @@ 265 char *argv0; 266 #include "arg.h" 267 #include "st.h" 268 #include "win.h" 269 +#include "hb.h" 270 271 /* types used in config.h */ 272 typedef struct { 273 uint mod; 274 @@ -140,10 +141,11 @@ typedef struct { 275 GC gc; 276 } DC; 277 278 static inline ushort sixd_to_16bit(int); 279 +static void xresetfontsettings(ushort mode, Font **font, int *frcflags); 280 static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int); 281 -static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); 282 +static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int, int); 283 static void xdrawglyph(Glyph, int, int); 284 static void xclear(int, int, int, int); 285 static int xgeommasktogravity(int); 286 static int ximopen(Display *); 287 @@ -756,9 +758,9 @@ xresize(int col, int row) 288 XftDrawChange(xw.draw, xw.buf); 289 xclear(0, 0, win.w, win.h); 290 291 /* resize to new width */ 292 - xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); 293 + xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4); 294 } 295 296 ushort 297 sixd_to_16bit(int x) 298 @@ -1061,8 +1063,11 @@ xunloadfont(Font *f) 299 300 void 301 xunloadfonts(void) 302 { 303 + /* Clear Harfbuzz font cache. */ 304 + hbunloadfonts(); 305 + 306 /* Free the loaded fonts in the font cache. */ 307 while (frclen > 0) 308 XftFontClose(xw.dpy, frc[--frclen].font); 309 310 @@ -1184,9 +1189,9 @@ xinit(int cols, int rows) 311 XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); 312 XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); 313 314 /* font spec buffer */ 315 - xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); 316 + xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4); 317 318 /* Xft rendering context */ 319 xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); 320 321 @@ -1240,149 +1245,162 @@ xinit(int cols, int rows) 322 323 boxdraw_xinit(xw.dpy, xw.cmap, xw.draw, xw.vis); 324 } 325 326 +void 327 +xresetfontsettings(ushort mode, Font **font, int *frcflags) 328 +{ 329 + *font = &dc.font; 330 + if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { 331 + *font = &dc.ibfont; 332 + *frcflags = FRC_ITALICBOLD; 333 + } else if (mode & ATTR_ITALIC) { 334 + *font = &dc.ifont; 335 + *frcflags = FRC_ITALIC; 336 + } else if (mode & ATTR_BOLD) { 337 + *font = &dc.bfont; 338 + *frcflags = FRC_BOLD; 339 + } 340 +} 341 + 342 int 343 xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y) 344 { 345 float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp; 346 - ushort mode, prevmode = USHRT_MAX; 347 + ushort mode = glyphs[0].mode & ~ATTR_WRAP; 348 Font *font = &dc.font; 349 int frcflags = FRC_NORMAL; 350 - float runewidth = win.cw; 351 + float runewidth = win.cw * ((glyphs[0].mode & ATTR_WIDE) ? 2.0f : 1.0f); 352 Rune rune; 353 FT_UInt glyphidx; 354 FcResult fcres; 355 FcPattern *fcpattern, *fontpattern; 356 FcFontSet *fcsets[] = { NULL }; 357 FcCharSet *fccharset; 358 - int i, f, numspecs = 0; 359 + int f, code_idx, numspecs = 0; 360 + float cluster_xp = xp, cluster_yp = yp; 361 + HbTransformData shaped = { 0 }; 362 363 - for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { 364 - /* Fetch rune and mode for current glyph. */ 365 - rune = glyphs[i].u; 366 - mode = glyphs[i].mode; 367 + /* Initial values. */ 368 + xresetfontsettings(mode, &font, &frcflags); 369 370 - /* Skip dummy wide-character spacing. */ 371 - if (mode == ATTR_WDUMMY) 372 + /* Shape the segment. */ 373 + hbtransform(&shaped, font->match, glyphs, 0, len); 374 + xp = winx; yp = winy + font->ascent; 375 + cluster_xp = xp; cluster_yp = yp; 376 + 377 + for (code_idx = 0; code_idx < shaped.count; code_idx++) { 378 + int idx = shaped.glyphs[code_idx].cluster; 379 + 380 + if (glyphs[idx].mode & ATTR_WDUMMY) 381 continue; 382 383 - /* Determine font for glyph if different from previous glyph. */ 384 - if (prevmode != mode) { 385 - prevmode = mode; 386 - font = &dc.font; 387 - frcflags = FRC_NORMAL; 388 - runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f); 389 - if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { 390 - font = &dc.ibfont; 391 - frcflags = FRC_ITALICBOLD; 392 - } else if (mode & ATTR_ITALIC) { 393 - font = &dc.ifont; 394 - frcflags = FRC_ITALIC; 395 - } else if (mode & ATTR_BOLD) { 396 - font = &dc.bfont; 397 - frcflags = FRC_BOLD; 398 - } 399 - yp = winy + font->ascent; 400 + /* Advance the drawing cursor if we've moved to a new cluster */ 401 + if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluster) { 402 + xp += runewidth; 403 + cluster_xp = xp; 404 + cluster_yp = yp; 405 } 406 407 - if (mode & ATTR_BOXDRAW) { 408 + if (glyphs[idx].mode & ATTR_BOXDRAW) { 409 /* minor shoehorning: boxdraw uses only this ushort */ 410 - glyphidx = boxdrawindex(&glyphs[i]); 411 - } else { 412 - /* Lookup character index with default font. */ 413 - glyphidx = XftCharIndex(xw.dpy, font->match, rune); 414 - } 415 - if (glyphidx) { 416 specs[numspecs].font = font->match; 417 - specs[numspecs].glyph = glyphidx; 418 - specs[numspecs].x = (short)xp; 419 - specs[numspecs].y = (short)yp; 420 - xp += runewidth; 421 + specs[numspecs].glyph = boxdrawindex(&glyphs[idx]); 422 + specs[numspecs].x = xp; 423 + specs[numspecs].y = yp; 424 numspecs++; 425 - continue; 426 - } 427 - 428 - /* Fallback on font cache, search the font cache for match. */ 429 - for (f = 0; f < frclen; f++) { 430 - glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); 431 - /* Everything correct. */ 432 - if (glyphidx && frc[f].flags == frcflags) 433 - break; 434 - /* We got a default font for a not found glyph. */ 435 - if (!glyphidx && frc[f].flags == frcflags 436 - && frc[f].unicodep == rune) { 437 - break; 438 + } else if (shaped.glyphs[code_idx].codepoint != 0) { 439 + /* If symbol is found, put it into the specs. */ 440 + specs[numspecs].font = font->match; 441 + specs[numspecs].glyph = shaped.glyphs[code_idx].codepoint; 442 + specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.); 443 + specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.); 444 + cluster_xp += shaped.positions[code_idx].x_advance / 64.; 445 + cluster_yp += shaped.positions[code_idx].y_advance / 64.; 446 + numspecs++; 447 + } else { 448 + /* If it's not found, try to fetch it through the font cache. */ 449 + rune = glyphs[idx].u; 450 + for (f = 0; f < frclen; f++) { 451 + glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); 452 + /* Everything correct. */ 453 + if (glyphidx && frc[f].flags == frcflags) 454 + break; 455 + /* We got a default font for a not found glyph. */ 456 + if (!glyphidx && frc[f].flags == frcflags 457 + && frc[f].unicodep == rune) { 458 + break; 459 + } 460 } 461 - } 462 - 463 - /* Nothing was found. Use fontconfig to find matching font. */ 464 - if (f >= frclen) { 465 - if (!font->set) 466 - font->set = FcFontSort(0, font->pattern, 467 - 1, 0, &fcres); 468 - fcsets[0] = font->set; 469 - 470 - /* 471 - * Nothing was found in the cache. Now use 472 - * some dozen of Fontconfig calls to get the 473 - * font for one single character. 474 - * 475 - * Xft and fontconfig are design failures. 476 - */ 477 - fcpattern = FcPatternDuplicate(font->pattern); 478 - fccharset = FcCharSetCreate(); 479 - 480 - FcCharSetAddChar(fccharset, rune); 481 - FcPatternAddCharSet(fcpattern, FC_CHARSET, 482 - fccharset); 483 - FcPatternAddBool(fcpattern, FC_SCALABLE, 1); 484 - 485 - FcConfigSubstitute(0, fcpattern, 486 - FcMatchPattern); 487 - FcDefaultSubstitute(fcpattern); 488 489 - fontpattern = FcFontSetMatch(0, fcsets, 1, 490 - fcpattern, &fcres); 491 - 492 - /* Allocate memory for the new cache entry. */ 493 - if (frclen >= frccap) { 494 - frccap += 16; 495 - frc = xrealloc(frc, frccap * sizeof(Fontcache)); 496 + /* Nothing was found. Use fontconfig to find matching font. */ 497 + if (f >= frclen) { 498 + if (!font->set) 499 + font->set = FcFontSort(0, font->pattern, 500 + 1, 0, &fcres); 501 + fcsets[0] = font->set; 502 + 503 + /* 504 + * Nothing was found in the cache. Now use 505 + * some dozen of Fontconfig calls to get the 506 + * font for one single character. 507 + * 508 + * Xft and fontconfig are design failures. 509 + */ 510 + fcpattern = FcPatternDuplicate(font->pattern); 511 + fccharset = FcCharSetCreate(); 512 + 513 + FcCharSetAddChar(fccharset, rune); 514 + FcPatternAddCharSet(fcpattern, FC_CHARSET, 515 + fccharset); 516 + FcPatternAddBool(fcpattern, FC_SCALABLE, 1); 517 + 518 + FcConfigSubstitute(0, fcpattern, 519 + FcMatchPattern); 520 + FcDefaultSubstitute(fcpattern); 521 + 522 + fontpattern = FcFontSetMatch(0, fcsets, 1, 523 + fcpattern, &fcres); 524 + 525 + /* Allocate memory for the new cache entry. */ 526 + if (frclen >= frccap) { 527 + frccap += 16; 528 + frc = xrealloc(frc, frccap * sizeof(Fontcache)); 529 + } 530 + 531 + frc[frclen].font = XftFontOpenPattern(xw.dpy, 532 + fontpattern); 533 + if (!frc[frclen].font) 534 + die("XftFontOpenPattern failed seeking fallback font: %s\n", 535 + strerror(errno)); 536 + frc[frclen].flags = frcflags; 537 + frc[frclen].unicodep = rune; 538 + 539 + glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); 540 + 541 + f = frclen; 542 + frclen++; 543 + 544 + FcPatternDestroy(fcpattern); 545 + FcCharSetDestroy(fccharset); 546 } 547 548 - frc[frclen].font = XftFontOpenPattern(xw.dpy, 549 - fontpattern); 550 - if (!frc[frclen].font) 551 - die("XftFontOpenPattern failed seeking fallback font: %s\n", 552 - strerror(errno)); 553 - frc[frclen].flags = frcflags; 554 - frc[frclen].unicodep = rune; 555 - 556 - glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); 557 - 558 - f = frclen; 559 - frclen++; 560 - 561 - FcPatternDestroy(fcpattern); 562 - FcCharSetDestroy(fccharset); 563 + specs[numspecs].font = frc[f].font; 564 + specs[numspecs].glyph = glyphidx; 565 + specs[numspecs].x = (short)xp; 566 + specs[numspecs].y = (short)yp; 567 + numspecs++; 568 } 569 - 570 - specs[numspecs].font = frc[f].font; 571 - specs[numspecs].glyph = glyphidx; 572 - specs[numspecs].x = (short)xp; 573 - specs[numspecs].y = (short)yp; 574 - xp += runewidth; 575 - numspecs++; 576 } 577 578 + /* Cleanup and get ready for next segment. */ 579 + hbcleanup(&shaped); 580 return numspecs; 581 } 582 583 void 584 -xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) 585 +xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y, int charlen) 586 { 587 - int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); 588 int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, 589 width = charlen * win.cw; 590 Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; 591 XRenderColor colfg, colbg; 592 @@ -1520,23 +1538,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i 593 void 594 xdrawglyph(Glyph g, int x, int y) 595 { 596 int numspecs; 597 - XftGlyphFontSpec spec; 598 + XftGlyphFontSpec *specs = xw.specbuf; 599 600 - numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); 601 - xdrawglyphfontspecs(&spec, g, numspecs, x, y); 602 + numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y); 603 + xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? 2 : 1); 604 } 605 606 void 607 -xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) 608 +xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int len) 609 { 610 Color drawcol; 611 612 /* remove the old cursor */ 613 if (selected(ox, oy)) 614 og.mode ^= ATTR_REVERSE; 615 - xdrawglyph(og, ox, oy); 616 + 617 + /* Redraw the line where cursor was previously. 618 + * It will restore the ligatures broken by the cursor. */ 619 + xdrawline(line, 0, oy, len); 620 621 if (IS_SET(MODE_HIDE)) 622 return; 623 624 @@ -1668,30 +1689,30 @@ xdrawline(Line line, int x1, int y1, int x2) 625 int i, x, ox, numspecs; 626 Glyph base, new; 627 XftGlyphFontSpec *specs = xw.specbuf; 628 629 - numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); 630 i = ox = 0; 631 - for (x = x1; x < x2 && i < numspecs; x++) { 632 + for (x = x1; x < x2; x++) { 633 new = line[x]; 634 if (new.mode == ATTR_WDUMMY) 635 continue; 636 if (selected(x, y1)) 637 new.mode ^= ATTR_REVERSE; 638 - if (i > 0 && ATTRCMP(base, new)) { 639 - xdrawglyphfontspecs(specs, base, i, ox, y1); 640 - specs += i; 641 - numspecs -= i; 642 + if ((i > 0) && ATTRCMP(base, new)) { 643 + numspecs = xmakeglyphfontspecs(specs, &line[ox], x - ox, ox, y1); 644 + xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x - ox); 645 i = 0; 646 } 647 if (i == 0) { 648 ox = x; 649 base = new; 650 } 651 i++; 652 } 653 - if (i > 0) 654 - xdrawglyphfontspecs(specs, base, i, ox, y1); 655 + if (i > 0) { 656 + numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox, y1); 657 + xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox); 658 + } 659 } 660 661 void 662 xfinishdraw(void)