st-ligatures-alpha-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-alpha-20240105-0.9.diff (18800B)
---
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 47c615e..d7439a3 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 -LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\
34 + `$(PKG_CONFIG) --cflags freetype2` \
35 + `$(PKG_CONFIG) --cflags harfbuzz`
36 +LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender \
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..58d534b
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 62def59..041c6d8 100644
197 --- a/st.c
198 +++ b/st.c
199 @@ -2640,7 +2640,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 9f91e2a..b1a6256 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 27e81d1..0632636 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 @@ -142,8 +143,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 @@ -759,7 +761,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 @@ -1071,6 +1073,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 @@ -1202,7 +1207,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 @@ -1256,6 +1261,22 @@ xinit(int cols, int rows)
288 xsel.xtarget = XA_STRING;
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 @@ -1270,128 +1291,157 @@ 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 - /* Lookup character index with default font. */
366 - glyphidx = XftCharIndex(xw.dpy, font->match, rune);
367 - if (glyphidx) {
368 - specs[numspecs].font = font->match;
369 - specs[numspecs].glyph = glyphidx;
370 - specs[numspecs].x = (short)xp;
371 - specs[numspecs].y = (short)yp;
372 - xp += runewidth;
373 - numspecs++;
374 - continue;
375 - }
376 -
377 - /* Fallback on font cache, search the font cache for match. */
378 - for (f = 0; f < frclen; f++) {
379 - glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
380 - /* Everything correct. */
381 - if (glyphidx && frc[f].flags == frcflags)
382 - break;
383 - /* We got a default font for a not found glyph. */
384 - if (!glyphidx && frc[f].flags == frcflags
385 - && frc[f].unicodep == rune) {
386 - break;
387 + /* Shape the segment. */
388 + hbtransform(&shaped, font->match, glyphs, start, length);
389 + runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE) ? 2.0f : 1.0f);
390 + cluster_xp = xp; cluster_yp = yp;
391 + for (int code_idx = 0; code_idx < shaped.count; code_idx++) {
392 + int idx = shaped.glyphs[code_idx].cluster;
393 +
394 + if (glyphs[start + idx].mode & ATTR_WDUMMY)
395 + continue;
396 +
397 + /* Advance the drawing cursor if we've moved to a new cluster */
398 + if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluster) {
399 + xp += runewidth;
400 + cluster_xp = xp;
401 + cluster_yp = yp;
402 + runewidth = win.cw * ((glyphs[start + idx].mode & ATTR_WIDE) ? 2.0f : 1.0f);
403 + }
404 +
405 + if (shaped.glyphs[code_idx].codepoint != 0) {
406 + /* If symbol is found, put it into the specs. */
407 + specs[numspecs].font = font->match;
408 + specs[numspecs].glyph = shaped.glyphs[code_idx].codepoint;
409 + specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.);
410 + specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.);
411 + cluster_xp += shaped.positions[code_idx].x_advance / 64.;
412 + cluster_yp += shaped.positions[code_idx].y_advance / 64.;
413 + numspecs++;
414 + } else {
415 + /* If it's not found, try to fetch it through the font cache. */
416 + rune = glyphs[start + idx].u;
417 + for (f = 0; f < frclen; f++) {
418 + glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
419 + /* Everything correct. */
420 + if (glyphidx && frc[f].flags == frcflags)
421 + break;
422 + /* We got a default font for a not found glyph. */
423 + if (!glyphidx && frc[f].flags == frcflags
424 + && frc[f].unicodep == rune) {
425 + break;
426 + }
427 + }
428 +
429 + /* Nothing was found. Use fontconfig to find matching font. */
430 + if (f >= frclen) {
431 + if (!font->set)
432 + font->set = FcFontSort(0, font->pattern,
433 + 1, 0, &fcres);
434 + fcsets[0] = font->set;
435 +
436 + /*
437 + * Nothing was found in the cache. Now use
438 + * some dozen of Fontconfig calls to get the
439 + * font for one single character.
440 + *
441 + * Xft and fontconfig are design failures.
442 + */
443 + fcpattern = FcPatternDuplicate(font->pattern);
444 + fccharset = FcCharSetCreate();
445 +
446 + FcCharSetAddChar(fccharset, rune);
447 + FcPatternAddCharSet(fcpattern, FC_CHARSET,
448 + fccharset);
449 + FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
450 +
451 + FcConfigSubstitute(0, fcpattern,
452 + FcMatchPattern);
453 + FcDefaultSubstitute(fcpattern);
454 +
455 + fontpattern = FcFontSetMatch(0, fcsets, 1,
456 + fcpattern, &fcres);
457 +
458 + /* Allocate memory for the new cache entry. */
459 + if (frclen >= frccap) {
460 + frccap += 16;
461 + frc = xrealloc(frc, frccap * sizeof(Fontcache));
462 + }
463 +
464 + frc[frclen].font = XftFontOpenPattern(xw.dpy,
465 + fontpattern);
466 + if (!frc[frclen].font)
467 + die("XftFontOpenPattern failed seeking fallback font: %s\n",
468 + strerror(errno));
469 + frc[frclen].flags = frcflags;
470 + frc[frclen].unicodep = rune;
471 +
472 + glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
473 +
474 + f = frclen;
475 + frclen++;
476 +
477 + FcPatternDestroy(fcpattern);
478 + FcCharSetDestroy(fccharset);
479 + }
480 +
481 + specs[numspecs].font = frc[f].font;
482 + specs[numspecs].glyph = glyphidx;
483 + specs[numspecs].x = (short)xp;
484 + specs[numspecs].y = (short)yp;
485 + numspecs++;
486 + }
487 }
488 - }
489
490 - /* Nothing was found. Use fontconfig to find matching font. */
491 - if (f >= frclen) {
492 - if (!font->set)
493 - font->set = FcFontSort(0, font->pattern,
494 - 1, 0, &fcres);
495 - fcsets[0] = font->set;
496 + /* Cleanup and get ready for next segment. */
497 + hbcleanup(&shaped);
498 + start = i;
499
500 - /*
501 - * Nothing was found in the cache. Now use
502 - * some dozen of Fontconfig calls to get the
503 - * font for one single character.
504 - *
505 - * Xft and fontconfig are design failures.
506 - */
507 - fcpattern = FcPatternDuplicate(font->pattern);
508 - fccharset = FcCharSetCreate();
509 -
510 - FcCharSetAddChar(fccharset, rune);
511 - FcPatternAddCharSet(fcpattern, FC_CHARSET,
512 - fccharset);
513 - FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
514 -
515 - FcConfigSubstitute(0, fcpattern,
516 - FcMatchPattern);
517 - FcDefaultSubstitute(fcpattern);
518 -
519 - fontpattern = FcFontSetMatch(0, fcsets, 1,
520 - fcpattern, &fcres);
521 -
522 - /* Allocate memory for the new cache entry. */
523 - if (frclen >= frccap) {
524 - frccap += 16;
525 - frc = xrealloc(frc, frccap * sizeof(Fontcache));
526 + /* Determine font for glyph if different from previous glyph. */
527 + if (prevmode != mode) {
528 + prevmode = mode;
529 + xresetfontsettings(mode, &font, &frcflags);
530 + yp = winy + font->ascent;
531 }
532 -
533 - frc[frclen].font = XftFontOpenPattern(xw.dpy,
534 - fontpattern);
535 - if (!frc[frclen].font)
536 - die("XftFontOpenPattern failed seeking fallback font: %s\n",
537 - strerror(errno));
538 - frc[frclen].flags = frcflags;
539 - frc[frclen].unicodep = rune;
540 -
541 - glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
542 -
543 - f = frclen;
544 - frclen++;
545 -
546 - FcPatternDestroy(fcpattern);
547 - FcCharSetDestroy(fccharset);
548 }
549 -
550 - specs[numspecs].font = frc[f].font;
551 - specs[numspecs].glyph = glyphidx;
552 - specs[numspecs].x = (short)xp;
553 - specs[numspecs].y = (short)yp;
554 - xp += runewidth;
555 - numspecs++;
556 }
557
558 + hbcleanup(&shaped);
559 return numspecs;
560 }
561
562 void
563 -xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
564 +xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y, int charlen)
565 {
566 - int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
567 int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
568 width = charlen * win.cw;
569 Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
570 @@ -1527,21 +1577,24 @@ void
571 xdrawglyph(Glyph g, int x, int y)
572 {
573 int numspecs;
574 - XftGlyphFontSpec spec;
575 + XftGlyphFontSpec *specs = xw.specbuf;
576
577 - numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
578 - xdrawglyphfontspecs(&spec, g, numspecs, x, y);
579 + numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y);
580 + xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? 2 : 1);
581 }
582
583 void
584 -xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
585 +xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int len)
586 {
587 Color drawcol;
588
589 /* remove the old cursor */
590 if (selected(ox, oy))
591 og.mode ^= ATTR_REVERSE;
592 - xdrawglyph(og, ox, oy);
593 +
594 + /* Redraw the line where cursor was previously.
595 + * It will restore the ligatures broken by the cursor. */
596 + xdrawline(line, 0, oy, len);
597
598 if (IS_SET(MODE_HIDE))
599 return;
600 @@ -1669,18 +1722,16 @@ xdrawline(Line line, int x1, int y1, int x2)
601 Glyph base, new;
602 XftGlyphFontSpec *specs = xw.specbuf;
603
604 - numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
605 i = ox = 0;
606 - for (x = x1; x < x2 && i < numspecs; x++) {
607 + for (x = x1; x < x2; x++) {
608 new = line[x];
609 if (new.mode == ATTR_WDUMMY)
610 continue;
611 if (selected(x, y1))
612 new.mode ^= ATTR_REVERSE;
613 - if (i > 0 && ATTRCMP(base, new)) {
614 - xdrawglyphfontspecs(specs, base, i, ox, y1);
615 - specs += i;
616 - numspecs -= i;
617 + if ((i > 0) && ATTRCMP(base, new)) {
618 + numspecs = xmakeglyphfontspecs(specs, &line[ox], x - ox, ox, y1);
619 + xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x - ox);
620 i = 0;
621 }
622 if (i == 0) {
623 @@ -1689,8 +1740,10 @@ xdrawline(Line line, int x1, int y1, int x2)
624 }
625 i++;
626 }
627 - if (i > 0)
628 - xdrawglyphfontspecs(specs, base, i, ox, y1);
629 + if (i > 0) {
630 + numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox, y1);
631 + xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox);
632 + }
633 }
634
635 void