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