st-ligatures-alpha-scrollback-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-scrollback-20240105-0.9.diff (18803B)
---
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..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 79ee9ba..454771d 100644
197 --- a/st.c
198 +++ b/st.c
199 @@ -2711,7 +2711,9 @@ draw(void)
200 drawregion(0, 0, term.col, term.row);
201 if (term.scr == 0)
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 +
207 term.ocx = cx;
208 term.ocy = term.c.y;
209 xfinishdraw();
210 diff --git a/st.h b/st.h
211 index 78762a2..01eea49 100644
212 --- a/st.h
213 +++ b/st.h
214 @@ -11,7 +11,8 @@
215 #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
216 #define DEFAULT(a, b) (a) = (a) ? (a) : (b)
217 #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
218 -#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \
219 +#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode & (~ATTR_WRAP)) || \
220 + (a).fg != (b).fg || \
221 (a).bg != (b).bg)
222 #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
223 (t1.tv_nsec-t2.tv_nsec)/1E6)
224 diff --git a/win.h b/win.h
225 index 6de960d..94679e4 100644
226 --- a/win.h
227 +++ b/win.h
228 @@ -25,7 +25,7 @@ enum win_mode {
229
230 void xbell(void);
231 void xclipcopy(void);
232 -void xdrawcursor(int, int, Glyph, int, int, Glyph);
233 +void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int);
234 void xdrawline(Line, int, int, int);
235 void xfinishdraw(void);
236 void xloadcols(void);
237 diff --git a/x.c b/x.c
238 index 27e81d1..5e11c1f 100644
239 --- a/x.c
240 +++ b/x.c
241 @@ -19,6 +19,7 @@ char *argv0;
242 #include "arg.h"
243 #include "st.h"
244 #include "win.h"
245 +#include "hb.h"
246
247 /* types used in config.h */
248 typedef struct {
249 @@ -142,8 +143,9 @@ typedef struct {
250 } DC;
251
252 static inline ushort sixd_to_16bit(int);
253 +static void xresetfontsettings(ushort mode, Font **font, int *frcflags);
254 static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int);
255 -static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int);
256 +static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int, int);
257 static void xdrawglyph(Glyph, int, int);
258 static void xclear(int, int, int, int);
259 static int xgeommasktogravity(int);
260 @@ -759,7 +761,7 @@ xresize(int col, int row)
261 xclear(0, 0, win.w, win.h);
262
263 /* resize to new width */
264 - xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
265 + xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
266 }
267
268 ushort
269 @@ -1071,6 +1073,9 @@ xunloadfont(Font *f)
270 void
271 xunloadfonts(void)
272 {
273 + /* Clear Harfbuzz font cache. */
274 + hbunloadfonts();
275 +
276 /* Free the loaded fonts in the font cache. */
277 while (frclen > 0)
278 XftFontClose(xw.dpy, frc[--frclen].font);
279 @@ -1202,7 +1207,7 @@ xinit(int cols, int rows)
280 XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
281
282 /* font spec buffer */
283 - xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
284 + xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
285
286 /* Xft rendering context */
287 xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
288 @@ -1256,6 +1261,22 @@ xinit(int cols, int rows)
289 xsel.xtarget = XA_STRING;
290 }
291
292 +void
293 +xresetfontsettings(ushort mode, Font **font, int *frcflags)
294 +{
295 + *font = &dc.font;
296 + if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
297 + *font = &dc.ibfont;
298 + *frcflags = FRC_ITALICBOLD;
299 + } else if (mode & ATTR_ITALIC) {
300 + *font = &dc.ifont;
301 + *frcflags = FRC_ITALIC;
302 + } else if (mode & ATTR_BOLD) {
303 + *font = &dc.bfont;
304 + *frcflags = FRC_BOLD;
305 + }
306 +}
307 +
308 int
309 xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
310 {
311 @@ -1270,128 +1291,156 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
312 FcPattern *fcpattern, *fontpattern;
313 FcFontSet *fcsets[] = { NULL };
314 FcCharSet *fccharset;
315 - int i, f, numspecs = 0;
316 + int i, f, length = 0, start = 0, numspecs = 0;
317 + float cluster_xp = xp, cluster_yp = yp;
318 + HbTransformData shaped = { 0 };
319 +
320 + /* Initial values. */
321 + mode = prevmode = glyphs[0].mode & ~ATTR_WRAP;
322 + xresetfontsettings(mode, &font, &frcflags);
323
324 for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
325 - /* Fetch rune and mode for current glyph. */
326 - rune = glyphs[i].u;
327 - mode = glyphs[i].mode;
328 + mode = glyphs[i].mode & ~ATTR_WRAP;
329
330 /* Skip dummy wide-character spacing. */
331 - if (mode == ATTR_WDUMMY)
332 + if (mode & ATTR_WDUMMY && i < (len - 1))
333 continue;
334
335 - /* Determine font for glyph if different from previous glyph. */
336 - if (prevmode != mode) {
337 - prevmode = mode;
338 - font = &dc.font;
339 - frcflags = FRC_NORMAL;
340 - runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f);
341 - if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
342 - font = &dc.ibfont;
343 - frcflags = FRC_ITALICBOLD;
344 - } else if (mode & ATTR_ITALIC) {
345 - font = &dc.ifont;
346 - frcflags = FRC_ITALIC;
347 - } else if (mode & ATTR_BOLD) {
348 - font = &dc.bfont;
349 - frcflags = FRC_BOLD;
350 + if (
351 + prevmode != mode
352 + || ATTRCMP(glyphs[start], glyphs[i])
353 + || selected(x + i, y) != selected(x + start, y)
354 + || i == (len - 1)
355 + ) {
356 + /* Handle 1-character wide segments and end of line */
357 + length = i - start;
358 + if (i == start) {
359 + length = 1;
360 + } else if (i == (len - 1)) {
361 + length = (i - start + 1);
362 }
363 - yp = winy + font->ascent;
364 - }
365
366 - /* Lookup character index with default font. */
367 - glyphidx = XftCharIndex(xw.dpy, font->match, rune);
368 - if (glyphidx) {
369 - specs[numspecs].font = font->match;
370 - specs[numspecs].glyph = glyphidx;
371 - specs[numspecs].x = (short)xp;
372 - specs[numspecs].y = (short)yp;
373 - xp += runewidth;
374 - numspecs++;
375 - continue;
376 - }
377 -
378 - /* Fallback on font cache, search the font cache for match. */
379 - for (f = 0; f < frclen; f++) {
380 - glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
381 - /* Everything correct. */
382 - if (glyphidx && frc[f].flags == frcflags)
383 - break;
384 - /* We got a default font for a not found glyph. */
385 - if (!glyphidx && frc[f].flags == frcflags
386 - && frc[f].unicodep == rune) {
387 - break;
388 + /* Shape the segment. */
389 + hbtransform(&shaped, font->match, glyphs, start, length);
390 + runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE) ? 2.0f : 1.0f);
391 + cluster_xp = xp; cluster_yp = yp;
392 + for (int code_idx = 0; code_idx < shaped.count; code_idx++) {
393 + int idx = shaped.glyphs[code_idx].cluster;
394 +
395 + if (glyphs[start + idx].mode & ATTR_WDUMMY)
396 + continue;
397 +
398 + /* Advance the drawing cursor if we've moved to a new cluster */
399 + if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluster) {
400 + xp += runewidth;
401 + cluster_xp = xp;
402 + cluster_yp = yp;
403 + runewidth = win.cw * ((glyphs[start + idx].mode & ATTR_WIDE) ? 2.0f : 1.0f);
404 + }
405 +
406 + if (shaped.glyphs[code_idx].codepoint != 0) {
407 + /* If symbol is found, put it into the specs. */
408 + specs[numspecs].font = font->match;
409 + specs[numspecs].glyph = shaped.glyphs[code_idx].codepoint;
410 + specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.);
411 + specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.);
412 + cluster_xp += shaped.positions[code_idx].x_advance / 64.;
413 + cluster_yp += shaped.positions[code_idx].y_advance / 64.;
414 + numspecs++;
415 + } else {
416 + /* If it's not found, try to fetch it through the font cache. */
417 + rune = glyphs[start + idx].u;
418 + for (f = 0; f < frclen; f++) {
419 + glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
420 + /* Everything correct. */
421 + if (glyphidx && frc[f].flags == frcflags)
422 + break;
423 + /* We got a default font for a not found glyph. */
424 + if (!glyphidx && frc[f].flags == frcflags
425 + && frc[f].unicodep == rune) {
426 + break;
427 + }
428 + }
429 +
430 + /* Nothing was found. Use fontconfig to find matching font. */
431 + if (f >= frclen) {
432 + if (!font->set)
433 + font->set = FcFontSort(0, font->pattern,
434 + 1, 0, &fcres);
435 + fcsets[0] = font->set;
436 +
437 + /*
438 + * Nothing was found in the cache. Now use
439 + * some dozen of Fontconfig calls to get the
440 + * font for one single character.
441 + *
442 + * Xft and fontconfig are design failures.
443 + */
444 + fcpattern = FcPatternDuplicate(font->pattern);
445 + fccharset = FcCharSetCreate();
446 +
447 + FcCharSetAddChar(fccharset, rune);
448 + FcPatternAddCharSet(fcpattern, FC_CHARSET,
449 + fccharset);
450 + FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
451 +
452 + FcConfigSubstitute(0, fcpattern,
453 + FcMatchPattern);
454 + FcDefaultSubstitute(fcpattern);
455 +
456 + fontpattern = FcFontSetMatch(0, fcsets, 1,
457 + fcpattern, &fcres);
458 +
459 + /* Allocate memory for the new cache entry. */
460 + if (frclen >= frccap) {
461 + frccap += 16;
462 + frc = xrealloc(frc, frccap * sizeof(Fontcache));
463 + }
464 +
465 + frc[frclen].font = XftFontOpenPattern(xw.dpy,
466 + fontpattern);
467 + if (!frc[frclen].font)
468 + die("XftFontOpenPattern failed seeking fallback font: %s\n",
469 + strerror(errno));
470 + frc[frclen].flags = frcflags;
471 + frc[frclen].unicodep = rune;
472 +
473 + glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
474 +
475 + f = frclen;
476 + frclen++;
477 +
478 + FcPatternDestroy(fcpattern);
479 + FcCharSetDestroy(fccharset);
480 + }
481 +
482 + specs[numspecs].font = frc[f].font;
483 + specs[numspecs].glyph = glyphidx;
484 + specs[numspecs].x = (short)xp;
485 + specs[numspecs].y = (short)yp;
486 + numspecs++;
487 + }
488 }
489 - }
490
491 - /* Nothing was found. Use fontconfig to find matching font. */
492 - if (f >= frclen) {
493 - if (!font->set)
494 - font->set = FcFontSort(0, font->pattern,
495 - 1, 0, &fcres);
496 - fcsets[0] = font->set;
497 + /* Cleanup and get ready for next segment. */
498 + hbcleanup(&shaped);
499 + start = i;
500
501 - /*
502 - * Nothing was found in the cache. Now use
503 - * some dozen of Fontconfig calls to get the
504 - * font for one single character.
505 - *
506 - * Xft and fontconfig are design failures.
507 - */
508 - fcpattern = FcPatternDuplicate(font->pattern);
509 - fccharset = FcCharSetCreate();
510 -
511 - FcCharSetAddChar(fccharset, rune);
512 - FcPatternAddCharSet(fcpattern, FC_CHARSET,
513 - fccharset);
514 - FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
515 -
516 - FcConfigSubstitute(0, fcpattern,
517 - FcMatchPattern);
518 - FcDefaultSubstitute(fcpattern);
519 -
520 - fontpattern = FcFontSetMatch(0, fcsets, 1,
521 - fcpattern, &fcres);
522 -
523 - /* Allocate memory for the new cache entry. */
524 - if (frclen >= frccap) {
525 - frccap += 16;
526 - frc = xrealloc(frc, frccap * sizeof(Fontcache));
527 + /* Determine font for glyph if different from previous glyph. */
528 + if (prevmode != mode) {
529 + prevmode = mode;
530 + xresetfontsettings(mode, &font, &frcflags);
531 + yp = winy + font->ascent;
532 }
533 -
534 - frc[frclen].font = XftFontOpenPattern(xw.dpy,
535 - fontpattern);
536 - if (!frc[frclen].font)
537 - die("XftFontOpenPattern failed seeking fallback font: %s\n",
538 - strerror(errno));
539 - frc[frclen].flags = frcflags;
540 - frc[frclen].unicodep = rune;
541 -
542 - glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
543 -
544 - f = frclen;
545 - frclen++;
546 -
547 - FcPatternDestroy(fcpattern);
548 - FcCharSetDestroy(fccharset);
549 }
550 -
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 - xp += runewidth;
556 - numspecs++;
557 }
558
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 +1576,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 +1721,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 +1739,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