st-ligatures-alpha-scrollback-ringbuffer-20241226-0.9.2.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-ringbuffer-20241226-0.9.2.diff (18905B)
---
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 069a6c2..977b7c7 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 -lXrender\
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 d9b163e..fbca4ba 100644
214 --- a/st.c
215 +++ b/st.c
216 @@ -2777,9 +2777,10 @@ draw(void)
217
218 drawregion(0, 0, term.col, term.row);
219 if (TSCREEN.off == 0)
220 xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx],
221 - term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]);
222 + term.ocx, term.ocy, TLINE(term.ocy)[term.ocx],
223 + TLINE(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 073851a..d0b071d 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 c497e53..a213e52 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 @@ -143,10 +144,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 @@ -760,9 +762,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 @@ -1072,8 +1074,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 @@ -1203,9 +1208,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 @@ -1257,144 +1262,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 @@ -1528,23 +1544,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 @@ -1676,30 +1695,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)