st-ligatures-boxdraw-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-boxdraw-20241226-0.9.2.diff (19317B)
---
1 diff --git a/Makefile b/Makefile
2 index a64b4c2..05124bf 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 boxdraw.c
11 +SRC = st.c x.c boxdraw.c hb.c
12 OBJ = $(SRC:.c=.o)
13
14 all: st
15
16 @@ -15,10 +15,11 @@ 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 boxdraw.o: config.h st.h boxdraw_data.h
24 +hb.o: st.h
25
26 $(OBJ): config.h config.mk
27
28 st: $(OBJ)
29 diff --git a/config.mk b/config.mk
30 index fdc29a7..6833b3b 100644
31 --- a/config.mk
32 +++ b/config.mk
33 @@ -14,12 +14,14 @@ PKG_CONFIG = pkg-config
34
35 # includes and libs
36 INCS = -I$(X11INC) \
37 `$(PKG_CONFIG) --cflags fontconfig` \
38 - `$(PKG_CONFIG) --cflags freetype2`
39 + `$(PKG_CONFIG) --cflags freetype2` \
40 + `$(PKG_CONFIG) --cflags harfbuzz`
41 LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \
42 `$(PKG_CONFIG) --libs fontconfig` \
43 - `$(PKG_CONFIG) --libs freetype2`
44 + `$(PKG_CONFIG) --libs freetype2` \
45 + `$(PKG_CONFIG) --libs harfbuzz`
46
47 # flags
48 STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
49 STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS)
50 @@ -28,9 +30,10 @@ STLDFLAGS = $(LIBS) $(LDFLAGS)
51 # OpenBSD:
52 #CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE
53 #LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \
54 # `$(PKG_CONFIG) --libs fontconfig` \
55 -# `$(PKG_CONFIG) --libs freetype2`
56 +# `$(PKG_CONFIG) --libs freetype2` \
57 +# `$(PKG_CONFIG) --libs harfbuzz`
58 #MANPREFIX = ${PREFIX}/man
59
60 # compiler and linker
61 # CC = c99
62 diff --git a/hb.c b/hb.c
63 new file mode 100644
64 index 0000000..99412c8
65 --- /dev/null
66 +++ b/hb.c
67 @@ -0,0 +1,125 @@
68 +#include <stdlib.h>
69 +#include <stdio.h>
70 +#include <math.h>
71 +#include <X11/Xft/Xft.h>
72 +#include <X11/cursorfont.h>
73 +#include <hb.h>
74 +#include <hb-ft.h>
75 +
76 +#include "st.h"
77 +#include "hb.h"
78 +
79 +#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 }
80 +#define BUFFER_STEP 256
81 +
82 +hb_font_t *hbfindfont(XftFont *match);
83 +
84 +typedef struct {
85 + XftFont *match;
86 + hb_font_t *font;
87 +} HbFontMatch;
88 +
89 +typedef struct {
90 + size_t capacity;
91 + HbFontMatch *fonts;
92 +} HbFontCache;
93 +
94 +static HbFontCache hbfontcache = { 0, NULL };
95 +
96 +typedef struct {
97 + size_t capacity;
98 + Rune *runes;
99 +} RuneBuffer;
100 +
101 +static RuneBuffer hbrunebuffer = { 0, NULL };
102 +
103 +/*
104 + * Poplulate the array with a list of font features, wrapped in FEATURE macro,
105 + * e. g.
106 + * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g')
107 + */
108 +hb_feature_t features[] = { };
109 +
110 +void
111 +hbunloadfonts()
112 +{
113 + for (int i = 0; i < hbfontcache.capacity; i++) {
114 + hb_font_destroy(hbfontcache.fonts[i].font);
115 + XftUnlockFace(hbfontcache.fonts[i].match);
116 + }
117 +
118 + if (hbfontcache.fonts != NULL) {
119 + free(hbfontcache.fonts);
120 + hbfontcache.fonts = NULL;
121 + }
122 + hbfontcache.capacity = 0;
123 +}
124 +
125 +hb_font_t *
126 +hbfindfont(XftFont *match)
127 +{
128 + for (int i = 0; i < hbfontcache.capacity; i++) {
129 + if (hbfontcache.fonts[i].match == match)
130 + return hbfontcache.fonts[i].font;
131 + }
132 +
133 + /* Font not found in cache, caching it now. */
134 + hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * (hbfontcache.capacity + 1));
135 + FT_Face face = XftLockFace(match);
136 + hb_font_t *font = hb_ft_font_create(face, NULL);
137 + if (font == NULL)
138 + die("Failed to load Harfbuzz font.");
139 +
140 + hbfontcache.fonts[hbfontcache.capacity].match = match;
141 + hbfontcache.fonts[hbfontcache.capacity].font = font;
142 + hbfontcache.capacity += 1;
143 +
144 + return font;
145 +}
146 +
147 +void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, int start, int length) {
148 + ushort mode = USHRT_MAX;
149 + unsigned int glyph_count;
150 + int rune_idx, glyph_idx, end = start + length;
151 +
152 + hb_font_t *font = hbfindfont(xfont);
153 + if (font == NULL)
154 + return;
155 +
156 + hb_buffer_t *buffer = hb_buffer_create();
157 + hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
158 + hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS);
159 +
160 + /* Resize the buffer if required length is larger. */
161 + if (hbrunebuffer.capacity < length) {
162 + hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_STEP;
163 + hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer.capacity * sizeof(Rune));
164 + }
165 +
166 + /* Fill buffer with codepoints. */
167 + for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, rune_idx++) {
168 + hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u;
169 + mode = glyphs[glyph_idx].mode;
170 + if (mode & ATTR_WDUMMY)
171 + hbrunebuffer.runes[rune_idx] = 0x0020;
172 + }
173 + hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, length);
174 +
175 + /* Shape the segment. */
176 + hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t));
177 +
178 + /* Get new glyph info. */
179 + hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_count);
180 + hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &glyph_count);
181 +
182 + /* Fill the output. */
183 + data->buffer = buffer;
184 + data->glyphs = info;
185 + data->positions = pos;
186 + data->count = glyph_count;
187 +}
188 +
189 +void hbcleanup(HbTransformData *data) {
190 + hb_buffer_destroy(data->buffer);
191 + memset(data, 0, sizeof(HbTransformData));
192 +}
193 diff --git a/hb.h b/hb.h
194 new file mode 100644
195 index 0000000..3b0ef44
196 --- /dev/null
197 +++ b/hb.h
198 @@ -0,0 +1,14 @@
199 +#include <X11/Xft/Xft.h>
200 +#include <hb.h>
201 +#include <hb-ft.h>
202 +
203 +typedef struct {
204 + hb_buffer_t *buffer;
205 + hb_glyph_info_t *glyphs;
206 + hb_glyph_position_t *positions;
207 + unsigned int count;
208 +} HbTransformData;
209 +
210 +void hbunloadfonts();
211 +void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
212 +void hbcleanup(HbTransformData *);
213 diff --git a/st.c b/st.c
214 index ec6fbf3..1385f77 100644
215 --- a/st.c
216 +++ b/st.c
217 @@ -2661,9 +2661,10 @@ draw(void)
218 cx--;
219
220 drawregion(0, 0, term.col, term.row);
221 xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
222 - term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
223 + term.ocx, term.ocy, term.line[term.ocy][term.ocx],
224 + term.line[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 808f5f7..ae41368 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 978a8fc..c2d9993 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 @@ -140,10 +141,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 @@ -756,9 +758,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 @@ -1061,8 +1063,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 @@ -1184,9 +1189,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 @@ -1240,149 +1245,162 @@ xinit(int cols, int rows)
322
323 boxdraw_xinit(xw.dpy, xw.cmap, xw.draw, xw.vis);
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;
403 + cluster_xp = xp;
404 + cluster_yp = yp;
405 }
406
407 - if (mode & ATTR_BOXDRAW) {
408 + if (glyphs[idx].mode & ATTR_BOXDRAW) {
409 /* minor shoehorning: boxdraw uses only this ushort */
410 - glyphidx = boxdrawindex(&glyphs[i]);
411 - } else {
412 - /* Lookup character index with default font. */
413 - glyphidx = XftCharIndex(xw.dpy, font->match, rune);
414 - }
415 - if (glyphidx) {
416 specs[numspecs].font = font->match;
417 - specs[numspecs].glyph = glyphidx;
418 - specs[numspecs].x = (short)xp;
419 - specs[numspecs].y = (short)yp;
420 - xp += runewidth;
421 + specs[numspecs].glyph = boxdrawindex(&glyphs[idx]);
422 + specs[numspecs].x = xp;
423 + specs[numspecs].y = yp;
424 numspecs++;
425 - continue;
426 - }
427 -
428 - /* Fallback on font cache, search the font cache for match. */
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 + } else if (shaped.glyphs[code_idx].codepoint != 0) {
439 + /* If symbol is found, put it into the specs. */
440 + specs[numspecs].font = font->match;
441 + specs[numspecs].glyph = shaped.glyphs[code_idx].codepoint;
442 + specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.);
443 + specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.);
444 + cluster_xp += shaped.positions[code_idx].x_advance / 64.;
445 + cluster_yp += shaped.positions[code_idx].y_advance / 64.;
446 + numspecs++;
447 + } else {
448 + /* If it's not found, try to fetch it through the font cache. */
449 + rune = glyphs[idx].u;
450 + for (f = 0; f < frclen; f++) {
451 + glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
452 + /* Everything correct. */
453 + if (glyphidx && frc[f].flags == frcflags)
454 + break;
455 + /* We got a default font for a not found glyph. */
456 + if (!glyphidx && frc[f].flags == frcflags
457 + && frc[f].unicodep == rune) {
458 + break;
459 + }
460 }
461 - }
462 -
463 - /* Nothing was found. Use fontconfig to find matching font. */
464 - if (f >= frclen) {
465 - if (!font->set)
466 - font->set = FcFontSort(0, font->pattern,
467 - 1, 0, &fcres);
468 - fcsets[0] = font->set;
469 -
470 - /*
471 - * Nothing was found in the cache. Now use
472 - * some dozen of Fontconfig calls to get the
473 - * font for one single character.
474 - *
475 - * Xft and fontconfig are design failures.
476 - */
477 - fcpattern = FcPatternDuplicate(font->pattern);
478 - fccharset = FcCharSetCreate();
479 -
480 - FcCharSetAddChar(fccharset, rune);
481 - FcPatternAddCharSet(fcpattern, FC_CHARSET,
482 - fccharset);
483 - FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
484 -
485 - FcConfigSubstitute(0, fcpattern,
486 - FcMatchPattern);
487 - FcDefaultSubstitute(fcpattern);
488
489 - fontpattern = FcFontSetMatch(0, fcsets, 1,
490 - fcpattern, &fcres);
491 -
492 - /* Allocate memory for the new cache entry. */
493 - if (frclen >= frccap) {
494 - frccap += 16;
495 - frc = xrealloc(frc, frccap * sizeof(Fontcache));
496 + /* Nothing was found. Use fontconfig to find matching font. */
497 + if (f >= frclen) {
498 + if (!font->set)
499 + font->set = FcFontSort(0, font->pattern,
500 + 1, 0, &fcres);
501 + fcsets[0] = font->set;
502 +
503 + /*
504 + * Nothing was found in the cache. Now use
505 + * some dozen of Fontconfig calls to get the
506 + * font for one single character.
507 + *
508 + * Xft and fontconfig are design failures.
509 + */
510 + fcpattern = FcPatternDuplicate(font->pattern);
511 + fccharset = FcCharSetCreate();
512 +
513 + FcCharSetAddChar(fccharset, rune);
514 + FcPatternAddCharSet(fcpattern, FC_CHARSET,
515 + fccharset);
516 + FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
517 +
518 + FcConfigSubstitute(0, fcpattern,
519 + FcMatchPattern);
520 + FcDefaultSubstitute(fcpattern);
521 +
522 + fontpattern = FcFontSetMatch(0, fcsets, 1,
523 + fcpattern, &fcres);
524 +
525 + /* Allocate memory for the new cache entry. */
526 + if (frclen >= frccap) {
527 + frccap += 16;
528 + frc = xrealloc(frc, frccap * sizeof(Fontcache));
529 + }
530 +
531 + frc[frclen].font = XftFontOpenPattern(xw.dpy,
532 + fontpattern);
533 + if (!frc[frclen].font)
534 + die("XftFontOpenPattern failed seeking fallback font: %s\n",
535 + strerror(errno));
536 + frc[frclen].flags = frcflags;
537 + frc[frclen].unicodep = rune;
538 +
539 + glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
540 +
541 + f = frclen;
542 + frclen++;
543 +
544 + FcPatternDestroy(fcpattern);
545 + FcCharSetDestroy(fccharset);
546 }
547
548 - frc[frclen].font = XftFontOpenPattern(xw.dpy,
549 - fontpattern);
550 - if (!frc[frclen].font)
551 - die("XftFontOpenPattern failed seeking fallback font: %s\n",
552 - strerror(errno));
553 - frc[frclen].flags = frcflags;
554 - frc[frclen].unicodep = rune;
555 -
556 - glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
557 -
558 - f = frclen;
559 - frclen++;
560 -
561 - FcPatternDestroy(fcpattern);
562 - FcCharSetDestroy(fccharset);
563 + specs[numspecs].font = frc[f].font;
564 + specs[numspecs].glyph = glyphidx;
565 + specs[numspecs].x = (short)xp;
566 + specs[numspecs].y = (short)yp;
567 + numspecs++;
568 }
569 -
570 - specs[numspecs].font = frc[f].font;
571 - specs[numspecs].glyph = glyphidx;
572 - specs[numspecs].x = (short)xp;
573 - specs[numspecs].y = (short)yp;
574 - xp += runewidth;
575 - numspecs++;
576 }
577
578 + /* Cleanup and get ready for next segment. */
579 + hbcleanup(&shaped);
580 return numspecs;
581 }
582
583 void
584 -xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
585 +xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y, int charlen)
586 {
587 - int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
588 int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
589 width = charlen * win.cw;
590 Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
591 XRenderColor colfg, colbg;
592 @@ -1520,23 +1538,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
593 void
594 xdrawglyph(Glyph g, int x, int y)
595 {
596 int numspecs;
597 - XftGlyphFontSpec spec;
598 + XftGlyphFontSpec *specs = xw.specbuf;
599
600 - numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
601 - xdrawglyphfontspecs(&spec, g, numspecs, x, y);
602 + numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y);
603 + xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? 2 : 1);
604 }
605
606 void
607 -xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
608 +xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int len)
609 {
610 Color drawcol;
611
612 /* remove the old cursor */
613 if (selected(ox, oy))
614 og.mode ^= ATTR_REVERSE;
615 - xdrawglyph(og, ox, oy);
616 +
617 + /* Redraw the line where cursor was previously.
618 + * It will restore the ligatures broken by the cursor. */
619 + xdrawline(line, 0, oy, len);
620
621 if (IS_SET(MODE_HIDE))
622 return;
623
624 @@ -1668,30 +1689,30 @@ xdrawline(Line line, int x1, int y1, int x2)
625 int i, x, ox, numspecs;
626 Glyph base, new;
627 XftGlyphFontSpec *specs = xw.specbuf;
628
629 - numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
630 i = ox = 0;
631 - for (x = x1; x < x2 && i < numspecs; x++) {
632 + for (x = x1; x < x2; x++) {
633 new = line[x];
634 if (new.mode == ATTR_WDUMMY)
635 continue;
636 if (selected(x, y1))
637 new.mode ^= ATTR_REVERSE;
638 - if (i > 0 && ATTRCMP(base, new)) {
639 - xdrawglyphfontspecs(specs, base, i, ox, y1);
640 - specs += i;
641 - numspecs -= i;
642 + if ((i > 0) && ATTRCMP(base, new)) {
643 + numspecs = xmakeglyphfontspecs(specs, &line[ox], x - ox, ox, y1);
644 + xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x - ox);
645 i = 0;
646 }
647 if (i == 0) {
648 ox = x;
649 base = new;
650 }
651 i++;
652 }
653 - if (i > 0)
654 - xdrawglyphfontspecs(specs, base, i, ox, y1);
655 + if (i > 0) {
656 + numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox, y1);
657 + xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox);
658 + }
659 }
660
661 void
662 xfinishdraw(void)