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