st-ligatures-scrollback-20251007-0.9.3.diff (18957B)
1 diff --git a/Makefile b/Makefile 2 index 15db421..dfcea0f 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 11 +SRC = st.c x.c hb.c 12 OBJ = $(SRC:.c=.o) 13 14 all: st 15 16 @@ -15,9 +15,10 @@ 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 +hb.o: st.h 24 25 $(OBJ): config.h config.mk 26 27 st: $(OBJ) 28 diff --git a/config.mk b/config.mk 29 index 2fc854e..ec69d1a 100644 30 --- a/config.mk 31 +++ b/config.mk 32 @@ -14,12 +14,14 @@ PKG_CONFIG = pkg-config 33 34 # includes and libs 35 INCS = -I$(X11INC) \ 36 `$(PKG_CONFIG) --cflags fontconfig` \ 37 - `$(PKG_CONFIG) --cflags freetype2` 38 + `$(PKG_CONFIG) --cflags freetype2` \ 39 + `$(PKG_CONFIG) --cflags harfbuzz` 40 LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ 41 `$(PKG_CONFIG) --libs fontconfig` \ 42 - `$(PKG_CONFIG) --libs freetype2` 43 + `$(PKG_CONFIG) --libs freetype2` \ 44 + `$(PKG_CONFIG) --libs harfbuzz` 45 46 # flags 47 STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 48 STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS) 49 @@ -28,9 +30,10 @@ STLDFLAGS = $(LIBS) $(LDFLAGS) 50 # OpenBSD: 51 #CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE 52 #LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \ 53 # `$(PKG_CONFIG) --libs fontconfig` \ 54 -# `$(PKG_CONFIG) --libs freetype2` 55 +# `$(PKG_CONFIG) --libs freetype2` \ 56 +# `$(PKG_CONFIG) --libs harfbuzz` 57 #MANPREFIX = ${PREFIX}/man 58 59 # compiler and linker 60 # CC = c99 61 diff --git a/hb.c b/hb.c 62 new file mode 100644 63 index 0000000..99412c8 64 --- /dev/null 65 +++ b/hb.c 66 @@ -0,0 +1,125 @@ 67 +#include <stdlib.h> 68 +#include <stdio.h> 69 +#include <math.h> 70 +#include <X11/Xft/Xft.h> 71 +#include <X11/cursorfont.h> 72 +#include <hb.h> 73 +#include <hb-ft.h> 74 + 75 +#include "st.h" 76 +#include "hb.h" 77 + 78 +#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 } 79 +#define BUFFER_STEP 256 80 + 81 +hb_font_t *hbfindfont(XftFont *match); 82 + 83 +typedef struct { 84 + XftFont *match; 85 + hb_font_t *font; 86 +} HbFontMatch; 87 + 88 +typedef struct { 89 + size_t capacity; 90 + HbFontMatch *fonts; 91 +} HbFontCache; 92 + 93 +static HbFontCache hbfontcache = { 0, NULL }; 94 + 95 +typedef struct { 96 + size_t capacity; 97 + Rune *runes; 98 +} RuneBuffer; 99 + 100 +static RuneBuffer hbrunebuffer = { 0, NULL }; 101 + 102 +/* 103 + * Poplulate the array with a list of font features, wrapped in FEATURE macro, 104 + * e. g. 105 + * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') 106 + */ 107 +hb_feature_t features[] = { }; 108 + 109 +void 110 +hbunloadfonts() 111 +{ 112 + for (int i = 0; i < hbfontcache.capacity; i++) { 113 + hb_font_destroy(hbfontcache.fonts[i].font); 114 + XftUnlockFace(hbfontcache.fonts[i].match); 115 + } 116 + 117 + if (hbfontcache.fonts != NULL) { 118 + free(hbfontcache.fonts); 119 + hbfontcache.fonts = NULL; 120 + } 121 + hbfontcache.capacity = 0; 122 +} 123 + 124 +hb_font_t * 125 +hbfindfont(XftFont *match) 126 +{ 127 + for (int i = 0; i < hbfontcache.capacity; i++) { 128 + if (hbfontcache.fonts[i].match == match) 129 + return hbfontcache.fonts[i].font; 130 + } 131 + 132 + /* Font not found in cache, caching it now. */ 133 + hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * (hbfontcache.capacity + 1)); 134 + FT_Face face = XftLockFace(match); 135 + hb_font_t *font = hb_ft_font_create(face, NULL); 136 + if (font == NULL) 137 + die("Failed to load Harfbuzz font."); 138 + 139 + hbfontcache.fonts[hbfontcache.capacity].match = match; 140 + hbfontcache.fonts[hbfontcache.capacity].font = font; 141 + hbfontcache.capacity += 1; 142 + 143 + return font; 144 +} 145 + 146 +void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, int start, int length) { 147 + ushort mode = USHRT_MAX; 148 + unsigned int glyph_count; 149 + int rune_idx, glyph_idx, end = start + length; 150 + 151 + hb_font_t *font = hbfindfont(xfont); 152 + if (font == NULL) 153 + return; 154 + 155 + hb_buffer_t *buffer = hb_buffer_create(); 156 + hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); 157 + hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); 158 + 159 + /* Resize the buffer if required length is larger. */ 160 + if (hbrunebuffer.capacity < length) { 161 + hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_STEP; 162 + hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer.capacity * sizeof(Rune)); 163 + } 164 + 165 + /* Fill buffer with codepoints. */ 166 + for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, rune_idx++) { 167 + hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u; 168 + mode = glyphs[glyph_idx].mode; 169 + if (mode & ATTR_WDUMMY) 170 + hbrunebuffer.runes[rune_idx] = 0x0020; 171 + } 172 + hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, length); 173 + 174 + /* Shape the segment. */ 175 + hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t)); 176 + 177 + /* Get new glyph info. */ 178 + hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_count); 179 + hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &glyph_count); 180 + 181 + /* Fill the output. */ 182 + data->buffer = buffer; 183 + data->glyphs = info; 184 + data->positions = pos; 185 + data->count = glyph_count; 186 +} 187 + 188 +void hbcleanup(HbTransformData *data) { 189 + hb_buffer_destroy(data->buffer); 190 + memset(data, 0, sizeof(HbTransformData)); 191 +} 192 diff --git a/hb.h b/hb.h 193 new file mode 100644 194 index 0000000..96e808b 195 --- /dev/null 196 +++ b/hb.h 197 @@ -0,0 +1,15 @@ 198 +#include <X11/Xft/Xft.h> 199 +#include <hb.h> 200 +#include <hb-ft.h> 201 + 202 +typedef struct { 203 + hb_buffer_t *buffer; 204 + hb_glyph_info_t *glyphs; 205 + hb_glyph_position_t *positions; 206 + unsigned int count; 207 +} HbTransformData; 208 + 209 +void hbunloadfonts(); 210 +void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int); 211 +void hbcleanup(HbTransformData *); 212 + 213 diff --git a/st.c b/st.c 214 index 3ba17bc..1415966 100644 215 --- a/st.c 216 +++ b/st.c 217 @@ -2755,9 +2755,10 @@ draw(void) 218 219 drawregion(0, 0, term.col, term.row); 220 if (term.scr == 0) 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 818a6f8..4e584b6 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 d73152b..9aca2e7 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 @@ -1187,9 +1192,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 @@ -1241,144 +1246,155 @@ xinit(int cols, int rows) 322 if (xsel.xtarget == None) 323 xsel.xtarget = XA_STRING; 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 * (idx - shaped.glyphs[code_idx - 1].cluster); 403 + cluster_xp = xp; 404 + cluster_yp = yp; 405 } 406 407 - /* Lookup character index with default font. */ 408 - glyphidx = XftCharIndex(xw.dpy, font->match, rune); 409 - if (glyphidx) { 410 + if (shaped.glyphs[code_idx].codepoint != 0) { 411 + /* If symbol is found, put it into the specs. */ 412 specs[numspecs].font = font->match; 413 - specs[numspecs].glyph = glyphidx; 414 - specs[numspecs].x = (short)xp; 415 - specs[numspecs].y = (short)yp; 416 - xp += runewidth; 417 + specs[numspecs].glyph = shaped.glyphs[code_idx].codepoint; 418 + specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.); 419 + specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.); 420 + cluster_xp += shaped.positions[code_idx].x_advance / 64.; 421 + cluster_yp += shaped.positions[code_idx].y_advance / 64.; 422 numspecs++; 423 - continue; 424 - } 425 - 426 - /* Fallback on font cache, search the font cache for match. */ 427 - for (f = 0; f < frclen; f++) { 428 - glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); 429 - /* Everything correct. */ 430 - if (glyphidx && frc[f].flags == frcflags) 431 - break; 432 - /* We got a default font for a not found glyph. */ 433 - if (!glyphidx && frc[f].flags == frcflags 434 - && frc[f].unicodep == rune) { 435 - break; 436 + } else { 437 + /* If it's not found, try to fetch it through the font cache. */ 438 + rune = glyphs[idx].u; 439 + for (f = 0; f < frclen; f++) { 440 + glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); 441 + /* Everything correct. */ 442 + if (glyphidx && frc[f].flags == frcflags) 443 + break; 444 + /* We got a default font for a not found glyph. */ 445 + if (!glyphidx && frc[f].flags == frcflags 446 + && frc[f].unicodep == rune) { 447 + break; 448 + } 449 } 450 - } 451 - 452 - /* Nothing was found. Use fontconfig to find matching font. */ 453 - if (f >= frclen) { 454 - if (!font->set) 455 - font->set = FcFontSort(0, font->pattern, 456 - 1, 0, &fcres); 457 - fcsets[0] = font->set; 458 459 - /* 460 - * Nothing was found in the cache. Now use 461 - * some dozen of Fontconfig calls to get the 462 - * font for one single character. 463 - * 464 - * Xft and fontconfig are design failures. 465 - */ 466 - fcpattern = FcPatternDuplicate(font->pattern); 467 - fccharset = FcCharSetCreate(); 468 - 469 - FcCharSetAddChar(fccharset, rune); 470 - FcPatternAddCharSet(fcpattern, FC_CHARSET, 471 - fccharset); 472 - FcPatternAddBool(fcpattern, FC_SCALABLE, 1); 473 - 474 - FcConfigSubstitute(0, fcpattern, 475 - FcMatchPattern); 476 - FcDefaultSubstitute(fcpattern); 477 - 478 - fontpattern = FcFontSetMatch(0, fcsets, 1, 479 - fcpattern, &fcres); 480 - 481 - /* Allocate memory for the new cache entry. */ 482 - if (frclen >= frccap) { 483 - frccap += 16; 484 - frc = xrealloc(frc, frccap * sizeof(Fontcache)); 485 + /* Nothing was found. Use fontconfig to find matching font. */ 486 + if (f >= frclen) { 487 + if (!font->set) 488 + font->set = FcFontSort(0, font->pattern, 489 + 1, 0, &fcres); 490 + fcsets[0] = font->set; 491 + 492 + /* 493 + * Nothing was found in the cache. Now use 494 + * some dozen of Fontconfig calls to get the 495 + * font for one single character. 496 + * 497 + * Xft and fontconfig are design failures. 498 + */ 499 + fcpattern = FcPatternDuplicate(font->pattern); 500 + fccharset = FcCharSetCreate(); 501 + 502 + FcCharSetAddChar(fccharset, rune); 503 + FcPatternAddCharSet(fcpattern, FC_CHARSET, 504 + fccharset); 505 + FcPatternAddBool(fcpattern, FC_SCALABLE, 1); 506 + 507 + FcConfigSubstitute(0, fcpattern, 508 + FcMatchPattern); 509 + FcDefaultSubstitute(fcpattern); 510 + 511 + fontpattern = FcFontSetMatch(0, fcsets, 1, 512 + fcpattern, &fcres); 513 + 514 + /* Allocate memory for the new cache entry. */ 515 + if (frclen >= frccap) { 516 + frccap += 16; 517 + frc = xrealloc(frc, frccap * sizeof(Fontcache)); 518 + } 519 + 520 + frc[frclen].font = XftFontOpenPattern(xw.dpy, 521 + fontpattern); 522 + if (!frc[frclen].font) 523 + die("XftFontOpenPattern failed seeking fallback font: %s\n", 524 + strerror(errno)); 525 + frc[frclen].flags = frcflags; 526 + frc[frclen].unicodep = rune; 527 + 528 + glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); 529 + 530 + f = frclen; 531 + frclen++; 532 + 533 + FcPatternDestroy(fcpattern); 534 + FcCharSetDestroy(fccharset); 535 } 536 537 - frc[frclen].font = XftFontOpenPattern(xw.dpy, 538 - fontpattern); 539 - if (!frc[frclen].font) 540 - die("XftFontOpenPattern failed seeking fallback font: %s\n", 541 - strerror(errno)); 542 - frc[frclen].flags = frcflags; 543 - frc[frclen].unicodep = rune; 544 - 545 - glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); 546 - 547 - f = frclen; 548 - frclen++; 549 - 550 - FcPatternDestroy(fcpattern); 551 - FcCharSetDestroy(fccharset); 552 + specs[numspecs].font = frc[f].font; 553 + specs[numspecs].glyph = glyphidx; 554 + specs[numspecs].x = (short)xp; 555 + specs[numspecs].y = (short)yp; 556 + numspecs++; 557 } 558 - 559 - specs[numspecs].font = frc[f].font; 560 - specs[numspecs].glyph = glyphidx; 561 - specs[numspecs].x = (short)xp; 562 - specs[numspecs].y = (short)yp; 563 - xp += runewidth; 564 - numspecs++; 565 } 566 567 + /* Cleanup and get ready for next segment. */ 568 + hbcleanup(&shaped); 569 return numspecs; 570 } 571 572 void 573 -xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) 574 +xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y, int charlen) 575 { 576 - int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); 577 int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, 578 width = charlen * win.cw; 579 Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; 580 XRenderColor colfg, colbg; 581 @@ -1512,23 +1528,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i 582 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 613 @@ -1660,30 +1679,30 @@ xdrawline(Line line, int x1, int y1, int x2) 614 int i, x, ox, numspecs; 615 Glyph base, new; 616 XftGlyphFontSpec *specs = xw.specbuf; 617 618 - numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); 619 i = ox = 0; 620 - for (x = x1; x < x2 && i < numspecs; x++) { 621 + for (x = x1; x < x2; x++) { 622 new = line[x]; 623 if (new.mode == ATTR_WDUMMY) 624 continue; 625 if (selected(x, y1)) 626 new.mode ^= ATTR_REVERSE; 627 - if (i > 0 && ATTRCMP(base, new)) { 628 - xdrawglyphfontspecs(specs, base, i, ox, y1); 629 - specs += i; 630 - numspecs -= i; 631 + if ((i > 0) && ATTRCMP(base, new)) { 632 + numspecs = xmakeglyphfontspecs(specs, &line[ox], x - ox, ox, y1); 633 + xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x - ox); 634 i = 0; 635 } 636 if (i == 0) { 637 ox = x; 638 base = new; 639 } 640 i++; 641 } 642 - if (i > 0) 643 - xdrawglyphfontspecs(specs, base, i, ox, y1); 644 + if (i > 0) { 645 + numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox, y1); 646 + xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox); 647 + } 648 } 649 650 void 651 xfinishdraw(void)