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