st-ligatures-boxdraw-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-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