st-ligatures-scrollback-ringbuffer-20251007-0.9.3.diff - sites - public wiki contents of suckless.org
(HTM) git clone git://git.suckless.org/sites
(DIR) Log
(DIR) Files
(DIR) Refs
---
st-ligatures-scrollback-ringbuffer-20251007-0.9.3.diff (18944B)
---
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 2fc854e..ec69d1a 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 \
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..96e808b
195 --- /dev/null
196 +++ b/hb.h
197 @@ -0,0 +1,15 @@
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 +
213 diff --git a/st.c b/st.c
214 index 0074ec7..fc916e9 100644
215 --- a/st.c
216 +++ b/st.c
217 @@ -2803,9 +2803,10 @@ draw(void)
218
219 drawregion(0, 0, term.col, term.row);
220 if (TSCREEN.off == 0)
221 xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx],
222 - term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]);
223 + term.ocx, term.ocy, TLINE(term.ocy)[term.ocx],
224 + TLINE(term.ocy), term.col);
225 term.ocx = cx;
226 term.ocy = term.c.y;
227 xfinishdraw();
228 if (ocx != term.ocx || ocy != term.ocy)
229 diff --git a/st.h b/st.h
230 index 3cea73b..709a369 100644
231 --- a/st.h
232 +++ b/st.h
233 @@ -10,9 +10,10 @@
234 #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
235 #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
236 #define DEFAULT(a, b) (a) = (a) ? (a) : (b)
237 #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
238 -#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \
239 +#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode & (~ATTR_WRAP)) || \
240 + (a).fg != (b).fg || \
241 (a).bg != (b).bg)
242 #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
243 (t1.tv_nsec-t2.tv_nsec)/1E6)
244 #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
245 diff --git a/win.h b/win.h
246 index 6de960d..94679e4 100644
247 --- a/win.h
248 +++ b/win.h
249 @@ -24,9 +24,9 @@ enum win_mode {
250 };
251
252 void xbell(void);
253 void xclipcopy(void);
254 -void xdrawcursor(int, int, Glyph, int, int, Glyph);
255 +void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int);
256 void xdrawline(Line, int, int, int);
257 void xfinishdraw(void);
258 void xloadcols(void);
259 int xsetcolorname(int, const char *);
260 diff --git a/x.c b/x.c
261 index 1e4bdf5..f8372ba 100644
262 --- a/x.c
263 +++ b/x.c
264 @@ -18,8 +18,9 @@
265 char *argv0;
266 #include "arg.h"
267 #include "st.h"
268 #include "win.h"
269 +#include "hb.h"
270
271 /* types used in config.h */
272 typedef struct {
273 uint mod;
274 @@ -142,10 +143,11 @@ typedef struct {
275 GC gc;
276 } DC;
277
278 static inline ushort sixd_to_16bit(int);
279 +static void xresetfontsettings(ushort mode, Font **font, int *frcflags);
280 static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int);
281 -static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int);
282 +static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int, int);
283 static void xdrawglyph(Glyph, int, int);
284 static void xclear(int, int, int, int);
285 static int xgeommasktogravity(int);
286 static int ximopen(Display *);
287 @@ -758,9 +760,9 @@ xresize(int col, int row)
288 XftDrawChange(xw.draw, xw.buf);
289 xclear(0, 0, win.w, win.h);
290
291 /* resize to new width */
292 - xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
293 + xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
294 }
295
296 ushort
297 sixd_to_16bit(int x)
298 @@ -1063,8 +1065,11 @@ xunloadfont(Font *f)
299
300 void
301 xunloadfonts(void)
302 {
303 + /* Clear Harfbuzz font cache. */
304 + hbunloadfonts();
305 +
306 /* Free the loaded fonts in the font cache. */
307 while (frclen > 0)
308 XftFontClose(xw.dpy, frc[--frclen].font);
309
310 @@ -1189,9 +1194,9 @@ xinit(int cols, int rows)
311 XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel);
312 XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
313
314 /* font spec buffer */
315 - xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
316 + xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
317
318 /* Xft rendering context */
319 xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
320
321 @@ -1243,144 +1248,155 @@ xinit(int cols, int rows)
322 if (xsel.xtarget == None)
323 xsel.xtarget = XA_STRING;
324 }
325
326 +void
327 +xresetfontsettings(ushort mode, Font **font, int *frcflags)
328 +{
329 + *font = &dc.font;
330 + if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
331 + *font = &dc.ibfont;
332 + *frcflags = FRC_ITALICBOLD;
333 + } else if (mode & ATTR_ITALIC) {
334 + *font = &dc.ifont;
335 + *frcflags = FRC_ITALIC;
336 + } else if (mode & ATTR_BOLD) {
337 + *font = &dc.bfont;
338 + *frcflags = FRC_BOLD;
339 + }
340 +}
341 +
342 int
343 xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
344 {
345 float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp;
346 - ushort mode, prevmode = USHRT_MAX;
347 + ushort mode = glyphs[0].mode & ~ATTR_WRAP;
348 Font *font = &dc.font;
349 int frcflags = FRC_NORMAL;
350 - float runewidth = win.cw;
351 + float runewidth = win.cw * ((glyphs[0].mode & ATTR_WIDE) ? 2.0f : 1.0f);
352 Rune rune;
353 FT_UInt glyphidx;
354 FcResult fcres;
355 FcPattern *fcpattern, *fontpattern;
356 FcFontSet *fcsets[] = { NULL };
357 FcCharSet *fccharset;
358 - int i, f, numspecs = 0;
359 + int f, code_idx, numspecs = 0;
360 + float cluster_xp = xp, cluster_yp = yp;
361 + HbTransformData shaped = { 0 };
362
363 - for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
364 - /* Fetch rune and mode for current glyph. */
365 - rune = glyphs[i].u;
366 - mode = glyphs[i].mode;
367 + /* Initial values. */
368 + xresetfontsettings(mode, &font, &frcflags);
369
370 - /* Skip dummy wide-character spacing. */
371 - if (mode == ATTR_WDUMMY)
372 + /* Shape the segment. */
373 + hbtransform(&shaped, font->match, glyphs, 0, len);
374 + xp = winx; yp = winy + font->ascent;
375 + cluster_xp = xp; cluster_yp = yp;
376 +
377 + for (code_idx = 0; code_idx < shaped.count; code_idx++) {
378 + int idx = shaped.glyphs[code_idx].cluster;
379 +
380 + if (glyphs[idx].mode & ATTR_WDUMMY)
381 continue;
382
383 - /* Determine font for glyph if different from previous glyph. */
384 - if (prevmode != mode) {
385 - prevmode = mode;
386 - font = &dc.font;
387 - frcflags = FRC_NORMAL;
388 - runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f);
389 - if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
390 - font = &dc.ibfont;
391 - frcflags = FRC_ITALICBOLD;
392 - } else if (mode & ATTR_ITALIC) {
393 - font = &dc.ifont;
394 - frcflags = FRC_ITALIC;
395 - } else if (mode & ATTR_BOLD) {
396 - font = &dc.bfont;
397 - frcflags = FRC_BOLD;
398 - }
399 - yp = winy + font->ascent;
400 + /* Advance the drawing cursor if we've moved to a new cluster */
401 + if (code_idx > 0 && (idx != shaped.glyphs[code_idx - 1].cluster)) {
402 + xp += runewidth * (idx - shaped.glyphs[code_idx - 1].cluster);
403 + cluster_xp = xp;
404 + cluster_yp = yp;
405 }
406
407 - /* Lookup character index with default font. */
408 - glyphidx = XftCharIndex(xw.dpy, font->match, rune);
409 - if (glyphidx) {
410 + if (shaped.glyphs[code_idx].codepoint != 0) {
411 + /* If symbol is found, put it into the specs. */
412 specs[numspecs].font = font->match;
413 - specs[numspecs].glyph = glyphidx;
414 - specs[numspecs].x = (short)xp;
415 - specs[numspecs].y = (short)yp;
416 - xp += runewidth;
417 + specs[numspecs].glyph = shaped.glyphs[code_idx].codepoint;
418 + specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.);
419 + specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.);
420 + cluster_xp += shaped.positions[code_idx].x_advance / 64.;
421 + cluster_yp += shaped.positions[code_idx].y_advance / 64.;
422 numspecs++;
423 - continue;
424 - }
425 -
426 - /* Fallback on font cache, search the font cache for match. */
427 - for (f = 0; f < frclen; f++) {
428 - glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
429 - /* Everything correct. */
430 - if (glyphidx && frc[f].flags == frcflags)
431 - break;
432 - /* We got a default font for a not found glyph. */
433 - if (!glyphidx && frc[f].flags == frcflags
434 - && frc[f].unicodep == rune) {
435 - break;
436 + } else {
437 + /* If it's not found, try to fetch it through the font cache. */
438 + rune = glyphs[idx].u;
439 + for (f = 0; f < frclen; f++) {
440 + glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
441 + /* Everything correct. */
442 + if (glyphidx && frc[f].flags == frcflags)
443 + break;
444 + /* We got a default font for a not found glyph. */
445 + if (!glyphidx && frc[f].flags == frcflags
446 + && frc[f].unicodep == rune) {
447 + break;
448 + }
449 }
450 - }
451 -
452 - /* Nothing was found. Use fontconfig to find matching font. */
453 - if (f >= frclen) {
454 - if (!font->set)
455 - font->set = FcFontSort(0, font->pattern,
456 - 1, 0, &fcres);
457 - fcsets[0] = font->set;
458
459 - /*
460 - * Nothing was found in the cache. Now use
461 - * some dozen of Fontconfig calls to get the
462 - * font for one single character.
463 - *
464 - * Xft and fontconfig are design failures.
465 - */
466 - fcpattern = FcPatternDuplicate(font->pattern);
467 - fccharset = FcCharSetCreate();
468 -
469 - FcCharSetAddChar(fccharset, rune);
470 - FcPatternAddCharSet(fcpattern, FC_CHARSET,
471 - fccharset);
472 - FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
473 -
474 - FcConfigSubstitute(0, fcpattern,
475 - FcMatchPattern);
476 - FcDefaultSubstitute(fcpattern);
477 -
478 - fontpattern = FcFontSetMatch(0, fcsets, 1,
479 - fcpattern, &fcres);
480 -
481 - /* Allocate memory for the new cache entry. */
482 - if (frclen >= frccap) {
483 - frccap += 16;
484 - frc = xrealloc(frc, frccap * sizeof(Fontcache));
485 + /* Nothing was found. Use fontconfig to find matching font. */
486 + if (f >= frclen) {
487 + if (!font->set)
488 + font->set = FcFontSort(0, font->pattern,
489 + 1, 0, &fcres);
490 + fcsets[0] = font->set;
491 +
492 + /*
493 + * Nothing was found in the cache. Now use
494 + * some dozen of Fontconfig calls to get the
495 + * font for one single character.
496 + *
497 + * Xft and fontconfig are design failures.
498 + */
499 + fcpattern = FcPatternDuplicate(font->pattern);
500 + fccharset = FcCharSetCreate();
501 +
502 + FcCharSetAddChar(fccharset, rune);
503 + FcPatternAddCharSet(fcpattern, FC_CHARSET,
504 + fccharset);
505 + FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
506 +
507 + FcConfigSubstitute(0, fcpattern,
508 + FcMatchPattern);
509 + FcDefaultSubstitute(fcpattern);
510 +
511 + fontpattern = FcFontSetMatch(0, fcsets, 1,
512 + fcpattern, &fcres);
513 +
514 + /* Allocate memory for the new cache entry. */
515 + if (frclen >= frccap) {
516 + frccap += 16;
517 + frc = xrealloc(frc, frccap * sizeof(Fontcache));
518 + }
519 +
520 + frc[frclen].font = XftFontOpenPattern(xw.dpy,
521 + fontpattern);
522 + if (!frc[frclen].font)
523 + die("XftFontOpenPattern failed seeking fallback font: %s\n",
524 + strerror(errno));
525 + frc[frclen].flags = frcflags;
526 + frc[frclen].unicodep = rune;
527 +
528 + glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
529 +
530 + f = frclen;
531 + frclen++;
532 +
533 + FcPatternDestroy(fcpattern);
534 + FcCharSetDestroy(fccharset);
535 }
536
537 - frc[frclen].font = XftFontOpenPattern(xw.dpy,
538 - fontpattern);
539 - if (!frc[frclen].font)
540 - die("XftFontOpenPattern failed seeking fallback font: %s\n",
541 - strerror(errno));
542 - frc[frclen].flags = frcflags;
543 - frc[frclen].unicodep = rune;
544 -
545 - glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
546 -
547 - f = frclen;
548 - frclen++;
549 -
550 - FcPatternDestroy(fcpattern);
551 - FcCharSetDestroy(fccharset);
552 + specs[numspecs].font = frc[f].font;
553 + specs[numspecs].glyph = glyphidx;
554 + specs[numspecs].x = (short)xp;
555 + specs[numspecs].y = (short)yp;
556 + numspecs++;
557 }
558 -
559 - specs[numspecs].font = frc[f].font;
560 - specs[numspecs].glyph = glyphidx;
561 - specs[numspecs].x = (short)xp;
562 - specs[numspecs].y = (short)yp;
563 - xp += runewidth;
564 - numspecs++;
565 }
566
567 + /* Cleanup and get ready for next segment. */
568 + hbcleanup(&shaped);
569 return numspecs;
570 }
571
572 void
573 -xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
574 +xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y, int charlen)
575 {
576 - int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
577 int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
578 width = charlen * win.cw;
579 Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
580 XRenderColor colfg, colbg;
581 @@ -1514,23 +1530,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
582 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
613 @@ -1662,30 +1681,30 @@ xdrawline(Line line, int x1, int y1, int x2)
614 int i, x, ox, numspecs;
615 Glyph base, new;
616 XftGlyphFontSpec *specs = xw.specbuf;
617
618 - numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
619 i = ox = 0;
620 - for (x = x1; x < x2 && i < numspecs; x++) {
621 + for (x = x1; x < x2; x++) {
622 new = line[x];
623 if (new.mode == ATTR_WDUMMY)
624 continue;
625 if (selected(x, y1))
626 new.mode ^= ATTR_REVERSE;
627 - if (i > 0 && ATTRCMP(base, new)) {
628 - xdrawglyphfontspecs(specs, base, i, ox, y1);
629 - specs += i;
630 - numspecs -= i;
631 + if ((i > 0) && ATTRCMP(base, new)) {
632 + numspecs = xmakeglyphfontspecs(specs, &line[ox], x - ox, ox, y1);
633 + xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x - ox);
634 i = 0;
635 }
636 if (i == 0) {
637 ox = x;
638 base = new;
639 }
640 i++;
641 }
642 - if (i > 0)
643 - xdrawglyphfontspecs(specs, base, i, ox, y1);
644 + if (i > 0) {
645 + numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox, y1);
646 + xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox);
647 + }
648 }
649
650 void
651 xfinishdraw(void)