st-ligatures-scrollback-20241226-0.9.2.diff (18909B)
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 fdc29a7..6833b3b 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..3b0ef44 195 --- /dev/null 196 +++ b/hb.h 197 @@ -0,0 +1,14 @@ 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 diff --git a/st.c b/st.c 213 index 2478942..bba90d3 100644 214 --- a/st.c 215 +++ b/st.c 216 @@ -2729,9 +2729,10 @@ draw(void) 217 218 drawregion(0, 0, term.col, term.row); 219 if (term.scr == 0) 220 xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], 221 - term.ocx, term.ocy, term.line[term.ocy][term.ocx]); 222 + term.ocx, term.ocy, term.line[term.ocy][term.ocx], 223 + term.line[term.ocy], term.col); 224 term.ocx = cx; 225 term.ocy = term.c.y; 226 xfinishdraw(); 227 if (ocx != term.ocx || ocy != term.ocy) 228 diff --git a/st.h b/st.h 229 index 818a6f8..4e584b6 100644 230 --- a/st.h 231 +++ b/st.h 232 @@ -10,9 +10,10 @@ 233 #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) 234 #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) 235 #define DEFAULT(a, b) (a) = (a) ? (a) : (b) 236 #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) 237 -#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ 238 +#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode & (~ATTR_WRAP)) || \ 239 + (a).fg != (b).fg || \ 240 (a).bg != (b).bg) 241 #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ 242 (t1.tv_nsec-t2.tv_nsec)/1E6) 243 #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) 244 diff --git a/win.h b/win.h 245 index 6de960d..94679e4 100644 246 --- a/win.h 247 +++ b/win.h 248 @@ -24,9 +24,9 @@ enum win_mode { 249 }; 250 251 void xbell(void); 252 void xclipcopy(void); 253 -void xdrawcursor(int, int, Glyph, int, int, Glyph); 254 +void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int); 255 void xdrawline(Line, int, int, int); 256 void xfinishdraw(void); 257 void xloadcols(void); 258 int xsetcolorname(int, const char *); 259 diff --git a/x.c b/x.c 260 index bd23686..2bf3b72 100644 261 --- a/x.c 262 +++ b/x.c 263 @@ -18,8 +18,9 @@ 264 char *argv0; 265 #include "arg.h" 266 #include "st.h" 267 #include "win.h" 268 +#include "hb.h" 269 270 /* types used in config.h */ 271 typedef struct { 272 uint mod; 273 @@ -140,10 +141,11 @@ typedef struct { 274 GC gc; 275 } DC; 276 277 static inline ushort sixd_to_16bit(int); 278 +static void xresetfontsettings(ushort mode, Font **font, int *frcflags); 279 static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int); 280 -static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); 281 +static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int, int); 282 static void xdrawglyph(Glyph, int, int); 283 static void xclear(int, int, int, int); 284 static int xgeommasktogravity(int); 285 static int ximopen(Display *); 286 @@ -756,9 +758,9 @@ xresize(int col, int row) 287 XftDrawChange(xw.draw, xw.buf); 288 xclear(0, 0, win.w, win.h); 289 290 /* resize to new width */ 291 - xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); 292 + xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4); 293 } 294 295 ushort 296 sixd_to_16bit(int x) 297 @@ -1061,8 +1063,11 @@ xunloadfont(Font *f) 298 299 void 300 xunloadfonts(void) 301 { 302 + /* Clear Harfbuzz font cache. */ 303 + hbunloadfonts(); 304 + 305 /* Free the loaded fonts in the font cache. */ 306 while (frclen > 0) 307 XftFontClose(xw.dpy, frc[--frclen].font); 308 309 @@ -1184,9 +1189,9 @@ xinit(int cols, int rows) 310 XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); 311 XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); 312 313 /* font spec buffer */ 314 - xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); 315 + xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4); 316 317 /* Xft rendering context */ 318 xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); 319 320 @@ -1238,144 +1243,155 @@ xinit(int cols, int rows) 321 if (xsel.xtarget == None) 322 xsel.xtarget = XA_STRING; 323 } 324 325 +void 326 +xresetfontsettings(ushort mode, Font **font, int *frcflags) 327 +{ 328 + *font = &dc.font; 329 + if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { 330 + *font = &dc.ibfont; 331 + *frcflags = FRC_ITALICBOLD; 332 + } else if (mode & ATTR_ITALIC) { 333 + *font = &dc.ifont; 334 + *frcflags = FRC_ITALIC; 335 + } else if (mode & ATTR_BOLD) { 336 + *font = &dc.bfont; 337 + *frcflags = FRC_BOLD; 338 + } 339 +} 340 + 341 int 342 xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y) 343 { 344 float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp; 345 - ushort mode, prevmode = USHRT_MAX; 346 + ushort mode = glyphs[0].mode & ~ATTR_WRAP; 347 Font *font = &dc.font; 348 int frcflags = FRC_NORMAL; 349 - float runewidth = win.cw; 350 + float runewidth = win.cw * ((glyphs[0].mode & ATTR_WIDE) ? 2.0f : 1.0f); 351 Rune rune; 352 FT_UInt glyphidx; 353 FcResult fcres; 354 FcPattern *fcpattern, *fontpattern; 355 FcFontSet *fcsets[] = { NULL }; 356 FcCharSet *fccharset; 357 - int i, f, numspecs = 0; 358 + int f, code_idx, numspecs = 0; 359 + float cluster_xp = xp, cluster_yp = yp; 360 + HbTransformData shaped = { 0 }; 361 362 - for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { 363 - /* Fetch rune and mode for current glyph. */ 364 - rune = glyphs[i].u; 365 - mode = glyphs[i].mode; 366 + /* Initial values. */ 367 + xresetfontsettings(mode, &font, &frcflags); 368 369 - /* Skip dummy wide-character spacing. */ 370 - if (mode == ATTR_WDUMMY) 371 + /* Shape the segment. */ 372 + hbtransform(&shaped, font->match, glyphs, 0, len); 373 + xp = winx; yp = winy + font->ascent; 374 + cluster_xp = xp; cluster_yp = yp; 375 + 376 + for (code_idx = 0; code_idx < shaped.count; code_idx++) { 377 + int idx = shaped.glyphs[code_idx].cluster; 378 + 379 + if (glyphs[idx].mode & ATTR_WDUMMY) 380 continue; 381 382 - /* Determine font for glyph if different from previous glyph. */ 383 - if (prevmode != mode) { 384 - prevmode = mode; 385 - font = &dc.font; 386 - frcflags = FRC_NORMAL; 387 - runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f); 388 - if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { 389 - font = &dc.ibfont; 390 - frcflags = FRC_ITALICBOLD; 391 - } else if (mode & ATTR_ITALIC) { 392 - font = &dc.ifont; 393 - frcflags = FRC_ITALIC; 394 - } else if (mode & ATTR_BOLD) { 395 - font = &dc.bfont; 396 - frcflags = FRC_BOLD; 397 - } 398 - yp = winy + font->ascent; 399 + /* Advance the drawing cursor if we've moved to a new cluster */ 400 + if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluster) { 401 + xp += runewidth; 402 + cluster_xp = xp; 403 + cluster_yp = yp; 404 } 405 406 - /* Lookup character index with default font. */ 407 - glyphidx = XftCharIndex(xw.dpy, font->match, rune); 408 - if (glyphidx) { 409 + if (shaped.glyphs[code_idx].codepoint != 0) { 410 + /* If symbol is found, put it into the specs. */ 411 specs[numspecs].font = font->match; 412 - specs[numspecs].glyph = glyphidx; 413 - specs[numspecs].x = (short)xp; 414 - specs[numspecs].y = (short)yp; 415 - xp += runewidth; 416 + specs[numspecs].glyph = shaped.glyphs[code_idx].codepoint; 417 + specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.); 418 + specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.); 419 + cluster_xp += shaped.positions[code_idx].x_advance / 64.; 420 + cluster_yp += shaped.positions[code_idx].y_advance / 64.; 421 numspecs++; 422 - continue; 423 - } 424 - 425 - /* Fallback on font cache, search the font cache for match. */ 426 - for (f = 0; f < frclen; f++) { 427 - glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); 428 - /* Everything correct. */ 429 - if (glyphidx && frc[f].flags == frcflags) 430 - break; 431 - /* We got a default font for a not found glyph. */ 432 - if (!glyphidx && frc[f].flags == frcflags 433 - && frc[f].unicodep == rune) { 434 - break; 435 + } else { 436 + /* If it's not found, try to fetch it through the font cache. */ 437 + rune = glyphs[idx].u; 438 + for (f = 0; f < frclen; f++) { 439 + glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); 440 + /* Everything correct. */ 441 + if (glyphidx && frc[f].flags == frcflags) 442 + break; 443 + /* We got a default font for a not found glyph. */ 444 + if (!glyphidx && frc[f].flags == frcflags 445 + && frc[f].unicodep == rune) { 446 + break; 447 + } 448 } 449 - } 450 451 - /* Nothing was found. Use fontconfig to find matching font. */ 452 - if (f >= frclen) { 453 - if (!font->set) 454 - font->set = FcFontSort(0, font->pattern, 455 - 1, 0, &fcres); 456 - fcsets[0] = font->set; 457 - 458 - /* 459 - * Nothing was found in the cache. Now use 460 - * some dozen of Fontconfig calls to get the 461 - * font for one single character. 462 - * 463 - * Xft and fontconfig are design failures. 464 - */ 465 - fcpattern = FcPatternDuplicate(font->pattern); 466 - fccharset = FcCharSetCreate(); 467 - 468 - FcCharSetAddChar(fccharset, rune); 469 - FcPatternAddCharSet(fcpattern, FC_CHARSET, 470 - fccharset); 471 - FcPatternAddBool(fcpattern, FC_SCALABLE, 1); 472 - 473 - FcConfigSubstitute(0, fcpattern, 474 - FcMatchPattern); 475 - FcDefaultSubstitute(fcpattern); 476 - 477 - fontpattern = FcFontSetMatch(0, fcsets, 1, 478 - fcpattern, &fcres); 479 - 480 - /* Allocate memory for the new cache entry. */ 481 - if (frclen >= frccap) { 482 - frccap += 16; 483 - frc = xrealloc(frc, frccap * sizeof(Fontcache)); 484 + /* Nothing was found. Use fontconfig to find matching font. */ 485 + if (f >= frclen) { 486 + if (!font->set) 487 + font->set = FcFontSort(0, font->pattern, 488 + 1, 0, &fcres); 489 + fcsets[0] = font->set; 490 + 491 + /* 492 + * Nothing was found in the cache. Now use 493 + * some dozen of Fontconfig calls to get the 494 + * font for one single character. 495 + * 496 + * Xft and fontconfig are design failures. 497 + */ 498 + fcpattern = FcPatternDuplicate(font->pattern); 499 + fccharset = FcCharSetCreate(); 500 + 501 + FcCharSetAddChar(fccharset, rune); 502 + FcPatternAddCharSet(fcpattern, FC_CHARSET, 503 + fccharset); 504 + FcPatternAddBool(fcpattern, FC_SCALABLE, 1); 505 + 506 + FcConfigSubstitute(0, fcpattern, 507 + FcMatchPattern); 508 + FcDefaultSubstitute(fcpattern); 509 + 510 + fontpattern = FcFontSetMatch(0, fcsets, 1, 511 + fcpattern, &fcres); 512 + 513 + /* Allocate memory for the new cache entry. */ 514 + if (frclen >= frccap) { 515 + frccap += 16; 516 + frc = xrealloc(frc, frccap * sizeof(Fontcache)); 517 + } 518 + 519 + frc[frclen].font = XftFontOpenPattern(xw.dpy, 520 + fontpattern); 521 + if (!frc[frclen].font) 522 + die("XftFontOpenPattern failed seeking fallback font: %s\n", 523 + strerror(errno)); 524 + frc[frclen].flags = frcflags; 525 + frc[frclen].unicodep = rune; 526 + 527 + glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); 528 + 529 + f = frclen; 530 + frclen++; 531 + 532 + FcPatternDestroy(fcpattern); 533 + FcCharSetDestroy(fccharset); 534 } 535 536 - frc[frclen].font = XftFontOpenPattern(xw.dpy, 537 - fontpattern); 538 - if (!frc[frclen].font) 539 - die("XftFontOpenPattern failed seeking fallback font: %s\n", 540 - strerror(errno)); 541 - frc[frclen].flags = frcflags; 542 - frc[frclen].unicodep = rune; 543 - 544 - glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); 545 - 546 - f = frclen; 547 - frclen++; 548 - 549 - FcPatternDestroy(fcpattern); 550 - FcCharSetDestroy(fccharset); 551 + specs[numspecs].font = frc[f].font; 552 + specs[numspecs].glyph = glyphidx; 553 + specs[numspecs].x = (short)xp; 554 + specs[numspecs].y = (short)yp; 555 + numspecs++; 556 } 557 - 558 - specs[numspecs].font = frc[f].font; 559 - specs[numspecs].glyph = glyphidx; 560 - specs[numspecs].x = (short)xp; 561 - specs[numspecs].y = (short)yp; 562 - xp += runewidth; 563 - numspecs++; 564 } 565 566 + /* Cleanup and get ready for next segment. */ 567 + hbcleanup(&shaped); 568 return numspecs; 569 } 570 571 void 572 -xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) 573 +xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y, int charlen) 574 { 575 - int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); 576 int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, 577 width = charlen * win.cw; 578 Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; 579 XRenderColor colfg, colbg; 580 @@ -1509,23 +1525,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i 581 void 582 xdrawglyph(Glyph g, int x, int y) 583 { 584 int numspecs; 585 - XftGlyphFontSpec spec; 586 + XftGlyphFontSpec *specs = xw.specbuf; 587 588 - numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); 589 - xdrawglyphfontspecs(&spec, g, numspecs, x, y); 590 + numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y); 591 + xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? 2 : 1); 592 } 593 594 void 595 -xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) 596 +xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int len) 597 { 598 Color drawcol; 599 600 /* remove the old cursor */ 601 if (selected(ox, oy)) 602 og.mode ^= ATTR_REVERSE; 603 - xdrawglyph(og, ox, oy); 604 + 605 + /* Redraw the line where cursor was previously. 606 + * It will restore the ligatures broken by the cursor. */ 607 + xdrawline(line, 0, oy, len); 608 609 if (IS_SET(MODE_HIDE)) 610 return; 611 612 @@ -1657,30 +1676,30 @@ xdrawline(Line line, int x1, int y1, int x2) 613 int i, x, ox, numspecs; 614 Glyph base, new; 615 XftGlyphFontSpec *specs = xw.specbuf; 616 617 - numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); 618 i = ox = 0; 619 - for (x = x1; x < x2 && i < numspecs; x++) { 620 + for (x = x1; x < x2; x++) { 621 new = line[x]; 622 if (new.mode == ATTR_WDUMMY) 623 continue; 624 if (selected(x, y1)) 625 new.mode ^= ATTR_REVERSE; 626 - if (i > 0 && ATTRCMP(base, new)) { 627 - xdrawglyphfontspecs(specs, base, i, ox, y1); 628 - specs += i; 629 - numspecs -= i; 630 + if ((i > 0) && ATTRCMP(base, new)) { 631 + numspecs = xmakeglyphfontspecs(specs, &line[ox], x - ox, ox, y1); 632 + xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x - ox); 633 i = 0; 634 } 635 if (i == 0) { 636 ox = x; 637 base = new; 638 } 639 i++; 640 } 641 - if (i > 0) 642 - xdrawglyphfontspecs(specs, base, i, ox, y1); 643 + if (i > 0) { 644 + numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox, y1); 645 + xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox); 646 + } 647 } 648 649 void 650 xfinishdraw(void)