Introduction
Introduction Statistics Contact Development Disclaimer Help
[st][patches][ligatures] - sites - public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log
Files
Refs
---
commit 77456c4698f33511b0652c9dfd6f2904b4797584
parent b034a2ce4801188b3af190036eb66a13e24cbffc
Author: Alexander Rogachev <[email protected]>
Date: Fri, 10 Jan 2025 01:55:48 +0400
[st][patches][ligatures]
Some code cleanup and refactoring.
Diffstat:
D st.suckless.org/patches/ligatures/… | 633 ---------------------------…
A st.suckless.org/patches/ligatures/… | 650 +++++++++++++++++++++++++++…
D st.suckless.org/patches/ligatures/… | 635 ---------------------------…
A st.suckless.org/patches/ligatures/… | 650 +++++++++++++++++++++++++++…
D st.suckless.org/patches/ligatures/… | 635 ---------------------------…
A st.suckless.org/patches/ligatures/… | 650 +++++++++++++++++++++++++++…
D st.suckless.org/patches/ligatures/… | 635 ---------------------------…
A st.suckless.org/patches/ligatures/… | 650 +++++++++++++++++++++++++++…
D st.suckless.org/patches/ligatures/… | 647 ---------------------------…
A st.suckless.org/patches/ligatures/… | 662 +++++++++++++++++++++++++++…
D st.suckless.org/patches/ligatures/… | 634 ---------------------------…
A st.suckless.org/patches/ligatures/… | 650 +++++++++++++++++++++++++++…
D st.suckless.org/patches/ligatures/… | 633 ---------------------------…
A st.suckless.org/patches/ligatures/… | 650 +++++++++++++++++++++++++++…
M st.suckless.org/patches/ligatures/… | 14 +++++++-------
15 files changed, 4569 insertions(+), 4459 deletions(-)
---
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-20240427-0.9.…
@@ -1,633 +0,0 @@
-diff --git a/Makefile b/Makefile
-index 470ac86..38240da 100644
---- a/Makefile
-+++ b/Makefile
-@@ -4,7 +4,7 @@
-
- include config.mk
-
--SRC = st.c x.c
-+SRC = st.c x.c hb.c
- OBJ = $(SRC:.c=.o)
-
- all: st
-@@ -22,7 +22,8 @@ config.h:
- $(CC) $(STCFLAGS) -c $<
-
- st.o: config.h st.h win.h
--x.o: arg.h config.h st.h win.h
-+x.o: arg.h config.h st.h win.h hb.h
-+hb.o: st.h
-
- $(OBJ): config.h config.mk
-
-diff --git a/config.mk b/config.mk
-index 1e306f8..3e13e53 100644
---- a/config.mk
-+++ b/config.mk
-@@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config
- # includes and libs
- INCS = -I$(X11INC) \
- `$(PKG_CONFIG) --cflags fontconfig` \
-- `$(PKG_CONFIG) --cflags freetype2`
-+ `$(PKG_CONFIG) --cflags freetype2` \
-+ `$(PKG_CONFIG) --cflags harfbuzz`
- LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \
- `$(PKG_CONFIG) --libs fontconfig` \
-- `$(PKG_CONFIG) --libs freetype2`
-+ `$(PKG_CONFIG) --libs freetype2` \
-+ `$(PKG_CONFIG) --libs harfbuzz`
-
- # flags
- STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
-diff --git a/hb.c b/hb.c
-new file mode 100644
-index 0000000..99412c8
---- /dev/null
-+++ b/hb.c
-@@ -0,0 +1,125 @@
-+#include <stdlib.h>
-+#include <stdio.h>
-+#include <math.h>
-+#include <X11/Xft/Xft.h>
-+#include <X11/cursorfont.h>
-+#include <hb.h>
-+#include <hb-ft.h>
-+
-+#include "st.h"
-+#include "hb.h"
-+
-+#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start…
-+#define BUFFER_STEP 256
-+
-+hb_font_t *hbfindfont(XftFont *match);
-+
-+typedef struct {
-+ XftFont *match;
-+ hb_font_t *font;
-+} HbFontMatch;
-+
-+typedef struct {
-+ size_t capacity;
-+ HbFontMatch *fonts;
-+} HbFontCache;
-+
-+static HbFontCache hbfontcache = { 0, NULL };
-+
-+typedef struct {
-+ size_t capacity;
-+ Rune *runes;
-+} RuneBuffer;
-+
-+static RuneBuffer hbrunebuffer = { 0, NULL };
-+
-+/*
-+ * Poplulate the array with a list of font features, wrapped in FEATURE macro,
-+ * e. g.
-+ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g')
-+ */
-+hb_feature_t features[] = { };
-+
-+void
-+hbunloadfonts()
-+{
-+ for (int i = 0; i < hbfontcache.capacity; i++) {
-+ hb_font_destroy(hbfontcache.fonts[i].font);
-+ XftUnlockFace(hbfontcache.fonts[i].match);
-+ }
-+
-+ if (hbfontcache.fonts != NULL) {
-+ free(hbfontcache.fonts);
-+ hbfontcache.fonts = NULL;
-+ }
-+ hbfontcache.capacity = 0;
-+}
-+
-+hb_font_t *
-+hbfindfont(XftFont *match)
-+{
-+ for (int i = 0; i < hbfontcache.capacity; i++) {
-+ if (hbfontcache.fonts[i].match == match)
-+ return hbfontcache.fonts[i].font;
-+ }
-+
-+ /* Font not found in cache, caching it now. */
-+ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * …
-+ FT_Face face = XftLockFace(match);
-+ hb_font_t *font = hb_ft_font_create(face, NULL);
-+ if (font == NULL)
-+ die("Failed to load Harfbuzz font.");
-+
-+ hbfontcache.fonts[hbfontcache.capacity].match = match;
-+ hbfontcache.fonts[hbfontcache.capacity].font = font;
-+ hbfontcache.capacity += 1;
-+
-+ return font;
-+}
-+
-+void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, …
-+ ushort mode = USHRT_MAX;
-+ unsigned int glyph_count;
-+ int rune_idx, glyph_idx, end = start + length;
-+
-+ hb_font_t *font = hbfindfont(xfont);
-+ if (font == NULL)
-+ return;
-+
-+ hb_buffer_t *buffer = hb_buffer_create();
-+ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
-+ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_…
-+
-+ /* Resize the buffer if required length is larger. */
-+ if (hbrunebuffer.capacity < length) {
-+ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S…
-+ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer…
-+ }
-+
-+ /* Fill buffer with codepoints. */
-+ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r…
-+ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u;
-+ mode = glyphs[glyph_idx].mode;
-+ if (mode & ATTR_WDUMMY)
-+ hbrunebuffer.runes[rune_idx] = 0x0020;
-+ }
-+ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt…
-+
-+ /* Shape the segment. */
-+ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t…
-+
-+ /* Get new glyph info. */
-+ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun…
-+ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly…
-+
-+ /* Fill the output. */
-+ data->buffer = buffer;
-+ data->glyphs = info;
-+ data->positions = pos;
-+ data->count = glyph_count;
-+}
-+
-+void hbcleanup(HbTransformData *data) {
-+ hb_buffer_destroy(data->buffer);
-+ memset(data, 0, sizeof(HbTransformData));
-+}
-diff --git a/hb.h b/hb.h
-new file mode 100644
-index 0000000..3b0ef44
---- /dev/null
-+++ b/hb.h
-@@ -0,0 +1,14 @@
-+#include <X11/Xft/Xft.h>
-+#include <hb.h>
-+#include <hb-ft.h>
-+
-+typedef struct {
-+ hb_buffer_t *buffer;
-+ hb_glyph_info_t *glyphs;
-+ hb_glyph_position_t *positions;
-+ unsigned int count;
-+} HbTransformData;
-+
-+void hbunloadfonts();
-+void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
-+void hbcleanup(HbTransformData *);
-diff --git a/st.c b/st.c
-index 62def59..041c6d8 100644
---- a/st.c
-+++ b/st.c
-@@ -2640,7 +2640,8 @@ draw(void)
-
- drawregion(0, 0, term.col, term.row);
- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
-- term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
-+ term.ocx, term.ocy, term.line[term.ocy][term.ocx],
-+ term.line[term.ocy], term.col);
- term.ocx = cx;
- term.ocy = term.c.y;
- xfinishdraw();
-diff --git a/st.h b/st.h
-index fd3b0d8..142fdfe 100644
---- a/st.h
-+++ b/st.h
-@@ -11,7 +11,8 @@
- #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
- #define DEFAULT(a, b) (a) = (a) ? (a) : (b)
- #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)…
--#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f…
-+#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode …
-+ (a).fg != (b).fg || \
- (a).bg != (b).bg)
- #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
- (t1.tv_nsec-t2.tv_nsec)/1E6)
-diff --git a/win.h b/win.h
-index 6de960d..94679e4 100644
---- a/win.h
-+++ b/win.h
-@@ -25,7 +25,7 @@ enum win_mode {
-
- void xbell(void);
- void xclipcopy(void);
--void xdrawcursor(int, int, Glyph, int, int, Glyph);
-+void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int);
- void xdrawline(Line, int, int, int);
- void xfinishdraw(void);
- void xloadcols(void);
-diff --git a/x.c b/x.c
-index 2a3bd38..66605ae 100644
---- a/x.c
-+++ b/x.c
-@@ -19,6 +19,7 @@ char *argv0;
- #include "arg.h"
- #include "st.h"
- #include "win.h"
-+#include "hb.h"
-
- /* types used in config.h */
- typedef struct {
-@@ -141,8 +142,9 @@ typedef struct {
- } DC;
-
- static inline ushort sixd_to_16bit(int);
-+static void xresetfontsettings(ushort mode, Font **font, int *frcflags);
- static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i…
--static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
-+static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
- static void xdrawglyph(Glyph, int, int);
- static void xclear(int, int, int, int);
- static int xgeommasktogravity(int);
-@@ -757,7 +759,7 @@ xresize(int col, int row)
- xclear(0, 0, win.w, win.h);
-
- /* resize to new width */
-- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
-+ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
- }
-
- ushort
-@@ -1062,6 +1064,9 @@ xunloadfont(Font *f)
- void
- xunloadfonts(void)
- {
-+ /* Clear Harfbuzz font cache. */
-+ hbunloadfonts();
-+
- /* Free the loaded fonts in the font cache. */
- while (frclen > 0)
- XftFontClose(xw.dpy, frc[--frclen].font);
-@@ -1185,7 +1190,7 @@ xinit(int cols, int rows)
- XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
-
- /* font spec buffer */
-- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
-+ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
-
- /* Xft rendering context */
- xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
-@@ -1239,6 +1244,22 @@ xinit(int cols, int rows)
- xsel.xtarget = XA_STRING;
- }
-
-+void
-+xresetfontsettings(ushort mode, Font **font, int *frcflags)
-+{
-+ *font = &dc.font;
-+ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
-+ *font = &dc.ibfont;
-+ *frcflags = FRC_ITALICBOLD;
-+ } else if (mode & ATTR_ITALIC) {
-+ *font = &dc.ifont;
-+ *frcflags = FRC_ITALIC;
-+ } else if (mode & ATTR_BOLD) {
-+ *font = &dc.bfont;
-+ *frcflags = FRC_BOLD;
-+ }
-+}
-+
- int
- xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in…
- {
-@@ -1253,128 +1274,156 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const G…
- FcPattern *fcpattern, *fontpattern;
- FcFontSet *fcsets[] = { NULL };
- FcCharSet *fccharset;
-- int i, f, numspecs = 0;
-+ int i, f, length = 0, start = 0, numspecs = 0;
-+ float cluster_xp = xp, cluster_yp = yp;
-+ HbTransformData shaped = { 0 };
-+
-+ /* Initial values. */
-+ mode = prevmode = glyphs[0].mode & ~ATTR_WRAP;
-+ xresetfontsettings(mode, &font, &frcflags);
-
- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
-- /* Fetch rune and mode for current glyph. */
-- rune = glyphs[i].u;
-- mode = glyphs[i].mode;
-+ mode = glyphs[i].mode & ~ATTR_WRAP;
-
- /* Skip dummy wide-character spacing. */
-- if (mode == ATTR_WDUMMY)
-+ if (mode & ATTR_WDUMMY && i < (len - 1))
- continue;
-
-- /* Determine font for glyph if different from previous glyph.…
-- if (prevmode != mode) {
-- prevmode = mode;
-- font = &dc.font;
-- frcflags = FRC_NORMAL;
-- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0…
-- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
-- font = &dc.ibfont;
-- frcflags = FRC_ITALICBOLD;
-- } else if (mode & ATTR_ITALIC) {
-- font = &dc.ifont;
-- frcflags = FRC_ITALIC;
-- } else if (mode & ATTR_BOLD) {
-- font = &dc.bfont;
-- frcflags = FRC_BOLD;
-+ if (
-+ prevmode != mode
-+ || ATTRCMP(glyphs[start], glyphs[i])
-+ || selected(x + i, y) != selected(x + start, y)
-+ || i == (len - 1)
-+ ) {
-+ /* Handle 1-character wide segments and end of line */
-+ length = i - start;
-+ if (i == start) {
-+ length = 1;
-+ } else if (i == (len - 1)) {
-+ length = (i - start + 1);
- }
-- yp = winy + font->ascent;
-- }
-
-- /* Lookup character index with default font. */
-- glyphidx = XftCharIndex(xw.dpy, font->match, rune);
-- if (glyphidx) {
-- specs[numspecs].font = font->match;
-- specs[numspecs].glyph = glyphidx;
-- specs[numspecs].x = (short)xp;
-- specs[numspecs].y = (short)yp;
-- xp += runewidth;
-- numspecs++;
-- continue;
-- }
--
-- /* Fallback on font cache, search the font cache for match. */
-- for (f = 0; f < frclen; f++) {
-- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
-- /* Everything correct. */
-- if (glyphidx && frc[f].flags == frcflags)
-- break;
-- /* We got a default font for a not found glyph. */
-- if (!glyphidx && frc[f].flags == frcflags
-- && frc[f].unicodep == rune) {
-- break;
-+ /* Shape the segment. */
-+ hbtransform(&shaped, font->match, glyphs, start, leng…
-+ runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE…
-+ cluster_xp = xp; cluster_yp = yp;
-+ for (int code_idx = 0; code_idx < shaped.count; code_…
-+ int idx = shaped.glyphs[code_idx].cluster;
-+
-+ if (glyphs[start + idx].mode & ATTR_WDUMMY)
-+ continue;
-+
-+ /* Advance the drawing cursor if we've moved …
-+ if (code_idx > 0 && idx != shaped.glyphs[code…
-+ xp += runewidth;
-+ cluster_xp = xp;
-+ cluster_yp = yp;
-+ runewidth = win.cw * ((glyphs[start +…
-+ }
-+
-+ if (shaped.glyphs[code_idx].codepoint != 0) {
-+ /* If symbol is found, put it into th…
-+ specs[numspecs].font = font->match;
-+ specs[numspecs].glyph = shaped.glyphs…
-+ specs[numspecs].x = cluster_xp + (sho…
-+ specs[numspecs].y = cluster_yp - (sho…
-+ cluster_xp += shaped.positions[code_i…
-+ cluster_yp += shaped.positions[code_i…
-+ numspecs++;
-+ } else {
-+ /* If it's not found, try to fetch it…
-+ rune = glyphs[start + idx].u;
-+ for (f = 0; f < frclen; f++) {
-+ glyphidx = XftCharIndex(xw.dp…
-+ /* Everything correct. */
-+ if (glyphidx && frc[f].flags …
-+ break;
-+ /* We got a default font for …
-+ if (!glyphidx && frc[f].flags…
-+ && frc[f].uni…
-+ break;
-+ }
-+ }
-+
-+ /* Nothing was found. Use fontconfig …
-+ if (f >= frclen) {
-+ if (!font->set)
-+ font->set = FcFontSor…
-+ …
-+ fcsets[0] = font->set;
-+
-+ /*
-+ * Nothing was found in the c…
-+ * some dozen of Fontconfig c…
-+ * font for one single charac…
-+ *
-+ * Xft and fontconfig are des…
-+ */
-+ fcpattern = FcPatternDuplicat…
-+ fccharset = FcCharSetCreate();
-+
-+ FcCharSetAddChar(fccharset, r…
-+ FcPatternAddCharSet(fcpattern…
-+ fccharset);
-+ FcPatternAddBool(fcpattern, F…
-+
-+ FcConfigSubstitute(0, fcpatte…
-+ FcMatchPatter…
-+ FcDefaultSubstitute(fcpattern…
-+
-+ fontpattern = FcFontSetMatch(…
-+ fcpattern, &f…
-+
-+ /* Allocate memory for the ne…
-+ if (frclen >= frccap) {
-+ frccap += 16;
-+ frc = xrealloc(frc, f…
-+ }
-+
-+ frc[frclen].font = XftFontOpe…
-+ fontpattern);
-+ if (!frc[frclen].font)
-+ die("XftFontOpenPatte…
-+ strerror(errn…
-+ frc[frclen].flags = frcflags;
-+ frc[frclen].unicodep = rune;
-+
-+ glyphidx = XftCharIndex(xw.dp…
-+
-+ f = frclen;
-+ frclen++;
-+
-+ FcPatternDestroy(fcpattern);
-+ FcCharSetDestroy(fccharset);
-+ }
-+
-+ specs[numspecs].font = frc[f].font;
-+ specs[numspecs].glyph = glyphidx;
-+ specs[numspecs].x = (short)xp;
-+ specs[numspecs].y = (short)yp;
-+ numspecs++;
-+ }
- }
-- }
-
-- /* Nothing was found. Use fontconfig to find matching font. */
-- if (f >= frclen) {
-- if (!font->set)
-- font->set = FcFontSort(0, font->pattern,
-- 1, 0, &fcres);
-- fcsets[0] = font->set;
-+ /* Cleanup and get ready for next segment. */
-+ hbcleanup(&shaped);
-+ start = i;
-
-- /*
-- * Nothing was found in the cache. Now use
-- * some dozen of Fontconfig calls to get the
-- * font for one single character.
-- *
-- * Xft and fontconfig are design failures.
-- */
-- fcpattern = FcPatternDuplicate(font->pattern);
-- fccharset = FcCharSetCreate();
--
-- FcCharSetAddChar(fccharset, rune);
-- FcPatternAddCharSet(fcpattern, FC_CHARSET,
-- fccharset);
-- FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
--
-- FcConfigSubstitute(0, fcpattern,
-- FcMatchPattern);
-- FcDefaultSubstitute(fcpattern);
--
-- fontpattern = FcFontSetMatch(0, fcsets, 1,
-- fcpattern, &fcres);
--
-- /* Allocate memory for the new cache entry. */
-- if (frclen >= frccap) {
-- frccap += 16;
-- frc = xrealloc(frc, frccap * sizeof(Fontcache…
-+ /* Determine font for glyph if different from previou…
-+ if (prevmode != mode) {
-+ prevmode = mode;
-+ xresetfontsettings(mode, &font, &frcflags);
-+ yp = winy + font->ascent;
- }
--
-- frc[frclen].font = XftFontOpenPattern(xw.dpy,
-- fontpattern);
-- if (!frc[frclen].font)
-- die("XftFontOpenPattern failed seeking fallba…
-- strerror(errno));
-- frc[frclen].flags = frcflags;
-- frc[frclen].unicodep = rune;
--
-- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run…
--
-- f = frclen;
-- frclen++;
--
-- FcPatternDestroy(fcpattern);
-- FcCharSetDestroy(fccharset);
- }
--
-- specs[numspecs].font = frc[f].font;
-- specs[numspecs].glyph = glyphidx;
-- specs[numspecs].x = (short)xp;
-- specs[numspecs].y = (short)yp;
-- xp += runewidth;
-- numspecs++;
- }
-
- return numspecs;
- }
-
- void
--xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
-+xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
- {
-- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
- int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
- width = charlen * win.cw;
- Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
-@@ -1510,21 +1559,24 @@ void
- xdrawglyph(Glyph g, int x, int y)
- {
- int numspecs;
-- XftGlyphFontSpec spec;
-+ XftGlyphFontSpec *specs = xw.specbuf;
-
-- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
-- xdrawglyphfontspecs(&spec, g, numspecs, x, y);
-+ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y);
-+ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? …
- }
-
- void
--xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
-+xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int…
- {
- Color drawcol;
-
- /* remove the old cursor */
- if (selected(ox, oy))
- og.mode ^= ATTR_REVERSE;
-- xdrawglyph(og, ox, oy);
-+
-+ /* Redraw the line where cursor was previously.
-+ * It will restore the ligatures broken by the cursor. */
-+ xdrawline(line, 0, oy, len);
-
- if (IS_SET(MODE_HIDE))
- return;
-@@ -1652,18 +1704,16 @@ xdrawline(Line line, int x1, int y1, int x2)
- Glyph base, new;
- XftGlyphFontSpec *specs = xw.specbuf;
-
-- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
- i = ox = 0;
-- for (x = x1; x < x2 && i < numspecs; x++) {
-+ for (x = x1; x < x2; x++) {
- new = line[x];
- if (new.mode == ATTR_WDUMMY)
- continue;
- if (selected(x, y1))
- new.mode ^= ATTR_REVERSE;
-- if (i > 0 && ATTRCMP(base, new)) {
-- xdrawglyphfontspecs(specs, base, i, ox, y1);
-- specs += i;
-- numspecs -= i;
-+ if ((i > 0) && ATTRCMP(base, new)) {
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - …
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x …
- i = 0;
- }
- if (i == 0) {
-@@ -1672,8 +1722,10 @@ xdrawline(Line line, int x1, int y1, int x2)
- }
- i++;
- }
-- if (i > 0)
-- xdrawglyphfontspecs(specs, base, i, ox, y1);
-+ if (i > 0) {
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,…
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox);
-+ }
- }
-
- void
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-20241226-0.9.…
@@ -0,0 +1,650 @@
+diff --git a/Makefile b/Makefile
+index 15db421..dfcea0f 100644
+--- a/Makefile
++++ b/Makefile
+@@ -3,9 +3,9 @@
+ .POSIX:
+
+ include config.mk
+
+-SRC = st.c x.c
++SRC = st.c x.c hb.c
+ OBJ = $(SRC:.c=.o)
+
+ all: st
+
+@@ -15,9 +15,10 @@ config.h:
+ .c.o:
+ $(CC) $(STCFLAGS) -c $<
+
+ st.o: config.h st.h win.h
+-x.o: arg.h config.h st.h win.h
++x.o: arg.h config.h st.h win.h hb.h
++hb.o: st.h
+
+ $(OBJ): config.h config.mk
+
+ st: $(OBJ)
+diff --git a/config.mk b/config.mk
+index fdc29a7..6833b3b 100644
+--- a/config.mk
++++ b/config.mk
+@@ -14,12 +14,14 @@ PKG_CONFIG = pkg-config
+
+ # includes and libs
+ INCS = -I$(X11INC) \
+ `$(PKG_CONFIG) --cflags fontconfig` \
+- `$(PKG_CONFIG) --cflags freetype2`
++ `$(PKG_CONFIG) --cflags freetype2` \
++ `$(PKG_CONFIG) --cflags harfbuzz`
+ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \
+ `$(PKG_CONFIG) --libs fontconfig` \
+- `$(PKG_CONFIG) --libs freetype2`
++ `$(PKG_CONFIG) --libs freetype2` \
++ `$(PKG_CONFIG) --libs harfbuzz`
+
+ # flags
+ STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
+ STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS)
+@@ -28,9 +30,10 @@ STLDFLAGS = $(LIBS) $(LDFLAGS)
+ # OpenBSD:
+ #CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE
+ #LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \
+ # `$(PKG_CONFIG) --libs fontconfig` \
+-# `$(PKG_CONFIG) --libs freetype2`
++# `$(PKG_CONFIG) --libs freetype2` \
++# `$(PKG_CONFIG) --libs harfbuzz`
+ #MANPREFIX = ${PREFIX}/man
+
+ # compiler and linker
+ # CC = c99
+diff --git a/hb.c b/hb.c
+new file mode 100644
+index 0000000..99412c8
+--- /dev/null
++++ b/hb.c
+@@ -0,0 +1,125 @@
++#include <stdlib.h>
++#include <stdio.h>
++#include <math.h>
++#include <X11/Xft/Xft.h>
++#include <X11/cursorfont.h>
++#include <hb.h>
++#include <hb-ft.h>
++
++#include "st.h"
++#include "hb.h"
++
++#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start…
++#define BUFFER_STEP 256
++
++hb_font_t *hbfindfont(XftFont *match);
++
++typedef struct {
++ XftFont *match;
++ hb_font_t *font;
++} HbFontMatch;
++
++typedef struct {
++ size_t capacity;
++ HbFontMatch *fonts;
++} HbFontCache;
++
++static HbFontCache hbfontcache = { 0, NULL };
++
++typedef struct {
++ size_t capacity;
++ Rune *runes;
++} RuneBuffer;
++
++static RuneBuffer hbrunebuffer = { 0, NULL };
++
++/*
++ * Poplulate the array with a list of font features, wrapped in FEATURE macro,
++ * e. g.
++ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g')
++ */
++hb_feature_t features[] = { };
++
++void
++hbunloadfonts()
++{
++ for (int i = 0; i < hbfontcache.capacity; i++) {
++ hb_font_destroy(hbfontcache.fonts[i].font);
++ XftUnlockFace(hbfontcache.fonts[i].match);
++ }
++
++ if (hbfontcache.fonts != NULL) {
++ free(hbfontcache.fonts);
++ hbfontcache.fonts = NULL;
++ }
++ hbfontcache.capacity = 0;
++}
++
++hb_font_t *
++hbfindfont(XftFont *match)
++{
++ for (int i = 0; i < hbfontcache.capacity; i++) {
++ if (hbfontcache.fonts[i].match == match)
++ return hbfontcache.fonts[i].font;
++ }
++
++ /* Font not found in cache, caching it now. */
++ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * …
++ FT_Face face = XftLockFace(match);
++ hb_font_t *font = hb_ft_font_create(face, NULL);
++ if (font == NULL)
++ die("Failed to load Harfbuzz font.");
++
++ hbfontcache.fonts[hbfontcache.capacity].match = match;
++ hbfontcache.fonts[hbfontcache.capacity].font = font;
++ hbfontcache.capacity += 1;
++
++ return font;
++}
++
++void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, …
++ ushort mode = USHRT_MAX;
++ unsigned int glyph_count;
++ int rune_idx, glyph_idx, end = start + length;
++
++ hb_font_t *font = hbfindfont(xfont);
++ if (font == NULL)
++ return;
++
++ hb_buffer_t *buffer = hb_buffer_create();
++ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
++ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_…
++
++ /* Resize the buffer if required length is larger. */
++ if (hbrunebuffer.capacity < length) {
++ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S…
++ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer…
++ }
++
++ /* Fill buffer with codepoints. */
++ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r…
++ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u;
++ mode = glyphs[glyph_idx].mode;
++ if (mode & ATTR_WDUMMY)
++ hbrunebuffer.runes[rune_idx] = 0x0020;
++ }
++ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt…
++
++ /* Shape the segment. */
++ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t…
++
++ /* Get new glyph info. */
++ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun…
++ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly…
++
++ /* Fill the output. */
++ data->buffer = buffer;
++ data->glyphs = info;
++ data->positions = pos;
++ data->count = glyph_count;
++}
++
++void hbcleanup(HbTransformData *data) {
++ hb_buffer_destroy(data->buffer);
++ memset(data, 0, sizeof(HbTransformData));
++}
+diff --git a/hb.h b/hb.h
+new file mode 100644
+index 0000000..3b0ef44
+--- /dev/null
++++ b/hb.h
+@@ -0,0 +1,14 @@
++#include <X11/Xft/Xft.h>
++#include <hb.h>
++#include <hb-ft.h>
++
++typedef struct {
++ hb_buffer_t *buffer;
++ hb_glyph_info_t *glyphs;
++ hb_glyph_position_t *positions;
++ unsigned int count;
++} HbTransformData;
++
++void hbunloadfonts();
++void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
++void hbcleanup(HbTransformData *);
+diff --git a/st.c b/st.c
+index b9f66e7..da33a85 100644
+--- a/st.c
++++ b/st.c
+@@ -2658,9 +2658,10 @@ draw(void)
+ cx--;
+
+ drawregion(0, 0, term.col, term.row);
+ xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
+- term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
++ term.ocx, term.ocy, term.line[term.ocy][term.ocx],
++ term.line[term.ocy], term.col);
+ term.ocx = cx;
+ term.ocy = term.c.y;
+ xfinishdraw();
+ if (ocx != term.ocx || ocy != term.ocy)
+diff --git a/st.h b/st.h
+index fd3b0d8..142fdfe 100644
+--- a/st.h
++++ b/st.h
+@@ -10,9 +10,10 @@
+ #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
+ #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
+ #define DEFAULT(a, b) (a) = (a) ? (a) : (b)
+ #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)…
+-#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f…
++#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode …
++ (a).fg != (b).fg || \
+ (a).bg != (b).bg)
+ #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
+ (t1.tv_nsec-t2.tv_nsec)/1E6)
+ #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
+diff --git a/win.h b/win.h
+index 6de960d..94679e4 100644
+--- a/win.h
++++ b/win.h
+@@ -24,9 +24,9 @@ enum win_mode {
+ };
+
+ void xbell(void);
+ void xclipcopy(void);
+-void xdrawcursor(int, int, Glyph, int, int, Glyph);
++void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int);
+ void xdrawline(Line, int, int, int);
+ void xfinishdraw(void);
+ void xloadcols(void);
+ int xsetcolorname(int, const char *);
+diff --git a/x.c b/x.c
+index bd23686..2bf3b72 100644
+--- a/x.c
++++ b/x.c
+@@ -18,8 +18,9 @@
+ char *argv0;
+ #include "arg.h"
+ #include "st.h"
+ #include "win.h"
++#include "hb.h"
+
+ /* types used in config.h */
+ typedef struct {
+ uint mod;
+@@ -140,10 +141,11 @@ typedef struct {
+ GC gc;
+ } DC;
+
+ static inline ushort sixd_to_16bit(int);
++static void xresetfontsettings(ushort mode, Font **font, int *frcflags);
+ static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i…
+-static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
++static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
+ static void xdrawglyph(Glyph, int, int);
+ static void xclear(int, int, int, int);
+ static int xgeommasktogravity(int);
+ static int ximopen(Display *);
+@@ -756,9 +758,9 @@ xresize(int col, int row)
+ XftDrawChange(xw.draw, xw.buf);
+ xclear(0, 0, win.w, win.h);
+
+ /* resize to new width */
+- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
++ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
+ }
+
+ ushort
+ sixd_to_16bit(int x)
+@@ -1061,8 +1063,11 @@ xunloadfont(Font *f)
+
+ void
+ xunloadfonts(void)
+ {
++ /* Clear Harfbuzz font cache. */
++ hbunloadfonts();
++
+ /* Free the loaded fonts in the font cache. */
+ while (frclen > 0)
+ XftFontClose(xw.dpy, frc[--frclen].font);
+
+@@ -1184,9 +1189,9 @@ xinit(int cols, int rows)
+ XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel);
+ XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
+
+ /* font spec buffer */
+- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
++ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
+
+ /* Xft rendering context */
+ xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
+
+@@ -1238,144 +1243,155 @@ xinit(int cols, int rows)
+ if (xsel.xtarget == None)
+ xsel.xtarget = XA_STRING;
+ }
+
++void
++xresetfontsettings(ushort mode, Font **font, int *frcflags)
++{
++ *font = &dc.font;
++ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
++ *font = &dc.ibfont;
++ *frcflags = FRC_ITALICBOLD;
++ } else if (mode & ATTR_ITALIC) {
++ *font = &dc.ifont;
++ *frcflags = FRC_ITALIC;
++ } else if (mode & ATTR_BOLD) {
++ *font = &dc.bfont;
++ *frcflags = FRC_BOLD;
++ }
++}
++
+ int
+ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in…
+ {
+ float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp,…
+- ushort mode, prevmode = USHRT_MAX;
++ ushort mode = glyphs[0].mode & ~ATTR_WRAP;
+ Font *font = &dc.font;
+ int frcflags = FRC_NORMAL;
+- float runewidth = win.cw;
++ float runewidth = win.cw * ((glyphs[0].mode & ATTR_WIDE) ? 2.0f : 1.0…
+ Rune rune;
+ FT_UInt glyphidx;
+ FcResult fcres;
+ FcPattern *fcpattern, *fontpattern;
+ FcFontSet *fcsets[] = { NULL };
+ FcCharSet *fccharset;
+- int i, f, numspecs = 0;
++ int f, code_idx, numspecs = 0;
++ float cluster_xp = xp, cluster_yp = yp;
++ HbTransformData shaped = { 0 };
+
+- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
+- /* Fetch rune and mode for current glyph. */
+- rune = glyphs[i].u;
+- mode = glyphs[i].mode;
++ /* Initial values. */
++ xresetfontsettings(mode, &font, &frcflags);
+
+- /* Skip dummy wide-character spacing. */
+- if (mode == ATTR_WDUMMY)
++ /* Shape the segment. */
++ hbtransform(&shaped, font->match, glyphs, 0, len);
++ xp = winx; yp = winy + font->ascent;
++ cluster_xp = xp; cluster_yp = yp;
++
++ for (code_idx = 0; code_idx < shaped.count; code_idx++) {
++ int idx = shaped.glyphs[code_idx].cluster;
++
++ if (glyphs[idx].mode & ATTR_WDUMMY)
+ continue;
+
+- /* Determine font for glyph if different from previous glyph.…
+- if (prevmode != mode) {
+- prevmode = mode;
+- font = &dc.font;
+- frcflags = FRC_NORMAL;
+- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0…
+- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
+- font = &dc.ibfont;
+- frcflags = FRC_ITALICBOLD;
+- } else if (mode & ATTR_ITALIC) {
+- font = &dc.ifont;
+- frcflags = FRC_ITALIC;
+- } else if (mode & ATTR_BOLD) {
+- font = &dc.bfont;
+- frcflags = FRC_BOLD;
+- }
+- yp = winy + font->ascent;
++ /* Advance the drawing cursor if we've moved to a new cluster…
++ if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluste…
++ xp += runewidth;
++ cluster_xp = xp;
++ cluster_yp = yp;
+ }
+
+- /* Lookup character index with default font. */
+- glyphidx = XftCharIndex(xw.dpy, font->match, rune);
+- if (glyphidx) {
++ if (shaped.glyphs[code_idx].codepoint != 0) {
++ /* If symbol is found, put it into the specs. */
+ specs[numspecs].font = font->match;
+- specs[numspecs].glyph = glyphidx;
+- specs[numspecs].x = (short)xp;
+- specs[numspecs].y = (short)yp;
+- xp += runewidth;
++ specs[numspecs].glyph = shaped.glyphs[code_idx].codep…
++ specs[numspecs].x = cluster_xp + (short)(shaped.posit…
++ specs[numspecs].y = cluster_yp - (short)(shaped.posit…
++ cluster_xp += shaped.positions[code_idx].x_advance / …
++ cluster_yp += shaped.positions[code_idx].y_advance / …
+ numspecs++;
+- continue;
+- }
+-
+- /* Fallback on font cache, search the font cache for match. */
+- for (f = 0; f < frclen; f++) {
+- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
+- /* Everything correct. */
+- if (glyphidx && frc[f].flags == frcflags)
+- break;
+- /* We got a default font for a not found glyph. */
+- if (!glyphidx && frc[f].flags == frcflags
+- && frc[f].unicodep == rune) {
+- break;
++ } else {
++ /* If it's not found, try to fetch it through the fon…
++ rune = glyphs[idx].u;
++ for (f = 0; f < frclen; f++) {
++ glyphidx = XftCharIndex(xw.dpy, frc[f].font, …
++ /* Everything correct. */
++ if (glyphidx && frc[f].flags == frcflags)
++ break;
++ /* We got a default font for a not found glyp…
++ if (!glyphidx && frc[f].flags == frcflags
++ && frc[f].unicodep == rune) {
++ break;
++ }
+ }
+- }
+
+- /* Nothing was found. Use fontconfig to find matching font. */
+- if (f >= frclen) {
+- if (!font->set)
+- font->set = FcFontSort(0, font->pattern,
+- 1, 0, &fcres);
+- fcsets[0] = font->set;
+-
+- /*
+- * Nothing was found in the cache. Now use
+- * some dozen of Fontconfig calls to get the
+- * font for one single character.
+- *
+- * Xft and fontconfig are design failures.
+- */
+- fcpattern = FcPatternDuplicate(font->pattern);
+- fccharset = FcCharSetCreate();
+-
+- FcCharSetAddChar(fccharset, rune);
+- FcPatternAddCharSet(fcpattern, FC_CHARSET,
+- fccharset);
+- FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
+-
+- FcConfigSubstitute(0, fcpattern,
+- FcMatchPattern);
+- FcDefaultSubstitute(fcpattern);
+-
+- fontpattern = FcFontSetMatch(0, fcsets, 1,
+- fcpattern, &fcres);
+-
+- /* Allocate memory for the new cache entry. */
+- if (frclen >= frccap) {
+- frccap += 16;
+- frc = xrealloc(frc, frccap * sizeof(Fontcache…
++ /* Nothing was found. Use fontconfig to find matching…
++ if (f >= frclen) {
++ if (!font->set)
++ font->set = FcFontSort(0, font->patte…
++ 1, 0, &fcres);
++ fcsets[0] = font->set;
++
++ /*
++ * Nothing was found in the cache. Now use
++ * some dozen of Fontconfig calls to get the
++ * font for one single character.
++ *
++ * Xft and fontconfig are design failures.
++ */
++ fcpattern = FcPatternDuplicate(font->pattern);
++ fccharset = FcCharSetCreate();
++
++ FcCharSetAddChar(fccharset, rune);
++ FcPatternAddCharSet(fcpattern, FC_CHARSET,
++ fccharset);
++ FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
++
++ FcConfigSubstitute(0, fcpattern,
++ FcMatchPattern);
++ FcDefaultSubstitute(fcpattern);
++
++ fontpattern = FcFontSetMatch(0, fcsets, 1,
++ fcpattern, &fcres);
++
++ /* Allocate memory for the new cache entry. */
++ if (frclen >= frccap) {
++ frccap += 16;
++ frc = xrealloc(frc, frccap * sizeof(F…
++ }
++
++ frc[frclen].font = XftFontOpenPattern(xw.dpy,
++ fontpattern);
++ if (!frc[frclen].font)
++ die("XftFontOpenPattern failed seekin…
++ strerror(errno));
++ frc[frclen].flags = frcflags;
++ frc[frclen].unicodep = rune;
++
++ glyphidx = XftCharIndex(xw.dpy, frc[frclen].f…
++
++ f = frclen;
++ frclen++;
++
++ FcPatternDestroy(fcpattern);
++ FcCharSetDestroy(fccharset);
+ }
+
+- frc[frclen].font = XftFontOpenPattern(xw.dpy,
+- fontpattern);
+- if (!frc[frclen].font)
+- die("XftFontOpenPattern failed seeking fallba…
+- strerror(errno));
+- frc[frclen].flags = frcflags;
+- frc[frclen].unicodep = rune;
+-
+- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run…
+-
+- f = frclen;
+- frclen++;
+-
+- FcPatternDestroy(fcpattern);
+- FcCharSetDestroy(fccharset);
++ specs[numspecs].font = frc[f].font;
++ specs[numspecs].glyph = glyphidx;
++ specs[numspecs].x = (short)xp;
++ specs[numspecs].y = (short)yp;
++ numspecs++;
+ }
+-
+- specs[numspecs].font = frc[f].font;
+- specs[numspecs].glyph = glyphidx;
+- specs[numspecs].x = (short)xp;
+- specs[numspecs].y = (short)yp;
+- xp += runewidth;
+- numspecs++;
+ }
+
++ /* Cleanup and get ready for next segment. */
++ hbcleanup(&shaped);
+ return numspecs;
+ }
+
+ void
+-xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
++xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
+ {
+- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
+ int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
+ width = charlen * win.cw;
+ Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
+ XRenderColor colfg, colbg;
+@@ -1509,23 +1525,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Gly…
+ void
+ xdrawglyph(Glyph g, int x, int y)
+ {
+ int numspecs;
+- XftGlyphFontSpec spec;
++ XftGlyphFontSpec *specs = xw.specbuf;
+
+- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
+- xdrawglyphfontspecs(&spec, g, numspecs, x, y);
++ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y);
++ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? …
+ }
+
+ void
+-xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
++xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int…
+ {
+ Color drawcol;
+
+ /* remove the old cursor */
+ if (selected(ox, oy))
+ og.mode ^= ATTR_REVERSE;
+- xdrawglyph(og, ox, oy);
++
++ /* Redraw the line where cursor was previously.
++ * It will restore the ligatures broken by the cursor. */
++ xdrawline(line, 0, oy, len);
+
+ if (IS_SET(MODE_HIDE))
+ return;
+
+@@ -1657,30 +1676,30 @@ xdrawline(Line line, int x1, int y1, int x2)
+ int i, x, ox, numspecs;
+ Glyph base, new;
+ XftGlyphFontSpec *specs = xw.specbuf;
+
+- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
+ i = ox = 0;
+- for (x = x1; x < x2 && i < numspecs; x++) {
++ for (x = x1; x < x2; x++) {
+ new = line[x];
+ if (new.mode == ATTR_WDUMMY)
+ continue;
+ if (selected(x, y1))
+ new.mode ^= ATTR_REVERSE;
+- if (i > 0 && ATTRCMP(base, new)) {
+- xdrawglyphfontspecs(specs, base, i, ox, y1);
+- specs += i;
+- numspecs -= i;
++ if ((i > 0) && ATTRCMP(base, new)) {
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - …
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x …
+ i = 0;
+ }
+ if (i == 0) {
+ ox = x;
+ base = new;
+ }
+ i++;
+ }
+- if (i > 0)
+- xdrawglyphfontspecs(specs, base, i, ox, y1);
++ if (i > 0) {
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,…
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox);
++ }
+ }
+
+ void
+ xfinishdraw(void)
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-2024042…
@@ -1,635 +0,0 @@
-diff --git a/Makefile b/Makefile
-index 470ac86..38240da 100644
---- a/Makefile
-+++ b/Makefile
-@@ -4,7 +4,7 @@
-
- include config.mk
-
--SRC = st.c x.c
-+SRC = st.c x.c hb.c
- OBJ = $(SRC:.c=.o)
-
- all: st
-@@ -22,7 +22,8 @@ config.h:
- $(CC) $(STCFLAGS) -c $<
-
- st.o: config.h st.h win.h
--x.o: arg.h config.h st.h win.h
-+x.o: arg.h config.h st.h win.h hb.h
-+hb.o: st.h
-
- $(OBJ): config.h config.mk
-
-diff --git a/config.mk b/config.mk
-index 47c615e..d7439a3 100644
---- a/config.mk
-+++ b/config.mk
-@@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config
- # includes and libs
- INCS = -I$(X11INC) \
- `$(PKG_CONFIG) --cflags fontconfig` \
-- `$(PKG_CONFIG) --cflags freetype2`
--LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\
-+ `$(PKG_CONFIG) --cflags freetype2` \
-+ `$(PKG_CONFIG) --cflags harfbuzz`
-+LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender \
- `$(PKG_CONFIG) --libs fontconfig` \
-- `$(PKG_CONFIG) --libs freetype2`
-+ `$(PKG_CONFIG) --libs freetype2` \
-+ `$(PKG_CONFIG) --libs harfbuzz`
-
- # flags
- STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
-diff --git a/hb.c b/hb.c
-new file mode 100644
-index 0000000..58d534b
---- /dev/null
-+++ b/hb.c
-@@ -0,0 +1,125 @@
-+#include <stdlib.h>
-+#include <stdio.h>
-+#include <math.h>
-+#include <X11/Xft/Xft.h>
-+#include <X11/cursorfont.h>
-+#include <hb.h>
-+#include <hb-ft.h>
-+
-+#include "st.h"
-+#include "hb.h"
-+
-+#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start…
-+#define BUFFER_STEP 256
-+
-+hb_font_t *hbfindfont(XftFont *match);
-+
-+typedef struct {
-+ XftFont *match;
-+ hb_font_t *font;
-+} HbFontMatch;
-+
-+typedef struct {
-+ size_t capacity;
-+ HbFontMatch *fonts;
-+} HbFontCache;
-+
-+static HbFontCache hbfontcache = { 0, NULL };
-+
-+typedef struct {
-+ size_t capacity;
-+ Rune *runes;
-+} RuneBuffer;
-+
-+static RuneBuffer hbrunebuffer = { 0, NULL };
-+
-+/*
-+ * Poplulate the array with a list of font features, wrapped in FEATURE macro,
-+ * e. g.
-+ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g')
-+ */
-+hb_feature_t features[] = { };
-+
-+void
-+hbunloadfonts()
-+{
-+ for (int i = 0; i < hbfontcache.capacity; i++) {
-+ hb_font_destroy(hbfontcache.fonts[i].font);
-+ XftUnlockFace(hbfontcache.fonts[i].match);
-+ }
-+
-+ if (hbfontcache.fonts != NULL) {
-+ free(hbfontcache.fonts);
-+ hbfontcache.fonts = NULL;
-+ }
-+ hbfontcache.capacity = 0;
-+}
-+
-+hb_font_t *
-+hbfindfont(XftFont *match)
-+{
-+ for (int i = 0; i < hbfontcache.capacity; i++) {
-+ if (hbfontcache.fonts[i].match == match)
-+ return hbfontcache.fonts[i].font;
-+ }
-+
-+ /* Font not found in cache, caching it now. */
-+ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * …
-+ FT_Face face = XftLockFace(match);
-+ hb_font_t *font = hb_ft_font_create(face, NULL);
-+ if (font == NULL)
-+ die("Failed to load Harfbuzz font.");
-+
-+ hbfontcache.fonts[hbfontcache.capacity].match = match;
-+ hbfontcache.fonts[hbfontcache.capacity].font = font;
-+ hbfontcache.capacity += 1;
-+
-+ return font;
-+}
-+
-+void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, …
-+ ushort mode = USHRT_MAX;
-+ unsigned int glyph_count;
-+ int rune_idx, glyph_idx, end = start + length;
-+
-+ hb_font_t *font = hbfindfont(xfont);
-+ if (font == NULL)
-+ return;
-+
-+ hb_buffer_t *buffer = hb_buffer_create();
-+ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
-+ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_…
-+
-+ /* Resize the buffer if required length is larger. */
-+ if (hbrunebuffer.capacity < length) {
-+ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S…
-+ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer…
-+ }
-+
-+ /* Fill buffer with codepoints. */
-+ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r…
-+ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u;
-+ mode = glyphs[glyph_idx].mode;
-+ if (mode & ATTR_WDUMMY)
-+ hbrunebuffer.runes[rune_idx] = 0x0020;
-+ }
-+ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt…
-+
-+ /* Shape the segment. */
-+ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t…
-+
-+ /* Get new glyph info. */
-+ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun…
-+ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly…
-+
-+ /* Fill the output. */
-+ data->buffer = buffer;
-+ data->glyphs = info;
-+ data->positions = pos;
-+ data->count = glyph_count;
-+}
-+
-+void hbcleanup(HbTransformData *data) {
-+ hb_buffer_destroy(data->buffer);
-+ memset(data, 0, sizeof(HbTransformData));
-+}
-diff --git a/hb.h b/hb.h
-new file mode 100644
-index 0000000..3b0ef44
---- /dev/null
-+++ b/hb.h
-@@ -0,0 +1,14 @@
-+#include <X11/Xft/Xft.h>
-+#include <hb.h>
-+#include <hb-ft.h>
-+
-+typedef struct {
-+ hb_buffer_t *buffer;
-+ hb_glyph_info_t *glyphs;
-+ hb_glyph_position_t *positions;
-+ unsigned int count;
-+} HbTransformData;
-+
-+void hbunloadfonts();
-+void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
-+void hbcleanup(HbTransformData *);
-diff --git a/st.c b/st.c
-index 62def59..041c6d8 100644
---- a/st.c
-+++ b/st.c
-@@ -2640,7 +2640,8 @@ draw(void)
-
- drawregion(0, 0, term.col, term.row);
- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
-- term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
-+ term.ocx, term.ocy, term.line[term.ocy][term.ocx],
-+ term.line[term.ocy], term.col);
- term.ocx = cx;
- term.ocy = term.c.y;
- xfinishdraw();
-diff --git a/st.h b/st.h
-index 9f91e2a..b1a6256 100644
---- a/st.h
-+++ b/st.h
-@@ -11,7 +11,8 @@
- #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
- #define DEFAULT(a, b) (a) = (a) ? (a) : (b)
- #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)…
--#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f…
-+#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode …
-+ (a).fg != (b).fg || \
- (a).bg != (b).bg)
- #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
- (t1.tv_nsec-t2.tv_nsec)/1E6)
-diff --git a/win.h b/win.h
-index 6de960d..94679e4 100644
---- a/win.h
-+++ b/win.h
-@@ -25,7 +25,7 @@ enum win_mode {
-
- void xbell(void);
- void xclipcopy(void);
--void xdrawcursor(int, int, Glyph, int, int, Glyph);
-+void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int);
- void xdrawline(Line, int, int, int);
- void xfinishdraw(void);
- void xloadcols(void);
-diff --git a/x.c b/x.c
-index 27e81d1..0632636 100644
---- a/x.c
-+++ b/x.c
-@@ -19,6 +19,7 @@ char *argv0;
- #include "arg.h"
- #include "st.h"
- #include "win.h"
-+#include "hb.h"
-
- /* types used in config.h */
- typedef struct {
-@@ -142,8 +143,9 @@ typedef struct {
- } DC;
-
- static inline ushort sixd_to_16bit(int);
-+static void xresetfontsettings(ushort mode, Font **font, int *frcflags);
- static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i…
--static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
-+static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
- static void xdrawglyph(Glyph, int, int);
- static void xclear(int, int, int, int);
- static int xgeommasktogravity(int);
-@@ -759,7 +761,7 @@ xresize(int col, int row)
- xclear(0, 0, win.w, win.h);
-
- /* resize to new width */
-- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
-+ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
- }
-
- ushort
-@@ -1071,6 +1073,9 @@ xunloadfont(Font *f)
- void
- xunloadfonts(void)
- {
-+ /* Clear Harfbuzz font cache. */
-+ hbunloadfonts();
-+
- /* Free the loaded fonts in the font cache. */
- while (frclen > 0)
- XftFontClose(xw.dpy, frc[--frclen].font);
-@@ -1202,7 +1207,7 @@ xinit(int cols, int rows)
- XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
-
- /* font spec buffer */
-- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
-+ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
-
- /* Xft rendering context */
- xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
-@@ -1256,6 +1261,22 @@ xinit(int cols, int rows)
- xsel.xtarget = XA_STRING;
- }
-
-+void
-+xresetfontsettings(ushort mode, Font **font, int *frcflags)
-+{
-+ *font = &dc.font;
-+ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
-+ *font = &dc.ibfont;
-+ *frcflags = FRC_ITALICBOLD;
-+ } else if (mode & ATTR_ITALIC) {
-+ *font = &dc.ifont;
-+ *frcflags = FRC_ITALIC;
-+ } else if (mode & ATTR_BOLD) {
-+ *font = &dc.bfont;
-+ *frcflags = FRC_BOLD;
-+ }
-+}
-+
- int
- xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in…
- {
-@@ -1270,128 +1291,157 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const G…
- FcPattern *fcpattern, *fontpattern;
- FcFontSet *fcsets[] = { NULL };
- FcCharSet *fccharset;
-- int i, f, numspecs = 0;
-+ int i, f, length = 0, start = 0, numspecs = 0;
-+ float cluster_xp = xp, cluster_yp = yp;
-+ HbTransformData shaped = { 0 };
-+
-+ /* Initial values. */
-+ mode = prevmode = glyphs[0].mode & ~ATTR_WRAP;
-+ xresetfontsettings(mode, &font, &frcflags);
-
- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
-- /* Fetch rune and mode for current glyph. */
-- rune = glyphs[i].u;
-- mode = glyphs[i].mode;
-+ mode = glyphs[i].mode & ~ATTR_WRAP;
-
- /* Skip dummy wide-character spacing. */
-- if (mode == ATTR_WDUMMY)
-+ if (mode & ATTR_WDUMMY && i < (len - 1))
- continue;
-
-- /* Determine font for glyph if different from previous glyph.…
-- if (prevmode != mode) {
-- prevmode = mode;
-- font = &dc.font;
-- frcflags = FRC_NORMAL;
-- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0…
-- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
-- font = &dc.ibfont;
-- frcflags = FRC_ITALICBOLD;
-- } else if (mode & ATTR_ITALIC) {
-- font = &dc.ifont;
-- frcflags = FRC_ITALIC;
-- } else if (mode & ATTR_BOLD) {
-- font = &dc.bfont;
-- frcflags = FRC_BOLD;
-+ if (
-+ prevmode != mode
-+ || ATTRCMP(glyphs[start], glyphs[i])
-+ || selected(x + i, y) != selected(x + start, y)
-+ || i == (len - 1)
-+ ) {
-+ /* Handle 1-character wide segments and end of line */
-+ length = i - start;
-+ if (i == start) {
-+ length = 1;
-+ } else if (i == (len - 1)) {
-+ length = (i - start + 1);
- }
-- yp = winy + font->ascent;
-- }
-
-- /* Lookup character index with default font. */
-- glyphidx = XftCharIndex(xw.dpy, font->match, rune);
-- if (glyphidx) {
-- specs[numspecs].font = font->match;
-- specs[numspecs].glyph = glyphidx;
-- specs[numspecs].x = (short)xp;
-- specs[numspecs].y = (short)yp;
-- xp += runewidth;
-- numspecs++;
-- continue;
-- }
--
-- /* Fallback on font cache, search the font cache for match. */
-- for (f = 0; f < frclen; f++) {
-- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
-- /* Everything correct. */
-- if (glyphidx && frc[f].flags == frcflags)
-- break;
-- /* We got a default font for a not found glyph. */
-- if (!glyphidx && frc[f].flags == frcflags
-- && frc[f].unicodep == rune) {
-- break;
-+ /* Shape the segment. */
-+ hbtransform(&shaped, font->match, glyphs, start, leng…
-+ runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE…
-+ cluster_xp = xp; cluster_yp = yp;
-+ for (int code_idx = 0; code_idx < shaped.count; code_…
-+ int idx = shaped.glyphs[code_idx].cluster;
-+
-+ if (glyphs[start + idx].mode & ATTR_WDUMMY)
-+ continue;
-+
-+ /* Advance the drawing cursor if we've moved …
-+ if (code_idx > 0 && idx != shaped.glyphs[code…
-+ xp += runewidth;
-+ cluster_xp = xp;
-+ cluster_yp = yp;
-+ runewidth = win.cw * ((glyphs[start +…
-+ }
-+
-+ if (shaped.glyphs[code_idx].codepoint != 0) {
-+ /* If symbol is found, put it into th…
-+ specs[numspecs].font = font->match;
-+ specs[numspecs].glyph = shaped.glyphs…
-+ specs[numspecs].x = cluster_xp + (sho…
-+ specs[numspecs].y = cluster_yp - (sho…
-+ cluster_xp += shaped.positions[code_i…
-+ cluster_yp += shaped.positions[code_i…
-+ numspecs++;
-+ } else {
-+ /* If it's not found, try to fetch it…
-+ rune = glyphs[start + idx].u;
-+ for (f = 0; f < frclen; f++) {
-+ glyphidx = XftCharIndex(xw.dp…
-+ /* Everything correct. */
-+ if (glyphidx && frc[f].flags …
-+ break;
-+ /* We got a default font for …
-+ if (!glyphidx && frc[f].flags…
-+ && frc[f].uni…
-+ break;
-+ }
-+ }
-+
-+ /* Nothing was found. Use fontconfig …
-+ if (f >= frclen) {
-+ if (!font->set)
-+ font->set = FcFontSor…
-+ …
-+ fcsets[0] = font->set;
-+
-+ /*
-+ * Nothing was found in the c…
-+ * some dozen of Fontconfig c…
-+ * font for one single charac…
-+ *
-+ * Xft and fontconfig are des…
-+ */
-+ fcpattern = FcPatternDuplicat…
-+ fccharset = FcCharSetCreate();
-+
-+ FcCharSetAddChar(fccharset, r…
-+ FcPatternAddCharSet(fcpattern…
-+ fccharset);
-+ FcPatternAddBool(fcpattern, F…
-+
-+ FcConfigSubstitute(0, fcpatte…
-+ FcMatchPatter…
-+ FcDefaultSubstitute(fcpattern…
-+
-+ fontpattern = FcFontSetMatch(…
-+ fcpattern, &f…
-+
-+ /* Allocate memory for the ne…
-+ if (frclen >= frccap) {
-+ frccap += 16;
-+ frc = xrealloc(frc, f…
-+ }
-+
-+ frc[frclen].font = XftFontOpe…
-+ fontpattern);
-+ if (!frc[frclen].font)
-+ die("XftFontOpenPatte…
-+ strerror(errn…
-+ frc[frclen].flags = frcflags;
-+ frc[frclen].unicodep = rune;
-+
-+ glyphidx = XftCharIndex(xw.dp…
-+
-+ f = frclen;
-+ frclen++;
-+
-+ FcPatternDestroy(fcpattern);
-+ FcCharSetDestroy(fccharset);
-+ }
-+
-+ specs[numspecs].font = frc[f].font;
-+ specs[numspecs].glyph = glyphidx;
-+ specs[numspecs].x = (short)xp;
-+ specs[numspecs].y = (short)yp;
-+ numspecs++;
-+ }
- }
-- }
-
-- /* Nothing was found. Use fontconfig to find matching font. */
-- if (f >= frclen) {
-- if (!font->set)
-- font->set = FcFontSort(0, font->pattern,
-- 1, 0, &fcres);
-- fcsets[0] = font->set;
-+ /* Cleanup and get ready for next segment. */
-+ hbcleanup(&shaped);
-+ start = i;
-
-- /*
-- * Nothing was found in the cache. Now use
-- * some dozen of Fontconfig calls to get the
-- * font for one single character.
-- *
-- * Xft and fontconfig are design failures.
-- */
-- fcpattern = FcPatternDuplicate(font->pattern);
-- fccharset = FcCharSetCreate();
--
-- FcCharSetAddChar(fccharset, rune);
-- FcPatternAddCharSet(fcpattern, FC_CHARSET,
-- fccharset);
-- FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
--
-- FcConfigSubstitute(0, fcpattern,
-- FcMatchPattern);
-- FcDefaultSubstitute(fcpattern);
--
-- fontpattern = FcFontSetMatch(0, fcsets, 1,
-- fcpattern, &fcres);
--
-- /* Allocate memory for the new cache entry. */
-- if (frclen >= frccap) {
-- frccap += 16;
-- frc = xrealloc(frc, frccap * sizeof(Fontcache…
-+ /* Determine font for glyph if different from previou…
-+ if (prevmode != mode) {
-+ prevmode = mode;
-+ xresetfontsettings(mode, &font, &frcflags);
-+ yp = winy + font->ascent;
- }
--
-- frc[frclen].font = XftFontOpenPattern(xw.dpy,
-- fontpattern);
-- if (!frc[frclen].font)
-- die("XftFontOpenPattern failed seeking fallba…
-- strerror(errno));
-- frc[frclen].flags = frcflags;
-- frc[frclen].unicodep = rune;
--
-- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run…
--
-- f = frclen;
-- frclen++;
--
-- FcPatternDestroy(fcpattern);
-- FcCharSetDestroy(fccharset);
- }
--
-- specs[numspecs].font = frc[f].font;
-- specs[numspecs].glyph = glyphidx;
-- specs[numspecs].x = (short)xp;
-- specs[numspecs].y = (short)yp;
-- xp += runewidth;
-- numspecs++;
- }
-
-+ hbcleanup(&shaped);
- return numspecs;
- }
-
- void
--xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
-+xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
- {
-- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
- int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
- width = charlen * win.cw;
- Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
-@@ -1527,21 +1577,24 @@ void
- xdrawglyph(Glyph g, int x, int y)
- {
- int numspecs;
-- XftGlyphFontSpec spec;
-+ XftGlyphFontSpec *specs = xw.specbuf;
-
-- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
-- xdrawglyphfontspecs(&spec, g, numspecs, x, y);
-+ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y);
-+ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? …
- }
-
- void
--xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
-+xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int…
- {
- Color drawcol;
-
- /* remove the old cursor */
- if (selected(ox, oy))
- og.mode ^= ATTR_REVERSE;
-- xdrawglyph(og, ox, oy);
-+
-+ /* Redraw the line where cursor was previously.
-+ * It will restore the ligatures broken by the cursor. */
-+ xdrawline(line, 0, oy, len);
-
- if (IS_SET(MODE_HIDE))
- return;
-@@ -1669,18 +1722,16 @@ xdrawline(Line line, int x1, int y1, int x2)
- Glyph base, new;
- XftGlyphFontSpec *specs = xw.specbuf;
-
-- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
- i = ox = 0;
-- for (x = x1; x < x2 && i < numspecs; x++) {
-+ for (x = x1; x < x2; x++) {
- new = line[x];
- if (new.mode == ATTR_WDUMMY)
- continue;
- if (selected(x, y1))
- new.mode ^= ATTR_REVERSE;
-- if (i > 0 && ATTRCMP(base, new)) {
-- xdrawglyphfontspecs(specs, base, i, ox, y1);
-- specs += i;
-- numspecs -= i;
-+ if ((i > 0) && ATTRCMP(base, new)) {
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - …
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x …
- i = 0;
- }
- if (i == 0) {
-@@ -1689,8 +1740,10 @@ xdrawline(Line line, int x1, int y1, int x2)
- }
- i++;
- }
-- if (i > 0)
-- xdrawglyphfontspecs(specs, base, i, ox, y1);
-+ if (i > 0) {
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,…
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox);
-+ }
- }
-
- void
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-2024122…
@@ -0,0 +1,650 @@
+diff --git a/Makefile b/Makefile
+index 15db421..dfcea0f 100644
+--- a/Makefile
++++ b/Makefile
+@@ -3,9 +3,9 @@
+ .POSIX:
+
+ include config.mk
+
+-SRC = st.c x.c
++SRC = st.c x.c hb.c
+ OBJ = $(SRC:.c=.o)
+
+ all: st
+
+@@ -15,9 +15,10 @@ config.h:
+ .c.o:
+ $(CC) $(STCFLAGS) -c $<
+
+ st.o: config.h st.h win.h
+-x.o: arg.h config.h st.h win.h
++x.o: arg.h config.h st.h win.h hb.h
++hb.o: st.h
+
+ $(OBJ): config.h config.mk
+
+ st: $(OBJ)
+diff --git a/config.mk b/config.mk
+index 069a6c2..977b7c7 100644
+--- a/config.mk
++++ b/config.mk
+@@ -14,12 +14,14 @@ PKG_CONFIG = pkg-config
+
+ # includes and libs
+ INCS = -I$(X11INC) \
+ `$(PKG_CONFIG) --cflags fontconfig` \
+- `$(PKG_CONFIG) --cflags freetype2`
++ `$(PKG_CONFIG) --cflags freetype2` \
++ `$(PKG_CONFIG) --cflags harfbuzz`
+ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\
+ `$(PKG_CONFIG) --libs fontconfig` \
+- `$(PKG_CONFIG) --libs freetype2`
++ `$(PKG_CONFIG) --libs freetype2` \
++ `$(PKG_CONFIG) --libs harfbuzz`
+
+ # flags
+ STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
+ STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS)
+@@ -28,9 +30,10 @@ STLDFLAGS = $(LIBS) $(LDFLAGS)
+ # OpenBSD:
+ #CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE
+ #LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \
+ # `$(PKG_CONFIG) --libs fontconfig` \
+-# `$(PKG_CONFIG) --libs freetype2`
++# `$(PKG_CONFIG) --libs freetype2` \
++# `$(PKG_CONFIG) --libs harfbuzz`
+ #MANPREFIX = ${PREFIX}/man
+
+ # compiler and linker
+ # CC = c99
+diff --git a/hb.c b/hb.c
+new file mode 100644
+index 0000000..99412c8
+--- /dev/null
++++ b/hb.c
+@@ -0,0 +1,125 @@
++#include <stdlib.h>
++#include <stdio.h>
++#include <math.h>
++#include <X11/Xft/Xft.h>
++#include <X11/cursorfont.h>
++#include <hb.h>
++#include <hb-ft.h>
++
++#include "st.h"
++#include "hb.h"
++
++#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start…
++#define BUFFER_STEP 256
++
++hb_font_t *hbfindfont(XftFont *match);
++
++typedef struct {
++ XftFont *match;
++ hb_font_t *font;
++} HbFontMatch;
++
++typedef struct {
++ size_t capacity;
++ HbFontMatch *fonts;
++} HbFontCache;
++
++static HbFontCache hbfontcache = { 0, NULL };
++
++typedef struct {
++ size_t capacity;
++ Rune *runes;
++} RuneBuffer;
++
++static RuneBuffer hbrunebuffer = { 0, NULL };
++
++/*
++ * Poplulate the array with a list of font features, wrapped in FEATURE macro,
++ * e. g.
++ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g')
++ */
++hb_feature_t features[] = { };
++
++void
++hbunloadfonts()
++{
++ for (int i = 0; i < hbfontcache.capacity; i++) {
++ hb_font_destroy(hbfontcache.fonts[i].font);
++ XftUnlockFace(hbfontcache.fonts[i].match);
++ }
++
++ if (hbfontcache.fonts != NULL) {
++ free(hbfontcache.fonts);
++ hbfontcache.fonts = NULL;
++ }
++ hbfontcache.capacity = 0;
++}
++
++hb_font_t *
++hbfindfont(XftFont *match)
++{
++ for (int i = 0; i < hbfontcache.capacity; i++) {
++ if (hbfontcache.fonts[i].match == match)
++ return hbfontcache.fonts[i].font;
++ }
++
++ /* Font not found in cache, caching it now. */
++ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * …
++ FT_Face face = XftLockFace(match);
++ hb_font_t *font = hb_ft_font_create(face, NULL);
++ if (font == NULL)
++ die("Failed to load Harfbuzz font.");
++
++ hbfontcache.fonts[hbfontcache.capacity].match = match;
++ hbfontcache.fonts[hbfontcache.capacity].font = font;
++ hbfontcache.capacity += 1;
++
++ return font;
++}
++
++void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, …
++ ushort mode = USHRT_MAX;
++ unsigned int glyph_count;
++ int rune_idx, glyph_idx, end = start + length;
++
++ hb_font_t *font = hbfindfont(xfont);
++ if (font == NULL)
++ return;
++
++ hb_buffer_t *buffer = hb_buffer_create();
++ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
++ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_…
++
++ /* Resize the buffer if required length is larger. */
++ if (hbrunebuffer.capacity < length) {
++ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S…
++ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer…
++ }
++
++ /* Fill buffer with codepoints. */
++ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r…
++ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u;
++ mode = glyphs[glyph_idx].mode;
++ if (mode & ATTR_WDUMMY)
++ hbrunebuffer.runes[rune_idx] = 0x0020;
++ }
++ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt…
++
++ /* Shape the segment. */
++ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t…
++
++ /* Get new glyph info. */
++ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun…
++ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly…
++
++ /* Fill the output. */
++ data->buffer = buffer;
++ data->glyphs = info;
++ data->positions = pos;
++ data->count = glyph_count;
++}
++
++void hbcleanup(HbTransformData *data) {
++ hb_buffer_destroy(data->buffer);
++ memset(data, 0, sizeof(HbTransformData));
++}
+diff --git a/hb.h b/hb.h
+new file mode 100644
+index 0000000..3b0ef44
+--- /dev/null
++++ b/hb.h
+@@ -0,0 +1,14 @@
++#include <X11/Xft/Xft.h>
++#include <hb.h>
++#include <hb-ft.h>
++
++typedef struct {
++ hb_buffer_t *buffer;
++ hb_glyph_info_t *glyphs;
++ hb_glyph_position_t *positions;
++ unsigned int count;
++} HbTransformData;
++
++void hbunloadfonts();
++void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
++void hbcleanup(HbTransformData *);
+diff --git a/st.c b/st.c
+index b9f66e7..da33a85 100644
+--- a/st.c
++++ b/st.c
+@@ -2658,9 +2658,10 @@ draw(void)
+ cx--;
+
+ drawregion(0, 0, term.col, term.row);
+ xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
+- term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
++ term.ocx, term.ocy, term.line[term.ocy][term.ocx],
++ term.line[term.ocy], term.col);
+ term.ocx = cx;
+ term.ocy = term.c.y;
+ xfinishdraw();
+ if (ocx != term.ocx || ocy != term.ocy)
+diff --git a/st.h b/st.h
+index 9f91e2a..b1a6256 100644
+--- a/st.h
++++ b/st.h
+@@ -10,9 +10,10 @@
+ #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
+ #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
+ #define DEFAULT(a, b) (a) = (a) ? (a) : (b)
+ #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)…
+-#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f…
++#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode …
++ (a).fg != (b).fg || \
+ (a).bg != (b).bg)
+ #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
+ (t1.tv_nsec-t2.tv_nsec)/1E6)
+ #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
+diff --git a/win.h b/win.h
+index 6de960d..94679e4 100644
+--- a/win.h
++++ b/win.h
+@@ -24,9 +24,9 @@ enum win_mode {
+ };
+
+ void xbell(void);
+ void xclipcopy(void);
+-void xdrawcursor(int, int, Glyph, int, int, Glyph);
++void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int);
+ void xdrawline(Line, int, int, int);
+ void xfinishdraw(void);
+ void xloadcols(void);
+ int xsetcolorname(int, const char *);
+diff --git a/x.c b/x.c
+index 1e12ac8..b0819ac 100644
+--- a/x.c
++++ b/x.c
+@@ -18,8 +18,9 @@
+ char *argv0;
+ #include "arg.h"
+ #include "st.h"
+ #include "win.h"
++#include "hb.h"
+
+ /* types used in config.h */
+ typedef struct {
+ uint mod;
+@@ -141,10 +142,11 @@ typedef struct {
+ GC gc;
+ } DC;
+
+ static inline ushort sixd_to_16bit(int);
++static void xresetfontsettings(ushort mode, Font **font, int *frcflags);
+ static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i…
+-static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
++static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
+ static void xdrawglyph(Glyph, int, int);
+ static void xclear(int, int, int, int);
+ static int xgeommasktogravity(int);
+ static int ximopen(Display *);
+@@ -758,9 +760,9 @@ xresize(int col, int row)
+ XftDrawChange(xw.draw, xw.buf);
+ xclear(0, 0, win.w, win.h);
+
+ /* resize to new width */
+- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
++ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
+ }
+
+ ushort
+ sixd_to_16bit(int x)
+@@ -1070,8 +1072,11 @@ xunloadfont(Font *f)
+
+ void
+ xunloadfonts(void)
+ {
++ /* Clear Harfbuzz font cache. */
++ hbunloadfonts();
++
+ /* Free the loaded fonts in the font cache. */
+ while (frclen > 0)
+ XftFontClose(xw.dpy, frc[--frclen].font);
+
+@@ -1201,9 +1206,9 @@ xinit(int cols, int rows)
+ XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel);
+ XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
+
+ /* font spec buffer */
+- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
++ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
+
+ /* Xft rendering context */
+ xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
+
+@@ -1255,144 +1260,155 @@ xinit(int cols, int rows)
+ if (xsel.xtarget == None)
+ xsel.xtarget = XA_STRING;
+ }
+
++void
++xresetfontsettings(ushort mode, Font **font, int *frcflags)
++{
++ *font = &dc.font;
++ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
++ *font = &dc.ibfont;
++ *frcflags = FRC_ITALICBOLD;
++ } else if (mode & ATTR_ITALIC) {
++ *font = &dc.ifont;
++ *frcflags = FRC_ITALIC;
++ } else if (mode & ATTR_BOLD) {
++ *font = &dc.bfont;
++ *frcflags = FRC_BOLD;
++ }
++}
++
+ int
+ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in…
+ {
+ float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp,…
+- ushort mode, prevmode = USHRT_MAX;
++ ushort mode = glyphs[0].mode & ~ATTR_WRAP;
+ Font *font = &dc.font;
+ int frcflags = FRC_NORMAL;
+- float runewidth = win.cw;
++ float runewidth = win.cw * ((glyphs[0].mode & ATTR_WIDE) ? 2.0f : 1.0…
+ Rune rune;
+ FT_UInt glyphidx;
+ FcResult fcres;
+ FcPattern *fcpattern, *fontpattern;
+ FcFontSet *fcsets[] = { NULL };
+ FcCharSet *fccharset;
+- int i, f, numspecs = 0;
++ int f, code_idx, numspecs = 0;
++ float cluster_xp = xp, cluster_yp = yp;
++ HbTransformData shaped = { 0 };
+
+- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
+- /* Fetch rune and mode for current glyph. */
+- rune = glyphs[i].u;
+- mode = glyphs[i].mode;
++ /* Initial values. */
++ xresetfontsettings(mode, &font, &frcflags);
+
+- /* Skip dummy wide-character spacing. */
+- if (mode == ATTR_WDUMMY)
++ /* Shape the segment. */
++ hbtransform(&shaped, font->match, glyphs, 0, len);
++ xp = winx; yp = winy + font->ascent;
++ cluster_xp = xp; cluster_yp = yp;
++
++ for (code_idx = 0; code_idx < shaped.count; code_idx++) {
++ int idx = shaped.glyphs[code_idx].cluster;
++
++ if (glyphs[idx].mode & ATTR_WDUMMY)
+ continue;
+
+- /* Determine font for glyph if different from previous glyph.…
+- if (prevmode != mode) {
+- prevmode = mode;
+- font = &dc.font;
+- frcflags = FRC_NORMAL;
+- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0…
+- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
+- font = &dc.ibfont;
+- frcflags = FRC_ITALICBOLD;
+- } else if (mode & ATTR_ITALIC) {
+- font = &dc.ifont;
+- frcflags = FRC_ITALIC;
+- } else if (mode & ATTR_BOLD) {
+- font = &dc.bfont;
+- frcflags = FRC_BOLD;
+- }
+- yp = winy + font->ascent;
++ /* Advance the drawing cursor if we've moved to a new cluster…
++ if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluste…
++ xp += runewidth;
++ cluster_xp = xp;
++ cluster_yp = yp;
+ }
+
+- /* Lookup character index with default font. */
+- glyphidx = XftCharIndex(xw.dpy, font->match, rune);
+- if (glyphidx) {
++ if (shaped.glyphs[code_idx].codepoint != 0) {
++ /* If symbol is found, put it into the specs. */
+ specs[numspecs].font = font->match;
+- specs[numspecs].glyph = glyphidx;
+- specs[numspecs].x = (short)xp;
+- specs[numspecs].y = (short)yp;
+- xp += runewidth;
++ specs[numspecs].glyph = shaped.glyphs[code_idx].codep…
++ specs[numspecs].x = cluster_xp + (short)(shaped.posit…
++ specs[numspecs].y = cluster_yp - (short)(shaped.posit…
++ cluster_xp += shaped.positions[code_idx].x_advance / …
++ cluster_yp += shaped.positions[code_idx].y_advance / …
+ numspecs++;
+- continue;
+- }
+-
+- /* Fallback on font cache, search the font cache for match. */
+- for (f = 0; f < frclen; f++) {
+- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
+- /* Everything correct. */
+- if (glyphidx && frc[f].flags == frcflags)
+- break;
+- /* We got a default font for a not found glyph. */
+- if (!glyphidx && frc[f].flags == frcflags
+- && frc[f].unicodep == rune) {
+- break;
++ } else {
++ /* If it's not found, try to fetch it through the fon…
++ rune = glyphs[idx].u;
++ for (f = 0; f < frclen; f++) {
++ glyphidx = XftCharIndex(xw.dpy, frc[f].font, …
++ /* Everything correct. */
++ if (glyphidx && frc[f].flags == frcflags)
++ break;
++ /* We got a default font for a not found glyp…
++ if (!glyphidx && frc[f].flags == frcflags
++ && frc[f].unicodep == rune) {
++ break;
++ }
+ }
+- }
+
+- /* Nothing was found. Use fontconfig to find matching font. */
+- if (f >= frclen) {
+- if (!font->set)
+- font->set = FcFontSort(0, font->pattern,
+- 1, 0, &fcres);
+- fcsets[0] = font->set;
+-
+- /*
+- * Nothing was found in the cache. Now use
+- * some dozen of Fontconfig calls to get the
+- * font for one single character.
+- *
+- * Xft and fontconfig are design failures.
+- */
+- fcpattern = FcPatternDuplicate(font->pattern);
+- fccharset = FcCharSetCreate();
+-
+- FcCharSetAddChar(fccharset, rune);
+- FcPatternAddCharSet(fcpattern, FC_CHARSET,
+- fccharset);
+- FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
+-
+- FcConfigSubstitute(0, fcpattern,
+- FcMatchPattern);
+- FcDefaultSubstitute(fcpattern);
+-
+- fontpattern = FcFontSetMatch(0, fcsets, 1,
+- fcpattern, &fcres);
+-
+- /* Allocate memory for the new cache entry. */
+- if (frclen >= frccap) {
+- frccap += 16;
+- frc = xrealloc(frc, frccap * sizeof(Fontcache…
++ /* Nothing was found. Use fontconfig to find matching…
++ if (f >= frclen) {
++ if (!font->set)
++ font->set = FcFontSort(0, font->patte…
++ 1, 0, &fcres);
++ fcsets[0] = font->set;
++
++ /*
++ * Nothing was found in the cache. Now use
++ * some dozen of Fontconfig calls to get the
++ * font for one single character.
++ *
++ * Xft and fontconfig are design failures.
++ */
++ fcpattern = FcPatternDuplicate(font->pattern);
++ fccharset = FcCharSetCreate();
++
++ FcCharSetAddChar(fccharset, rune);
++ FcPatternAddCharSet(fcpattern, FC_CHARSET,
++ fccharset);
++ FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
++
++ FcConfigSubstitute(0, fcpattern,
++ FcMatchPattern);
++ FcDefaultSubstitute(fcpattern);
++
++ fontpattern = FcFontSetMatch(0, fcsets, 1,
++ fcpattern, &fcres);
++
++ /* Allocate memory for the new cache entry. */
++ if (frclen >= frccap) {
++ frccap += 16;
++ frc = xrealloc(frc, frccap * sizeof(F…
++ }
++
++ frc[frclen].font = XftFontOpenPattern(xw.dpy,
++ fontpattern);
++ if (!frc[frclen].font)
++ die("XftFontOpenPattern failed seekin…
++ strerror(errno));
++ frc[frclen].flags = frcflags;
++ frc[frclen].unicodep = rune;
++
++ glyphidx = XftCharIndex(xw.dpy, frc[frclen].f…
++
++ f = frclen;
++ frclen++;
++
++ FcPatternDestroy(fcpattern);
++ FcCharSetDestroy(fccharset);
+ }
+
+- frc[frclen].font = XftFontOpenPattern(xw.dpy,
+- fontpattern);
+- if (!frc[frclen].font)
+- die("XftFontOpenPattern failed seeking fallba…
+- strerror(errno));
+- frc[frclen].flags = frcflags;
+- frc[frclen].unicodep = rune;
+-
+- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run…
+-
+- f = frclen;
+- frclen++;
+-
+- FcPatternDestroy(fcpattern);
+- FcCharSetDestroy(fccharset);
++ specs[numspecs].font = frc[f].font;
++ specs[numspecs].glyph = glyphidx;
++ specs[numspecs].x = (short)xp;
++ specs[numspecs].y = (short)yp;
++ numspecs++;
+ }
+-
+- specs[numspecs].font = frc[f].font;
+- specs[numspecs].glyph = glyphidx;
+- specs[numspecs].x = (short)xp;
+- specs[numspecs].y = (short)yp;
+- xp += runewidth;
+- numspecs++;
+ }
+
++ /* Cleanup and get ready for next segment. */
++ hbcleanup(&shaped);
+ return numspecs;
+ }
+
+ void
+-xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
++xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
+ {
+- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
+ int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
+ width = charlen * win.cw;
+ Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
+ XRenderColor colfg, colbg;
+@@ -1526,23 +1542,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Gly…
+ void
+ xdrawglyph(Glyph g, int x, int y)
+ {
+ int numspecs;
+- XftGlyphFontSpec spec;
++ XftGlyphFontSpec *specs = xw.specbuf;
+
+- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
+- xdrawglyphfontspecs(&spec, g, numspecs, x, y);
++ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y);
++ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? …
+ }
+
+ void
+-xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
++xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int…
+ {
+ Color drawcol;
+
+ /* remove the old cursor */
+ if (selected(ox, oy))
+ og.mode ^= ATTR_REVERSE;
+- xdrawglyph(og, ox, oy);
++
++ /* Redraw the line where cursor was previously.
++ * It will restore the ligatures broken by the cursor. */
++ xdrawline(line, 0, oy, len);
+
+ if (IS_SET(MODE_HIDE))
+ return;
+
+@@ -1674,30 +1693,30 @@ xdrawline(Line line, int x1, int y1, int x2)
+ int i, x, ox, numspecs;
+ Glyph base, new;
+ XftGlyphFontSpec *specs = xw.specbuf;
+
+- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
+ i = ox = 0;
+- for (x = x1; x < x2 && i < numspecs; x++) {
++ for (x = x1; x < x2; x++) {
+ new = line[x];
+ if (new.mode == ATTR_WDUMMY)
+ continue;
+ if (selected(x, y1))
+ new.mode ^= ATTR_REVERSE;
+- if (i > 0 && ATTRCMP(base, new)) {
+- xdrawglyphfontspecs(specs, base, i, ox, y1);
+- specs += i;
+- numspecs -= i;
++ if ((i > 0) && ATTRCMP(base, new)) {
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - …
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x …
+ i = 0;
+ }
+ if (i == 0) {
+ ox = x;
+ base = new;
+ }
+ i++;
+ }
+- if (i > 0)
+- xdrawglyphfontspecs(specs, base, i, ox, y1);
++ if (i > 0) {
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,…
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox);
++ }
+ }
+
+ void
+ xfinishdraw(void)
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-scrollb…
@@ -1,635 +0,0 @@
-diff --git a/Makefile b/Makefile
-index 470ac86..38240da 100644
---- a/Makefile
-+++ b/Makefile
-@@ -4,7 +4,7 @@
-
- include config.mk
-
--SRC = st.c x.c
-+SRC = st.c x.c hb.c
- OBJ = $(SRC:.c=.o)
-
- all: st
-@@ -22,7 +22,8 @@ config.h:
- $(CC) $(STCFLAGS) -c $<
-
- st.o: config.h st.h win.h
--x.o: arg.h config.h st.h win.h
-+x.o: arg.h config.h st.h win.h hb.h
-+hb.o: st.h
-
- $(OBJ): config.h config.mk
-
-diff --git a/config.mk b/config.mk
-index 47c615e..d7439a3 100644
---- a/config.mk
-+++ b/config.mk
-@@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config
- # includes and libs
- INCS = -I$(X11INC) \
- `$(PKG_CONFIG) --cflags fontconfig` \
-- `$(PKG_CONFIG) --cflags freetype2`
--LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\
-+ `$(PKG_CONFIG) --cflags freetype2` \
-+ `$(PKG_CONFIG) --cflags harfbuzz`
-+LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender \
- `$(PKG_CONFIG) --libs fontconfig` \
-- `$(PKG_CONFIG) --libs freetype2`
-+ `$(PKG_CONFIG) --libs freetype2` \
-+ `$(PKG_CONFIG) --libs harfbuzz`
-
- # flags
- STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
-diff --git a/hb.c b/hb.c
-new file mode 100644
-index 0000000..99412c8
---- /dev/null
-+++ b/hb.c
-@@ -0,0 +1,125 @@
-+#include <stdlib.h>
-+#include <stdio.h>
-+#include <math.h>
-+#include <X11/Xft/Xft.h>
-+#include <X11/cursorfont.h>
-+#include <hb.h>
-+#include <hb-ft.h>
-+
-+#include "st.h"
-+#include "hb.h"
-+
-+#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start…
-+#define BUFFER_STEP 256
-+
-+hb_font_t *hbfindfont(XftFont *match);
-+
-+typedef struct {
-+ XftFont *match;
-+ hb_font_t *font;
-+} HbFontMatch;
-+
-+typedef struct {
-+ size_t capacity;
-+ HbFontMatch *fonts;
-+} HbFontCache;
-+
-+static HbFontCache hbfontcache = { 0, NULL };
-+
-+typedef struct {
-+ size_t capacity;
-+ Rune *runes;
-+} RuneBuffer;
-+
-+static RuneBuffer hbrunebuffer = { 0, NULL };
-+
-+/*
-+ * Poplulate the array with a list of font features, wrapped in FEATURE macro,
-+ * e. g.
-+ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g')
-+ */
-+hb_feature_t features[] = { };
-+
-+void
-+hbunloadfonts()
-+{
-+ for (int i = 0; i < hbfontcache.capacity; i++) {
-+ hb_font_destroy(hbfontcache.fonts[i].font);
-+ XftUnlockFace(hbfontcache.fonts[i].match);
-+ }
-+
-+ if (hbfontcache.fonts != NULL) {
-+ free(hbfontcache.fonts);
-+ hbfontcache.fonts = NULL;
-+ }
-+ hbfontcache.capacity = 0;
-+}
-+
-+hb_font_t *
-+hbfindfont(XftFont *match)
-+{
-+ for (int i = 0; i < hbfontcache.capacity; i++) {
-+ if (hbfontcache.fonts[i].match == match)
-+ return hbfontcache.fonts[i].font;
-+ }
-+
-+ /* Font not found in cache, caching it now. */
-+ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * …
-+ FT_Face face = XftLockFace(match);
-+ hb_font_t *font = hb_ft_font_create(face, NULL);
-+ if (font == NULL)
-+ die("Failed to load Harfbuzz font.");
-+
-+ hbfontcache.fonts[hbfontcache.capacity].match = match;
-+ hbfontcache.fonts[hbfontcache.capacity].font = font;
-+ hbfontcache.capacity += 1;
-+
-+ return font;
-+}
-+
-+void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, …
-+ ushort mode = USHRT_MAX;
-+ unsigned int glyph_count;
-+ int rune_idx, glyph_idx, end = start + length;
-+
-+ hb_font_t *font = hbfindfont(xfont);
-+ if (font == NULL)
-+ return;
-+
-+ hb_buffer_t *buffer = hb_buffer_create();
-+ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
-+ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_…
-+
-+ /* Resize the buffer if required length is larger. */
-+ if (hbrunebuffer.capacity < length) {
-+ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S…
-+ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer…
-+ }
-+
-+ /* Fill buffer with codepoints. */
-+ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r…
-+ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u;
-+ mode = glyphs[glyph_idx].mode;
-+ if (mode & ATTR_WDUMMY)
-+ hbrunebuffer.runes[rune_idx] = 0x0020;
-+ }
-+ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt…
-+
-+ /* Shape the segment. */
-+ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t…
-+
-+ /* Get new glyph info. */
-+ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun…
-+ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly…
-+
-+ /* Fill the output. */
-+ data->buffer = buffer;
-+ data->glyphs = info;
-+ data->positions = pos;
-+ data->count = glyph_count;
-+}
-+
-+void hbcleanup(HbTransformData *data) {
-+ hb_buffer_destroy(data->buffer);
-+ memset(data, 0, sizeof(HbTransformData));
-+}
-diff --git a/hb.h b/hb.h
-new file mode 100644
-index 0000000..3b0ef44
---- /dev/null
-+++ b/hb.h
-@@ -0,0 +1,14 @@
-+#include <X11/Xft/Xft.h>
-+#include <hb.h>
-+#include <hb-ft.h>
-+
-+typedef struct {
-+ hb_buffer_t *buffer;
-+ hb_glyph_info_t *glyphs;
-+ hb_glyph_position_t *positions;
-+ unsigned int count;
-+} HbTransformData;
-+
-+void hbunloadfonts();
-+void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
-+void hbcleanup(HbTransformData *);
-diff --git a/st.c b/st.c
-index 79ee9ba..454771d 100644
---- a/st.c
-+++ b/st.c
-@@ -2711,7 +2711,9 @@ draw(void)
- drawregion(0, 0, term.col, term.row);
- if (term.scr == 0)
- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
-- term.ocx, term.ocy, term.line[term.ocy][term.…
-+ term.ocx, term.ocy, term.line[term.ocy][term.…
-+ term.line[term.ocy], term.col);
-+
- term.ocx = cx;
- term.ocy = term.c.y;
- xfinishdraw();
-diff --git a/st.h b/st.h
-index 78762a2..01eea49 100644
---- a/st.h
-+++ b/st.h
-@@ -11,7 +11,8 @@
- #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
- #define DEFAULT(a, b) (a) = (a) ? (a) : (b)
- #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)…
--#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f…
-+#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode …
-+ (a).fg != (b).fg || \
- (a).bg != (b).bg)
- #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
- (t1.tv_nsec-t2.tv_nsec)/1E6)
-diff --git a/win.h b/win.h
-index 6de960d..94679e4 100644
---- a/win.h
-+++ b/win.h
-@@ -25,7 +25,7 @@ enum win_mode {
-
- void xbell(void);
- void xclipcopy(void);
--void xdrawcursor(int, int, Glyph, int, int, Glyph);
-+void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int);
- void xdrawline(Line, int, int, int);
- void xfinishdraw(void);
- void xloadcols(void);
-diff --git a/x.c b/x.c
-index 27e81d1..5e11c1f 100644
---- a/x.c
-+++ b/x.c
-@@ -19,6 +19,7 @@ char *argv0;
- #include "arg.h"
- #include "st.h"
- #include "win.h"
-+#include "hb.h"
-
- /* types used in config.h */
- typedef struct {
-@@ -142,8 +143,9 @@ typedef struct {
- } DC;
-
- static inline ushort sixd_to_16bit(int);
-+static void xresetfontsettings(ushort mode, Font **font, int *frcflags);
- static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i…
--static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
-+static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
- static void xdrawglyph(Glyph, int, int);
- static void xclear(int, int, int, int);
- static int xgeommasktogravity(int);
-@@ -759,7 +761,7 @@ xresize(int col, int row)
- xclear(0, 0, win.w, win.h);
-
- /* resize to new width */
-- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
-+ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
- }
-
- ushort
-@@ -1071,6 +1073,9 @@ xunloadfont(Font *f)
- void
- xunloadfonts(void)
- {
-+ /* Clear Harfbuzz font cache. */
-+ hbunloadfonts();
-+
- /* Free the loaded fonts in the font cache. */
- while (frclen > 0)
- XftFontClose(xw.dpy, frc[--frclen].font);
-@@ -1202,7 +1207,7 @@ xinit(int cols, int rows)
- XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
-
- /* font spec buffer */
-- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
-+ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
-
- /* Xft rendering context */
- xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
-@@ -1256,6 +1261,22 @@ xinit(int cols, int rows)
- xsel.xtarget = XA_STRING;
- }
-
-+void
-+xresetfontsettings(ushort mode, Font **font, int *frcflags)
-+{
-+ *font = &dc.font;
-+ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
-+ *font = &dc.ibfont;
-+ *frcflags = FRC_ITALICBOLD;
-+ } else if (mode & ATTR_ITALIC) {
-+ *font = &dc.ifont;
-+ *frcflags = FRC_ITALIC;
-+ } else if (mode & ATTR_BOLD) {
-+ *font = &dc.bfont;
-+ *frcflags = FRC_BOLD;
-+ }
-+}
-+
- int
- xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in…
- {
-@@ -1270,128 +1291,156 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const G…
- FcPattern *fcpattern, *fontpattern;
- FcFontSet *fcsets[] = { NULL };
- FcCharSet *fccharset;
-- int i, f, numspecs = 0;
-+ int i, f, length = 0, start = 0, numspecs = 0;
-+ float cluster_xp = xp, cluster_yp = yp;
-+ HbTransformData shaped = { 0 };
-+
-+ /* Initial values. */
-+ mode = prevmode = glyphs[0].mode & ~ATTR_WRAP;
-+ xresetfontsettings(mode, &font, &frcflags);
-
- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
-- /* Fetch rune and mode for current glyph. */
-- rune = glyphs[i].u;
-- mode = glyphs[i].mode;
-+ mode = glyphs[i].mode & ~ATTR_WRAP;
-
- /* Skip dummy wide-character spacing. */
-- if (mode == ATTR_WDUMMY)
-+ if (mode & ATTR_WDUMMY && i < (len - 1))
- continue;
-
-- /* Determine font for glyph if different from previous glyph.…
-- if (prevmode != mode) {
-- prevmode = mode;
-- font = &dc.font;
-- frcflags = FRC_NORMAL;
-- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0…
-- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
-- font = &dc.ibfont;
-- frcflags = FRC_ITALICBOLD;
-- } else if (mode & ATTR_ITALIC) {
-- font = &dc.ifont;
-- frcflags = FRC_ITALIC;
-- } else if (mode & ATTR_BOLD) {
-- font = &dc.bfont;
-- frcflags = FRC_BOLD;
-+ if (
-+ prevmode != mode
-+ || ATTRCMP(glyphs[start], glyphs[i])
-+ || selected(x + i, y) != selected(x + start, y)
-+ || i == (len - 1)
-+ ) {
-+ /* Handle 1-character wide segments and end of line */
-+ length = i - start;
-+ if (i == start) {
-+ length = 1;
-+ } else if (i == (len - 1)) {
-+ length = (i - start + 1);
- }
-- yp = winy + font->ascent;
-- }
-
-- /* Lookup character index with default font. */
-- glyphidx = XftCharIndex(xw.dpy, font->match, rune);
-- if (glyphidx) {
-- specs[numspecs].font = font->match;
-- specs[numspecs].glyph = glyphidx;
-- specs[numspecs].x = (short)xp;
-- specs[numspecs].y = (short)yp;
-- xp += runewidth;
-- numspecs++;
-- continue;
-- }
--
-- /* Fallback on font cache, search the font cache for match. */
-- for (f = 0; f < frclen; f++) {
-- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
-- /* Everything correct. */
-- if (glyphidx && frc[f].flags == frcflags)
-- break;
-- /* We got a default font for a not found glyph. */
-- if (!glyphidx && frc[f].flags == frcflags
-- && frc[f].unicodep == rune) {
-- break;
-+ /* Shape the segment. */
-+ hbtransform(&shaped, font->match, glyphs, start, leng…
-+ runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE…
-+ cluster_xp = xp; cluster_yp = yp;
-+ for (int code_idx = 0; code_idx < shaped.count; code_…
-+ int idx = shaped.glyphs[code_idx].cluster;
-+
-+ if (glyphs[start + idx].mode & ATTR_WDUMMY)
-+ continue;
-+
-+ /* Advance the drawing cursor if we've moved …
-+ if (code_idx > 0 && idx != shaped.glyphs[code…
-+ xp += runewidth;
-+ cluster_xp = xp;
-+ cluster_yp = yp;
-+ runewidth = win.cw * ((glyphs[start +…
-+ }
-+
-+ if (shaped.glyphs[code_idx].codepoint != 0) {
-+ /* If symbol is found, put it into th…
-+ specs[numspecs].font = font->match;
-+ specs[numspecs].glyph = shaped.glyphs…
-+ specs[numspecs].x = cluster_xp + (sho…
-+ specs[numspecs].y = cluster_yp - (sho…
-+ cluster_xp += shaped.positions[code_i…
-+ cluster_yp += shaped.positions[code_i…
-+ numspecs++;
-+ } else {
-+ /* If it's not found, try to fetch it…
-+ rune = glyphs[start + idx].u;
-+ for (f = 0; f < frclen; f++) {
-+ glyphidx = XftCharIndex(xw.dp…
-+ /* Everything correct. */
-+ if (glyphidx && frc[f].flags …
-+ break;
-+ /* We got a default font for …
-+ if (!glyphidx && frc[f].flags…
-+ && frc[f].uni…
-+ break;
-+ }
-+ }
-+
-+ /* Nothing was found. Use fontconfig …
-+ if (f >= frclen) {
-+ if (!font->set)
-+ font->set = FcFontSor…
-+ …
-+ fcsets[0] = font->set;
-+
-+ /*
-+ * Nothing was found in the c…
-+ * some dozen of Fontconfig c…
-+ * font for one single charac…
-+ *
-+ * Xft and fontconfig are des…
-+ */
-+ fcpattern = FcPatternDuplicat…
-+ fccharset = FcCharSetCreate();
-+
-+ FcCharSetAddChar(fccharset, r…
-+ FcPatternAddCharSet(fcpattern…
-+ fccharset);
-+ FcPatternAddBool(fcpattern, F…
-+
-+ FcConfigSubstitute(0, fcpatte…
-+ FcMatchPatter…
-+ FcDefaultSubstitute(fcpattern…
-+
-+ fontpattern = FcFontSetMatch(…
-+ fcpattern, &f…
-+
-+ /* Allocate memory for the ne…
-+ if (frclen >= frccap) {
-+ frccap += 16;
-+ frc = xrealloc(frc, f…
-+ }
-+
-+ frc[frclen].font = XftFontOpe…
-+ fontpattern);
-+ if (!frc[frclen].font)
-+ die("XftFontOpenPatte…
-+ strerror(errn…
-+ frc[frclen].flags = frcflags;
-+ frc[frclen].unicodep = rune;
-+
-+ glyphidx = XftCharIndex(xw.dp…
-+
-+ f = frclen;
-+ frclen++;
-+
-+ FcPatternDestroy(fcpattern);
-+ FcCharSetDestroy(fccharset);
-+ }
-+
-+ specs[numspecs].font = frc[f].font;
-+ specs[numspecs].glyph = glyphidx;
-+ specs[numspecs].x = (short)xp;
-+ specs[numspecs].y = (short)yp;
-+ numspecs++;
-+ }
- }
-- }
-
-- /* Nothing was found. Use fontconfig to find matching font. */
-- if (f >= frclen) {
-- if (!font->set)
-- font->set = FcFontSort(0, font->pattern,
-- 1, 0, &fcres);
-- fcsets[0] = font->set;
-+ /* Cleanup and get ready for next segment. */
-+ hbcleanup(&shaped);
-+ start = i;
-
-- /*
-- * Nothing was found in the cache. Now use
-- * some dozen of Fontconfig calls to get the
-- * font for one single character.
-- *
-- * Xft and fontconfig are design failures.
-- */
-- fcpattern = FcPatternDuplicate(font->pattern);
-- fccharset = FcCharSetCreate();
--
-- FcCharSetAddChar(fccharset, rune);
-- FcPatternAddCharSet(fcpattern, FC_CHARSET,
-- fccharset);
-- FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
--
-- FcConfigSubstitute(0, fcpattern,
-- FcMatchPattern);
-- FcDefaultSubstitute(fcpattern);
--
-- fontpattern = FcFontSetMatch(0, fcsets, 1,
-- fcpattern, &fcres);
--
-- /* Allocate memory for the new cache entry. */
-- if (frclen >= frccap) {
-- frccap += 16;
-- frc = xrealloc(frc, frccap * sizeof(Fontcache…
-+ /* Determine font for glyph if different from previou…
-+ if (prevmode != mode) {
-+ prevmode = mode;
-+ xresetfontsettings(mode, &font, &frcflags);
-+ yp = winy + font->ascent;
- }
--
-- frc[frclen].font = XftFontOpenPattern(xw.dpy,
-- fontpattern);
-- if (!frc[frclen].font)
-- die("XftFontOpenPattern failed seeking fallba…
-- strerror(errno));
-- frc[frclen].flags = frcflags;
-- frc[frclen].unicodep = rune;
--
-- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run…
--
-- f = frclen;
-- frclen++;
--
-- FcPatternDestroy(fcpattern);
-- FcCharSetDestroy(fccharset);
- }
--
-- specs[numspecs].font = frc[f].font;
-- specs[numspecs].glyph = glyphidx;
-- specs[numspecs].x = (short)xp;
-- specs[numspecs].y = (short)yp;
-- xp += runewidth;
-- numspecs++;
- }
-
- return numspecs;
- }
-
- void
--xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
-+xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
- {
-- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
- int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
- width = charlen * win.cw;
- Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
-@@ -1527,21 +1576,24 @@ void
- xdrawglyph(Glyph g, int x, int y)
- {
- int numspecs;
-- XftGlyphFontSpec spec;
-+ XftGlyphFontSpec *specs = xw.specbuf;
-
-- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
-- xdrawglyphfontspecs(&spec, g, numspecs, x, y);
-+ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y);
-+ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? …
- }
-
- void
--xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
-+xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int…
- {
- Color drawcol;
-
- /* remove the old cursor */
- if (selected(ox, oy))
- og.mode ^= ATTR_REVERSE;
-- xdrawglyph(og, ox, oy);
-+
-+ /* Redraw the line where cursor was previously.
-+ * It will restore the ligatures broken by the cursor. */
-+ xdrawline(line, 0, oy, len);
-
- if (IS_SET(MODE_HIDE))
- return;
-@@ -1669,18 +1721,16 @@ xdrawline(Line line, int x1, int y1, int x2)
- Glyph base, new;
- XftGlyphFontSpec *specs = xw.specbuf;
-
-- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
- i = ox = 0;
-- for (x = x1; x < x2 && i < numspecs; x++) {
-+ for (x = x1; x < x2; x++) {
- new = line[x];
- if (new.mode == ATTR_WDUMMY)
- continue;
- if (selected(x, y1))
- new.mode ^= ATTR_REVERSE;
-- if (i > 0 && ATTRCMP(base, new)) {
-- xdrawglyphfontspecs(specs, base, i, ox, y1);
-- specs += i;
-- numspecs -= i;
-+ if ((i > 0) && ATTRCMP(base, new)) {
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - …
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x …
- i = 0;
- }
- if (i == 0) {
-@@ -1689,8 +1739,10 @@ xdrawline(Line line, int x1, int y1, int x2)
- }
- i++;
- }
-- if (i > 0)
-- xdrawglyphfontspecs(specs, base, i, ox, y1);
-+ if (i > 0) {
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,…
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox);
-+ }
- }
-
- void
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-scrollb…
@@ -0,0 +1,650 @@
+diff --git a/Makefile b/Makefile
+index 15db421..dfcea0f 100644
+--- a/Makefile
++++ b/Makefile
+@@ -3,9 +3,9 @@
+ .POSIX:
+
+ include config.mk
+
+-SRC = st.c x.c
++SRC = st.c x.c hb.c
+ OBJ = $(SRC:.c=.o)
+
+ all: st
+
+@@ -15,9 +15,10 @@ config.h:
+ .c.o:
+ $(CC) $(STCFLAGS) -c $<
+
+ st.o: config.h st.h win.h
+-x.o: arg.h config.h st.h win.h
++x.o: arg.h config.h st.h win.h hb.h
++hb.o: st.h
+
+ $(OBJ): config.h config.mk
+
+ st: $(OBJ)
+diff --git a/config.mk b/config.mk
+index 069a6c2..977b7c7 100644
+--- a/config.mk
++++ b/config.mk
+@@ -14,12 +14,14 @@ PKG_CONFIG = pkg-config
+
+ # includes and libs
+ INCS = -I$(X11INC) \
+ `$(PKG_CONFIG) --cflags fontconfig` \
+- `$(PKG_CONFIG) --cflags freetype2`
++ `$(PKG_CONFIG) --cflags freetype2` \
++ `$(PKG_CONFIG) --cflags harfbuzz`
+ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\
+ `$(PKG_CONFIG) --libs fontconfig` \
+- `$(PKG_CONFIG) --libs freetype2`
++ `$(PKG_CONFIG) --libs freetype2` \
++ `$(PKG_CONFIG) --libs harfbuzz`
+
+ # flags
+ STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
+ STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS)
+@@ -28,9 +30,10 @@ STLDFLAGS = $(LIBS) $(LDFLAGS)
+ # OpenBSD:
+ #CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE
+ #LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \
+ # `$(PKG_CONFIG) --libs fontconfig` \
+-# `$(PKG_CONFIG) --libs freetype2`
++# `$(PKG_CONFIG) --libs freetype2` \
++# `$(PKG_CONFIG) --libs harfbuzz`
+ #MANPREFIX = ${PREFIX}/man
+
+ # compiler and linker
+ # CC = c99
+diff --git a/hb.c b/hb.c
+new file mode 100644
+index 0000000..99412c8
+--- /dev/null
++++ b/hb.c
+@@ -0,0 +1,125 @@
++#include <stdlib.h>
++#include <stdio.h>
++#include <math.h>
++#include <X11/Xft/Xft.h>
++#include <X11/cursorfont.h>
++#include <hb.h>
++#include <hb-ft.h>
++
++#include "st.h"
++#include "hb.h"
++
++#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start…
++#define BUFFER_STEP 256
++
++hb_font_t *hbfindfont(XftFont *match);
++
++typedef struct {
++ XftFont *match;
++ hb_font_t *font;
++} HbFontMatch;
++
++typedef struct {
++ size_t capacity;
++ HbFontMatch *fonts;
++} HbFontCache;
++
++static HbFontCache hbfontcache = { 0, NULL };
++
++typedef struct {
++ size_t capacity;
++ Rune *runes;
++} RuneBuffer;
++
++static RuneBuffer hbrunebuffer = { 0, NULL };
++
++/*
++ * Poplulate the array with a list of font features, wrapped in FEATURE macro,
++ * e. g.
++ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g')
++ */
++hb_feature_t features[] = { };
++
++void
++hbunloadfonts()
++{
++ for (int i = 0; i < hbfontcache.capacity; i++) {
++ hb_font_destroy(hbfontcache.fonts[i].font);
++ XftUnlockFace(hbfontcache.fonts[i].match);
++ }
++
++ if (hbfontcache.fonts != NULL) {
++ free(hbfontcache.fonts);
++ hbfontcache.fonts = NULL;
++ }
++ hbfontcache.capacity = 0;
++}
++
++hb_font_t *
++hbfindfont(XftFont *match)
++{
++ for (int i = 0; i < hbfontcache.capacity; i++) {
++ if (hbfontcache.fonts[i].match == match)
++ return hbfontcache.fonts[i].font;
++ }
++
++ /* Font not found in cache, caching it now. */
++ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * …
++ FT_Face face = XftLockFace(match);
++ hb_font_t *font = hb_ft_font_create(face, NULL);
++ if (font == NULL)
++ die("Failed to load Harfbuzz font.");
++
++ hbfontcache.fonts[hbfontcache.capacity].match = match;
++ hbfontcache.fonts[hbfontcache.capacity].font = font;
++ hbfontcache.capacity += 1;
++
++ return font;
++}
++
++void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, …
++ ushort mode = USHRT_MAX;
++ unsigned int glyph_count;
++ int rune_idx, glyph_idx, end = start + length;
++
++ hb_font_t *font = hbfindfont(xfont);
++ if (font == NULL)
++ return;
++
++ hb_buffer_t *buffer = hb_buffer_create();
++ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
++ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_…
++
++ /* Resize the buffer if required length is larger. */
++ if (hbrunebuffer.capacity < length) {
++ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S…
++ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer…
++ }
++
++ /* Fill buffer with codepoints. */
++ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r…
++ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u;
++ mode = glyphs[glyph_idx].mode;
++ if (mode & ATTR_WDUMMY)
++ hbrunebuffer.runes[rune_idx] = 0x0020;
++ }
++ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt…
++
++ /* Shape the segment. */
++ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t…
++
++ /* Get new glyph info. */
++ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun…
++ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly…
++
++ /* Fill the output. */
++ data->buffer = buffer;
++ data->glyphs = info;
++ data->positions = pos;
++ data->count = glyph_count;
++}
++
++void hbcleanup(HbTransformData *data) {
++ hb_buffer_destroy(data->buffer);
++ memset(data, 0, sizeof(HbTransformData));
++}
+diff --git a/hb.h b/hb.h
+new file mode 100644
+index 0000000..3b0ef44
+--- /dev/null
++++ b/hb.h
+@@ -0,0 +1,14 @@
++#include <X11/Xft/Xft.h>
++#include <hb.h>
++#include <hb-ft.h>
++
++typedef struct {
++ hb_buffer_t *buffer;
++ hb_glyph_info_t *glyphs;
++ hb_glyph_position_t *positions;
++ unsigned int count;
++} HbTransformData;
++
++void hbunloadfonts();
++void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
++void hbcleanup(HbTransformData *);
+diff --git a/st.c b/st.c
+index 2478942..bba90d3 100644
+--- a/st.c
++++ b/st.c
+@@ -2729,9 +2729,10 @@ draw(void)
+
+ drawregion(0, 0, term.col, term.row);
+ if (term.scr == 0)
+ xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
+- term.ocx, term.ocy, term.line[term.ocy][term.…
++ term.ocx, term.ocy, term.line[term.ocy][term.…
++ term.line[term.ocy], term.col);
+ term.ocx = cx;
+ term.ocy = term.c.y;
+ xfinishdraw();
+ if (ocx != term.ocx || ocy != term.ocy)
+diff --git a/st.h b/st.h
+index 78762a2..01eea49 100644
+--- a/st.h
++++ b/st.h
+@@ -10,9 +10,10 @@
+ #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
+ #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
+ #define DEFAULT(a, b) (a) = (a) ? (a) : (b)
+ #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)…
+-#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f…
++#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode …
++ (a).fg != (b).fg || \
+ (a).bg != (b).bg)
+ #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
+ (t1.tv_nsec-t2.tv_nsec)/1E6)
+ #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
+diff --git a/win.h b/win.h
+index 6de960d..94679e4 100644
+--- a/win.h
++++ b/win.h
+@@ -24,9 +24,9 @@ enum win_mode {
+ };
+
+ void xbell(void);
+ void xclipcopy(void);
+-void xdrawcursor(int, int, Glyph, int, int, Glyph);
++void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int);
+ void xdrawline(Line, int, int, int);
+ void xfinishdraw(void);
+ void xloadcols(void);
+ int xsetcolorname(int, const char *);
+diff --git a/x.c b/x.c
+index 1e12ac8..b0819ac 100644
+--- a/x.c
++++ b/x.c
+@@ -18,8 +18,9 @@
+ char *argv0;
+ #include "arg.h"
+ #include "st.h"
+ #include "win.h"
++#include "hb.h"
+
+ /* types used in config.h */
+ typedef struct {
+ uint mod;
+@@ -141,10 +142,11 @@ typedef struct {
+ GC gc;
+ } DC;
+
+ static inline ushort sixd_to_16bit(int);
++static void xresetfontsettings(ushort mode, Font **font, int *frcflags);
+ static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i…
+-static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
++static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
+ static void xdrawglyph(Glyph, int, int);
+ static void xclear(int, int, int, int);
+ static int xgeommasktogravity(int);
+ static int ximopen(Display *);
+@@ -758,9 +760,9 @@ xresize(int col, int row)
+ XftDrawChange(xw.draw, xw.buf);
+ xclear(0, 0, win.w, win.h);
+
+ /* resize to new width */
+- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
++ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
+ }
+
+ ushort
+ sixd_to_16bit(int x)
+@@ -1070,8 +1072,11 @@ xunloadfont(Font *f)
+
+ void
+ xunloadfonts(void)
+ {
++ /* Clear Harfbuzz font cache. */
++ hbunloadfonts();
++
+ /* Free the loaded fonts in the font cache. */
+ while (frclen > 0)
+ XftFontClose(xw.dpy, frc[--frclen].font);
+
+@@ -1201,9 +1206,9 @@ xinit(int cols, int rows)
+ XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel);
+ XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
+
+ /* font spec buffer */
+- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
++ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
+
+ /* Xft rendering context */
+ xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
+
+@@ -1255,144 +1260,155 @@ xinit(int cols, int rows)
+ if (xsel.xtarget == None)
+ xsel.xtarget = XA_STRING;
+ }
+
++void
++xresetfontsettings(ushort mode, Font **font, int *frcflags)
++{
++ *font = &dc.font;
++ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
++ *font = &dc.ibfont;
++ *frcflags = FRC_ITALICBOLD;
++ } else if (mode & ATTR_ITALIC) {
++ *font = &dc.ifont;
++ *frcflags = FRC_ITALIC;
++ } else if (mode & ATTR_BOLD) {
++ *font = &dc.bfont;
++ *frcflags = FRC_BOLD;
++ }
++}
++
+ int
+ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in…
+ {
+ float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp,…
+- ushort mode, prevmode = USHRT_MAX;
++ ushort mode = glyphs[0].mode & ~ATTR_WRAP;
+ Font *font = &dc.font;
+ int frcflags = FRC_NORMAL;
+- float runewidth = win.cw;
++ float runewidth = win.cw * ((glyphs[0].mode & ATTR_WIDE) ? 2.0f : 1.0…
+ Rune rune;
+ FT_UInt glyphidx;
+ FcResult fcres;
+ FcPattern *fcpattern, *fontpattern;
+ FcFontSet *fcsets[] = { NULL };
+ FcCharSet *fccharset;
+- int i, f, numspecs = 0;
++ int f, code_idx, numspecs = 0;
++ float cluster_xp = xp, cluster_yp = yp;
++ HbTransformData shaped = { 0 };
+
+- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
+- /* Fetch rune and mode for current glyph. */
+- rune = glyphs[i].u;
+- mode = glyphs[i].mode;
++ /* Initial values. */
++ xresetfontsettings(mode, &font, &frcflags);
+
+- /* Skip dummy wide-character spacing. */
+- if (mode == ATTR_WDUMMY)
++ /* Shape the segment. */
++ hbtransform(&shaped, font->match, glyphs, 0, len);
++ xp = winx; yp = winy + font->ascent;
++ cluster_xp = xp; cluster_yp = yp;
++
++ for (code_idx = 0; code_idx < shaped.count; code_idx++) {
++ int idx = shaped.glyphs[code_idx].cluster;
++
++ if (glyphs[idx].mode & ATTR_WDUMMY)
+ continue;
+
+- /* Determine font for glyph if different from previous glyph.…
+- if (prevmode != mode) {
+- prevmode = mode;
+- font = &dc.font;
+- frcflags = FRC_NORMAL;
+- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0…
+- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
+- font = &dc.ibfont;
+- frcflags = FRC_ITALICBOLD;
+- } else if (mode & ATTR_ITALIC) {
+- font = &dc.ifont;
+- frcflags = FRC_ITALIC;
+- } else if (mode & ATTR_BOLD) {
+- font = &dc.bfont;
+- frcflags = FRC_BOLD;
+- }
+- yp = winy + font->ascent;
++ /* Advance the drawing cursor if we've moved to a new cluster…
++ if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluste…
++ xp += runewidth;
++ cluster_xp = xp;
++ cluster_yp = yp;
+ }
+
+- /* Lookup character index with default font. */
+- glyphidx = XftCharIndex(xw.dpy, font->match, rune);
+- if (glyphidx) {
++ if (shaped.glyphs[code_idx].codepoint != 0) {
++ /* If symbol is found, put it into the specs. */
+ specs[numspecs].font = font->match;
+- specs[numspecs].glyph = glyphidx;
+- specs[numspecs].x = (short)xp;
+- specs[numspecs].y = (short)yp;
+- xp += runewidth;
++ specs[numspecs].glyph = shaped.glyphs[code_idx].codep…
++ specs[numspecs].x = cluster_xp + (short)(shaped.posit…
++ specs[numspecs].y = cluster_yp - (short)(shaped.posit…
++ cluster_xp += shaped.positions[code_idx].x_advance / …
++ cluster_yp += shaped.positions[code_idx].y_advance / …
+ numspecs++;
+- continue;
+- }
+-
+- /* Fallback on font cache, search the font cache for match. */
+- for (f = 0; f < frclen; f++) {
+- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
+- /* Everything correct. */
+- if (glyphidx && frc[f].flags == frcflags)
+- break;
+- /* We got a default font for a not found glyph. */
+- if (!glyphidx && frc[f].flags == frcflags
+- && frc[f].unicodep == rune) {
+- break;
++ } else {
++ /* If it's not found, try to fetch it through the fon…
++ rune = glyphs[idx].u;
++ for (f = 0; f < frclen; f++) {
++ glyphidx = XftCharIndex(xw.dpy, frc[f].font, …
++ /* Everything correct. */
++ if (glyphidx && frc[f].flags == frcflags)
++ break;
++ /* We got a default font for a not found glyp…
++ if (!glyphidx && frc[f].flags == frcflags
++ && frc[f].unicodep == rune) {
++ break;
++ }
+ }
+- }
+
+- /* Nothing was found. Use fontconfig to find matching font. */
+- if (f >= frclen) {
+- if (!font->set)
+- font->set = FcFontSort(0, font->pattern,
+- 1, 0, &fcres);
+- fcsets[0] = font->set;
+-
+- /*
+- * Nothing was found in the cache. Now use
+- * some dozen of Fontconfig calls to get the
+- * font for one single character.
+- *
+- * Xft and fontconfig are design failures.
+- */
+- fcpattern = FcPatternDuplicate(font->pattern);
+- fccharset = FcCharSetCreate();
+-
+- FcCharSetAddChar(fccharset, rune);
+- FcPatternAddCharSet(fcpattern, FC_CHARSET,
+- fccharset);
+- FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
+-
+- FcConfigSubstitute(0, fcpattern,
+- FcMatchPattern);
+- FcDefaultSubstitute(fcpattern);
+-
+- fontpattern = FcFontSetMatch(0, fcsets, 1,
+- fcpattern, &fcres);
+-
+- /* Allocate memory for the new cache entry. */
+- if (frclen >= frccap) {
+- frccap += 16;
+- frc = xrealloc(frc, frccap * sizeof(Fontcache…
++ /* Nothing was found. Use fontconfig to find matching…
++ if (f >= frclen) {
++ if (!font->set)
++ font->set = FcFontSort(0, font->patte…
++ 1, 0, &fcres);
++ fcsets[0] = font->set;
++
++ /*
++ * Nothing was found in the cache. Now use
++ * some dozen of Fontconfig calls to get the
++ * font for one single character.
++ *
++ * Xft and fontconfig are design failures.
++ */
++ fcpattern = FcPatternDuplicate(font->pattern);
++ fccharset = FcCharSetCreate();
++
++ FcCharSetAddChar(fccharset, rune);
++ FcPatternAddCharSet(fcpattern, FC_CHARSET,
++ fccharset);
++ FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
++
++ FcConfigSubstitute(0, fcpattern,
++ FcMatchPattern);
++ FcDefaultSubstitute(fcpattern);
++
++ fontpattern = FcFontSetMatch(0, fcsets, 1,
++ fcpattern, &fcres);
++
++ /* Allocate memory for the new cache entry. */
++ if (frclen >= frccap) {
++ frccap += 16;
++ frc = xrealloc(frc, frccap * sizeof(F…
++ }
++
++ frc[frclen].font = XftFontOpenPattern(xw.dpy,
++ fontpattern);
++ if (!frc[frclen].font)
++ die("XftFontOpenPattern failed seekin…
++ strerror(errno));
++ frc[frclen].flags = frcflags;
++ frc[frclen].unicodep = rune;
++
++ glyphidx = XftCharIndex(xw.dpy, frc[frclen].f…
++
++ f = frclen;
++ frclen++;
++
++ FcPatternDestroy(fcpattern);
++ FcCharSetDestroy(fccharset);
+ }
+
+- frc[frclen].font = XftFontOpenPattern(xw.dpy,
+- fontpattern);
+- if (!frc[frclen].font)
+- die("XftFontOpenPattern failed seeking fallba…
+- strerror(errno));
+- frc[frclen].flags = frcflags;
+- frc[frclen].unicodep = rune;
+-
+- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run…
+-
+- f = frclen;
+- frclen++;
+-
+- FcPatternDestroy(fcpattern);
+- FcCharSetDestroy(fccharset);
++ specs[numspecs].font = frc[f].font;
++ specs[numspecs].glyph = glyphidx;
++ specs[numspecs].x = (short)xp;
++ specs[numspecs].y = (short)yp;
++ numspecs++;
+ }
+-
+- specs[numspecs].font = frc[f].font;
+- specs[numspecs].glyph = glyphidx;
+- specs[numspecs].x = (short)xp;
+- specs[numspecs].y = (short)yp;
+- xp += runewidth;
+- numspecs++;
+ }
+
++ /* Cleanup and get ready for next segment. */
++ hbcleanup(&shaped);
+ return numspecs;
+ }
+
+ void
+-xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
++xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
+ {
+- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
+ int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
+ width = charlen * win.cw;
+ Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
+ XRenderColor colfg, colbg;
+@@ -1526,23 +1542,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Gly…
+ void
+ xdrawglyph(Glyph g, int x, int y)
+ {
+ int numspecs;
+- XftGlyphFontSpec spec;
++ XftGlyphFontSpec *specs = xw.specbuf;
+
+- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
+- xdrawglyphfontspecs(&spec, g, numspecs, x, y);
++ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y);
++ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? …
+ }
+
+ void
+-xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
++xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int…
+ {
+ Color drawcol;
+
+ /* remove the old cursor */
+ if (selected(ox, oy))
+ og.mode ^= ATTR_REVERSE;
+- xdrawglyph(og, ox, oy);
++
++ /* Redraw the line where cursor was previously.
++ * It will restore the ligatures broken by the cursor. */
++ xdrawline(line, 0, oy, len);
+
+ if (IS_SET(MODE_HIDE))
+ return;
+
+@@ -1674,30 +1693,30 @@ xdrawline(Line line, int x1, int y1, int x2)
+ int i, x, ox, numspecs;
+ Glyph base, new;
+ XftGlyphFontSpec *specs = xw.specbuf;
+
+- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
+ i = ox = 0;
+- for (x = x1; x < x2 && i < numspecs; x++) {
++ for (x = x1; x < x2; x++) {
+ new = line[x];
+ if (new.mode == ATTR_WDUMMY)
+ continue;
+ if (selected(x, y1))
+ new.mode ^= ATTR_REVERSE;
+- if (i > 0 && ATTRCMP(base, new)) {
+- xdrawglyphfontspecs(specs, base, i, ox, y1);
+- specs += i;
+- numspecs -= i;
++ if ((i > 0) && ATTRCMP(base, new)) {
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - …
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x …
+ i = 0;
+ }
+ if (i == 0) {
+ ox = x;
+ base = new;
+ }
+ i++;
+ }
+- if (i > 0)
+- xdrawglyphfontspecs(specs, base, i, ox, y1);
++ if (i > 0) {
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,…
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox);
++ }
+ }
+
+ void
+ xfinishdraw(void)
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-scrollb…
@@ -1,635 +0,0 @@
-diff --git a/Makefile b/Makefile
-index 470ac86..38240da 100644
---- a/Makefile
-+++ b/Makefile
-@@ -4,7 +4,7 @@
-
- include config.mk
-
--SRC = st.c x.c
-+SRC = st.c x.c hb.c
- OBJ = $(SRC:.c=.o)
-
- all: st
-@@ -22,7 +22,8 @@ config.h:
- $(CC) $(STCFLAGS) -c $<
-
- st.o: config.h st.h win.h
--x.o: arg.h config.h st.h win.h
-+x.o: arg.h config.h st.h win.h hb.h
-+hb.o: st.h
-
- $(OBJ): config.h config.mk
-
-diff --git a/config.mk b/config.mk
-index 47c615e..d7439a3 100644
---- a/config.mk
-+++ b/config.mk
-@@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config
- # includes and libs
- INCS = -I$(X11INC) \
- `$(PKG_CONFIG) --cflags fontconfig` \
-- `$(PKG_CONFIG) --cflags freetype2`
--LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\
-+ `$(PKG_CONFIG) --cflags freetype2` \
-+ `$(PKG_CONFIG) --cflags harfbuzz`
-+LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender \
- `$(PKG_CONFIG) --libs fontconfig` \
-- `$(PKG_CONFIG) --libs freetype2`
-+ `$(PKG_CONFIG) --libs freetype2` \
-+ `$(PKG_CONFIG) --libs harfbuzz`
-
- # flags
- STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
-diff --git a/hb.c b/hb.c
-new file mode 100644
-index 0000000..99412c8
---- /dev/null
-+++ b/hb.c
-@@ -0,0 +1,125 @@
-+#include <stdlib.h>
-+#include <stdio.h>
-+#include <math.h>
-+#include <X11/Xft/Xft.h>
-+#include <X11/cursorfont.h>
-+#include <hb.h>
-+#include <hb-ft.h>
-+
-+#include "st.h"
-+#include "hb.h"
-+
-+#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start…
-+#define BUFFER_STEP 256
-+
-+hb_font_t *hbfindfont(XftFont *match);
-+
-+typedef struct {
-+ XftFont *match;
-+ hb_font_t *font;
-+} HbFontMatch;
-+
-+typedef struct {
-+ size_t capacity;
-+ HbFontMatch *fonts;
-+} HbFontCache;
-+
-+static HbFontCache hbfontcache = { 0, NULL };
-+
-+typedef struct {
-+ size_t capacity;
-+ Rune *runes;
-+} RuneBuffer;
-+
-+static RuneBuffer hbrunebuffer = { 0, NULL };
-+
-+/*
-+ * Poplulate the array with a list of font features, wrapped in FEATURE macro,
-+ * e. g.
-+ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g')
-+ */
-+hb_feature_t features[] = { };
-+
-+void
-+hbunloadfonts()
-+{
-+ for (int i = 0; i < hbfontcache.capacity; i++) {
-+ hb_font_destroy(hbfontcache.fonts[i].font);
-+ XftUnlockFace(hbfontcache.fonts[i].match);
-+ }
-+
-+ if (hbfontcache.fonts != NULL) {
-+ free(hbfontcache.fonts);
-+ hbfontcache.fonts = NULL;
-+ }
-+ hbfontcache.capacity = 0;
-+}
-+
-+hb_font_t *
-+hbfindfont(XftFont *match)
-+{
-+ for (int i = 0; i < hbfontcache.capacity; i++) {
-+ if (hbfontcache.fonts[i].match == match)
-+ return hbfontcache.fonts[i].font;
-+ }
-+
-+ /* Font not found in cache, caching it now. */
-+ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * …
-+ FT_Face face = XftLockFace(match);
-+ hb_font_t *font = hb_ft_font_create(face, NULL);
-+ if (font == NULL)
-+ die("Failed to load Harfbuzz font.");
-+
-+ hbfontcache.fonts[hbfontcache.capacity].match = match;
-+ hbfontcache.fonts[hbfontcache.capacity].font = font;
-+ hbfontcache.capacity += 1;
-+
-+ return font;
-+}
-+
-+void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, …
-+ ushort mode = USHRT_MAX;
-+ unsigned int glyph_count;
-+ int rune_idx, glyph_idx, end = start + length;
-+
-+ hb_font_t *font = hbfindfont(xfont);
-+ if (font == NULL)
-+ return;
-+
-+ hb_buffer_t *buffer = hb_buffer_create();
-+ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
-+ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_…
-+
-+ /* Resize the buffer if required length is larger. */
-+ if (hbrunebuffer.capacity < length) {
-+ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S…
-+ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer…
-+ }
-+
-+ /* Fill buffer with codepoints. */
-+ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r…
-+ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u;
-+ mode = glyphs[glyph_idx].mode;
-+ if (mode & ATTR_WDUMMY)
-+ hbrunebuffer.runes[rune_idx] = 0x0020;
-+ }
-+ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt…
-+
-+ /* Shape the segment. */
-+ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t…
-+
-+ /* Get new glyph info. */
-+ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun…
-+ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly…
-+
-+ /* Fill the output. */
-+ data->buffer = buffer;
-+ data->glyphs = info;
-+ data->positions = pos;
-+ data->count = glyph_count;
-+}
-+
-+void hbcleanup(HbTransformData *data) {
-+ hb_buffer_destroy(data->buffer);
-+ memset(data, 0, sizeof(HbTransformData));
-+}
-diff --git a/hb.h b/hb.h
-new file mode 100644
-index 0000000..3b0ef44
---- /dev/null
-+++ b/hb.h
-@@ -0,0 +1,14 @@
-+#include <X11/Xft/Xft.h>
-+#include <hb.h>
-+#include <hb-ft.h>
-+
-+typedef struct {
-+ hb_buffer_t *buffer;
-+ hb_glyph_info_t *glyphs;
-+ hb_glyph_position_t *positions;
-+ unsigned int count;
-+} HbTransformData;
-+
-+void hbunloadfonts();
-+void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
-+void hbcleanup(HbTransformData *);
-diff --git a/st.c b/st.c
-index c44797b..18aa1bf 100644
---- a/st.c
-+++ b/st.c
-@@ -2759,7 +2759,9 @@ draw(void)
- drawregion(0, 0, term.col, term.row);
- if (TSCREEN.off == 0)
- xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx],
-- term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]…
-+ term.ocx, term.ocy, TLINE(term.ocy)[term.ocx],
-+ TLINE(term.ocy), term.col);
-+
- term.ocx = cx;
- term.ocy = term.c.y;
- xfinishdraw();
-diff --git a/st.h b/st.h
-index 073851a..d0b071d 100644
---- a/st.h
-+++ b/st.h
-@@ -11,7 +11,8 @@
- #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
- #define DEFAULT(a, b) (a) = (a) ? (a) : (b)
- #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)…
--#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f…
-+#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode …
-+ (a).fg != (b).fg || \
- (a).bg != (b).bg)
- #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
- (t1.tv_nsec-t2.tv_nsec)/1E6)
-diff --git a/win.h b/win.h
-index 6de960d..94679e4 100644
---- a/win.h
-+++ b/win.h
-@@ -25,7 +25,7 @@ enum win_mode {
-
- void xbell(void);
- void xclipcopy(void);
--void xdrawcursor(int, int, Glyph, int, int, Glyph);
-+void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int);
- void xdrawline(Line, int, int, int);
- void xfinishdraw(void);
- void xloadcols(void);
-diff --git a/x.c b/x.c
-index b81f5be..c1611bb 100644
---- a/x.c
-+++ b/x.c
-@@ -19,6 +19,7 @@ char *argv0;
- #include "arg.h"
- #include "st.h"
- #include "win.h"
-+#include "hb.h"
-
- /* types used in config.h */
- typedef struct {
-@@ -144,8 +145,9 @@ typedef struct {
- } DC;
-
- static inline ushort sixd_to_16bit(int);
-+static void xresetfontsettings(ushort mode, Font **font, int *frcflags);
- static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i…
--static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
-+static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
- static void xdrawglyph(Glyph, int, int);
- static void xclear(int, int, int, int);
- static int xgeommasktogravity(int);
-@@ -761,7 +763,7 @@ xresize(int col, int row)
- xclear(0, 0, win.w, win.h);
-
- /* resize to new width */
-- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
-+ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
- }
-
- ushort
-@@ -1073,6 +1075,9 @@ xunloadfont(Font *f)
- void
- xunloadfonts(void)
- {
-+ /* Clear Harfbuzz font cache. */
-+ hbunloadfonts();
-+
- /* Free the loaded fonts in the font cache. */
- while (frclen > 0)
- XftFontClose(xw.dpy, frc[--frclen].font);
-@@ -1204,7 +1209,7 @@ xinit(int cols, int rows)
- XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
-
- /* font spec buffer */
-- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
-+ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
-
- /* Xft rendering context */
- xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
-@@ -1258,6 +1263,22 @@ xinit(int cols, int rows)
- xsel.xtarget = XA_STRING;
- }
-
-+void
-+xresetfontsettings(ushort mode, Font **font, int *frcflags)
-+{
-+ *font = &dc.font;
-+ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
-+ *font = &dc.ibfont;
-+ *frcflags = FRC_ITALICBOLD;
-+ } else if (mode & ATTR_ITALIC) {
-+ *font = &dc.ifont;
-+ *frcflags = FRC_ITALIC;
-+ } else if (mode & ATTR_BOLD) {
-+ *font = &dc.bfont;
-+ *frcflags = FRC_BOLD;
-+ }
-+}
-+
- int
- xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in…
- {
-@@ -1272,128 +1293,156 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const G…
- FcPattern *fcpattern, *fontpattern;
- FcFontSet *fcsets[] = { NULL };
- FcCharSet *fccharset;
-- int i, f, numspecs = 0;
-+ int i, f, length = 0, start = 0, numspecs = 0;
-+ float cluster_xp = xp, cluster_yp = yp;
-+ HbTransformData shaped = { 0 };
-+
-+ /* Initial values. */
-+ mode = prevmode = glyphs[0].mode & ~ATTR_WRAP;
-+ xresetfontsettings(mode, &font, &frcflags);
-
- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
-- /* Fetch rune and mode for current glyph. */
-- rune = glyphs[i].u;
-- mode = glyphs[i].mode;
-+ mode = glyphs[i].mode & ~ATTR_WRAP;
-
- /* Skip dummy wide-character spacing. */
-- if (mode == ATTR_WDUMMY)
-+ if (mode & ATTR_WDUMMY && i < (len - 1))
- continue;
-
-- /* Determine font for glyph if different from previous glyph.…
-- if (prevmode != mode) {
-- prevmode = mode;
-- font = &dc.font;
-- frcflags = FRC_NORMAL;
-- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0…
-- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
-- font = &dc.ibfont;
-- frcflags = FRC_ITALICBOLD;
-- } else if (mode & ATTR_ITALIC) {
-- font = &dc.ifont;
-- frcflags = FRC_ITALIC;
-- } else if (mode & ATTR_BOLD) {
-- font = &dc.bfont;
-- frcflags = FRC_BOLD;
-+ if (
-+ prevmode != mode
-+ || ATTRCMP(glyphs[start], glyphs[i])
-+ || selected(x + i, y) != selected(x + start, y)
-+ || i == (len - 1)
-+ ) {
-+ /* Handle 1-character wide segments and end of line */
-+ length = i - start;
-+ if (i == start) {
-+ length = 1;
-+ } else if (i == (len - 1)) {
-+ length = (i - start + 1);
- }
-- yp = winy + font->ascent;
-- }
-
-- /* Lookup character index with default font. */
-- glyphidx = XftCharIndex(xw.dpy, font->match, rune);
-- if (glyphidx) {
-- specs[numspecs].font = font->match;
-- specs[numspecs].glyph = glyphidx;
-- specs[numspecs].x = (short)xp;
-- specs[numspecs].y = (short)yp;
-- xp += runewidth;
-- numspecs++;
-- continue;
-- }
--
-- /* Fallback on font cache, search the font cache for match. */
-- for (f = 0; f < frclen; f++) {
-- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
-- /* Everything correct. */
-- if (glyphidx && frc[f].flags == frcflags)
-- break;
-- /* We got a default font for a not found glyph. */
-- if (!glyphidx && frc[f].flags == frcflags
-- && frc[f].unicodep == rune) {
-- break;
-+ /* Shape the segment. */
-+ hbtransform(&shaped, font->match, glyphs, start, leng…
-+ runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE…
-+ cluster_xp = xp; cluster_yp = yp;
-+ for (int code_idx = 0; code_idx < shaped.count; code_…
-+ int idx = shaped.glyphs[code_idx].cluster;
-+
-+ if (glyphs[start + idx].mode & ATTR_WDUMMY)
-+ continue;
-+
-+ /* Advance the drawing cursor if we've moved …
-+ if (code_idx > 0 && idx != shaped.glyphs[code…
-+ xp += runewidth;
-+ cluster_xp = xp;
-+ cluster_yp = yp;
-+ runewidth = win.cw * ((glyphs[start +…
-+ }
-+
-+ if (shaped.glyphs[code_idx].codepoint != 0) {
-+ /* If symbol is found, put it into th…
-+ specs[numspecs].font = font->match;
-+ specs[numspecs].glyph = shaped.glyphs…
-+ specs[numspecs].x = cluster_xp + (sho…
-+ specs[numspecs].y = cluster_yp - (sho…
-+ cluster_xp += shaped.positions[code_i…
-+ cluster_yp += shaped.positions[code_i…
-+ numspecs++;
-+ } else {
-+ /* If it's not found, try to fetch it…
-+ rune = glyphs[start + idx].u;
-+ for (f = 0; f < frclen; f++) {
-+ glyphidx = XftCharIndex(xw.dp…
-+ /* Everything correct. */
-+ if (glyphidx && frc[f].flags …
-+ break;
-+ /* We got a default font for …
-+ if (!glyphidx && frc[f].flags…
-+ && frc[f].uni…
-+ break;
-+ }
-+ }
-+
-+ /* Nothing was found. Use fontconfig …
-+ if (f >= frclen) {
-+ if (!font->set)
-+ font->set = FcFontSor…
-+ …
-+ fcsets[0] = font->set;
-+
-+ /*
-+ * Nothing was found in the c…
-+ * some dozen of Fontconfig c…
-+ * font for one single charac…
-+ *
-+ * Xft and fontconfig are des…
-+ */
-+ fcpattern = FcPatternDuplicat…
-+ fccharset = FcCharSetCreate();
-+
-+ FcCharSetAddChar(fccharset, r…
-+ FcPatternAddCharSet(fcpattern…
-+ fccharset);
-+ FcPatternAddBool(fcpattern, F…
-+
-+ FcConfigSubstitute(0, fcpatte…
-+ FcMatchPatter…
-+ FcDefaultSubstitute(fcpattern…
-+
-+ fontpattern = FcFontSetMatch(…
-+ fcpattern, &f…
-+
-+ /* Allocate memory for the ne…
-+ if (frclen >= frccap) {
-+ frccap += 16;
-+ frc = xrealloc(frc, f…
-+ }
-+
-+ frc[frclen].font = XftFontOpe…
-+ fontpattern);
-+ if (!frc[frclen].font)
-+ die("XftFontOpenPatte…
-+ strerror(errn…
-+ frc[frclen].flags = frcflags;
-+ frc[frclen].unicodep = rune;
-+
-+ glyphidx = XftCharIndex(xw.dp…
-+
-+ f = frclen;
-+ frclen++;
-+
-+ FcPatternDestroy(fcpattern);
-+ FcCharSetDestroy(fccharset);
-+ }
-+
-+ specs[numspecs].font = frc[f].font;
-+ specs[numspecs].glyph = glyphidx;
-+ specs[numspecs].x = (short)xp;
-+ specs[numspecs].y = (short)yp;
-+ numspecs++;
-+ }
- }
-- }
-
-- /* Nothing was found. Use fontconfig to find matching font. */
-- if (f >= frclen) {
-- if (!font->set)
-- font->set = FcFontSort(0, font->pattern,
-- 1, 0, &fcres);
-- fcsets[0] = font->set;
-+ /* Cleanup and get ready for next segment. */
-+ hbcleanup(&shaped);
-+ start = i;
-
-- /*
-- * Nothing was found in the cache. Now use
-- * some dozen of Fontconfig calls to get the
-- * font for one single character.
-- *
-- * Xft and fontconfig are design failures.
-- */
-- fcpattern = FcPatternDuplicate(font->pattern);
-- fccharset = FcCharSetCreate();
--
-- FcCharSetAddChar(fccharset, rune);
-- FcPatternAddCharSet(fcpattern, FC_CHARSET,
-- fccharset);
-- FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
--
-- FcConfigSubstitute(0, fcpattern,
-- FcMatchPattern);
-- FcDefaultSubstitute(fcpattern);
--
-- fontpattern = FcFontSetMatch(0, fcsets, 1,
-- fcpattern, &fcres);
--
-- /* Allocate memory for the new cache entry. */
-- if (frclen >= frccap) {
-- frccap += 16;
-- frc = xrealloc(frc, frccap * sizeof(Fontcache…
-+ /* Determine font for glyph if different from previou…
-+ if (prevmode != mode) {
-+ prevmode = mode;
-+ xresetfontsettings(mode, &font, &frcflags);
-+ yp = winy + font->ascent;
- }
--
-- frc[frclen].font = XftFontOpenPattern(xw.dpy,
-- fontpattern);
-- if (!frc[frclen].font)
-- die("XftFontOpenPattern failed seeking fallba…
-- strerror(errno));
-- frc[frclen].flags = frcflags;
-- frc[frclen].unicodep = rune;
--
-- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run…
--
-- f = frclen;
-- frclen++;
--
-- FcPatternDestroy(fcpattern);
-- FcCharSetDestroy(fccharset);
- }
--
-- specs[numspecs].font = frc[f].font;
-- specs[numspecs].glyph = glyphidx;
-- specs[numspecs].x = (short)xp;
-- specs[numspecs].y = (short)yp;
-- xp += runewidth;
-- numspecs++;
- }
-
- return numspecs;
- }
-
- void
--xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
-+xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
- {
-- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
- int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
- width = charlen * win.cw;
- Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
-@@ -1529,21 +1578,24 @@ void
- xdrawglyph(Glyph g, int x, int y)
- {
- int numspecs;
-- XftGlyphFontSpec spec;
-+ XftGlyphFontSpec *specs = xw.specbuf;
-
-- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
-- xdrawglyphfontspecs(&spec, g, numspecs, x, y);
-+ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y);
-+ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? …
- }
-
- void
--xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
-+xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int…
- {
- Color drawcol;
-
- /* remove the old cursor */
- if (selected(ox, oy))
- og.mode ^= ATTR_REVERSE;
-- xdrawglyph(og, ox, oy);
-+
-+ /* Redraw the line where cursor was previously.
-+ * It will restore the ligatures broken by the cursor. */
-+ xdrawline(line, 0, oy, len);
-
- if (IS_SET(MODE_HIDE))
- return;
-@@ -1671,18 +1723,16 @@ xdrawline(Line line, int x1, int y1, int x2)
- Glyph base, new;
- XftGlyphFontSpec *specs = xw.specbuf;
-
-- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
- i = ox = 0;
-- for (x = x1; x < x2 && i < numspecs; x++) {
-+ for (x = x1; x < x2; x++) {
- new = line[x];
- if (new.mode == ATTR_WDUMMY)
- continue;
- if (selected(x, y1))
- new.mode ^= ATTR_REVERSE;
-- if (i > 0 && ATTRCMP(base, new)) {
-- xdrawglyphfontspecs(specs, base, i, ox, y1);
-- specs += i;
-- numspecs -= i;
-+ if ((i > 0) && ATTRCMP(base, new)) {
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - …
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x …
- i = 0;
- }
- if (i == 0) {
-@@ -1691,8 +1741,10 @@ xdrawline(Line line, int x1, int y1, int x2)
- }
- i++;
- }
-- if (i > 0)
-- xdrawglyphfontspecs(specs, base, i, ox, y1);
-+ if (i > 0) {
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,…
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox);
-+ }
- }
-
- void
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-scrollb…
@@ -0,0 +1,650 @@
+diff --git a/Makefile b/Makefile
+index 15db421..dfcea0f 100644
+--- a/Makefile
++++ b/Makefile
+@@ -3,9 +3,9 @@
+ .POSIX:
+
+ include config.mk
+
+-SRC = st.c x.c
++SRC = st.c x.c hb.c
+ OBJ = $(SRC:.c=.o)
+
+ all: st
+
+@@ -15,9 +15,10 @@ config.h:
+ .c.o:
+ $(CC) $(STCFLAGS) -c $<
+
+ st.o: config.h st.h win.h
+-x.o: arg.h config.h st.h win.h
++x.o: arg.h config.h st.h win.h hb.h
++hb.o: st.h
+
+ $(OBJ): config.h config.mk
+
+ st: $(OBJ)
+diff --git a/config.mk b/config.mk
+index 069a6c2..977b7c7 100644
+--- a/config.mk
++++ b/config.mk
+@@ -14,12 +14,14 @@ PKG_CONFIG = pkg-config
+
+ # includes and libs
+ INCS = -I$(X11INC) \
+ `$(PKG_CONFIG) --cflags fontconfig` \
+- `$(PKG_CONFIG) --cflags freetype2`
++ `$(PKG_CONFIG) --cflags freetype2` \
++ `$(PKG_CONFIG) --cflags harfbuzz`
+ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\
+ `$(PKG_CONFIG) --libs fontconfig` \
+- `$(PKG_CONFIG) --libs freetype2`
++ `$(PKG_CONFIG) --libs freetype2` \
++ `$(PKG_CONFIG) --libs harfbuzz`
+
+ # flags
+ STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
+ STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS)
+@@ -28,9 +30,10 @@ STLDFLAGS = $(LIBS) $(LDFLAGS)
+ # OpenBSD:
+ #CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE
+ #LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \
+ # `$(PKG_CONFIG) --libs fontconfig` \
+-# `$(PKG_CONFIG) --libs freetype2`
++# `$(PKG_CONFIG) --libs freetype2` \
++# `$(PKG_CONFIG) --libs harfbuzz`
+ #MANPREFIX = ${PREFIX}/man
+
+ # compiler and linker
+ # CC = c99
+diff --git a/hb.c b/hb.c
+new file mode 100644
+index 0000000..99412c8
+--- /dev/null
++++ b/hb.c
+@@ -0,0 +1,125 @@
++#include <stdlib.h>
++#include <stdio.h>
++#include <math.h>
++#include <X11/Xft/Xft.h>
++#include <X11/cursorfont.h>
++#include <hb.h>
++#include <hb-ft.h>
++
++#include "st.h"
++#include "hb.h"
++
++#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start…
++#define BUFFER_STEP 256
++
++hb_font_t *hbfindfont(XftFont *match);
++
++typedef struct {
++ XftFont *match;
++ hb_font_t *font;
++} HbFontMatch;
++
++typedef struct {
++ size_t capacity;
++ HbFontMatch *fonts;
++} HbFontCache;
++
++static HbFontCache hbfontcache = { 0, NULL };
++
++typedef struct {
++ size_t capacity;
++ Rune *runes;
++} RuneBuffer;
++
++static RuneBuffer hbrunebuffer = { 0, NULL };
++
++/*
++ * Poplulate the array with a list of font features, wrapped in FEATURE macro,
++ * e. g.
++ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g')
++ */
++hb_feature_t features[] = { };
++
++void
++hbunloadfonts()
++{
++ for (int i = 0; i < hbfontcache.capacity; i++) {
++ hb_font_destroy(hbfontcache.fonts[i].font);
++ XftUnlockFace(hbfontcache.fonts[i].match);
++ }
++
++ if (hbfontcache.fonts != NULL) {
++ free(hbfontcache.fonts);
++ hbfontcache.fonts = NULL;
++ }
++ hbfontcache.capacity = 0;
++}
++
++hb_font_t *
++hbfindfont(XftFont *match)
++{
++ for (int i = 0; i < hbfontcache.capacity; i++) {
++ if (hbfontcache.fonts[i].match == match)
++ return hbfontcache.fonts[i].font;
++ }
++
++ /* Font not found in cache, caching it now. */
++ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * …
++ FT_Face face = XftLockFace(match);
++ hb_font_t *font = hb_ft_font_create(face, NULL);
++ if (font == NULL)
++ die("Failed to load Harfbuzz font.");
++
++ hbfontcache.fonts[hbfontcache.capacity].match = match;
++ hbfontcache.fonts[hbfontcache.capacity].font = font;
++ hbfontcache.capacity += 1;
++
++ return font;
++}
++
++void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, …
++ ushort mode = USHRT_MAX;
++ unsigned int glyph_count;
++ int rune_idx, glyph_idx, end = start + length;
++
++ hb_font_t *font = hbfindfont(xfont);
++ if (font == NULL)
++ return;
++
++ hb_buffer_t *buffer = hb_buffer_create();
++ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
++ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_…
++
++ /* Resize the buffer if required length is larger. */
++ if (hbrunebuffer.capacity < length) {
++ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S…
++ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer…
++ }
++
++ /* Fill buffer with codepoints. */
++ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r…
++ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u;
++ mode = glyphs[glyph_idx].mode;
++ if (mode & ATTR_WDUMMY)
++ hbrunebuffer.runes[rune_idx] = 0x0020;
++ }
++ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt…
++
++ /* Shape the segment. */
++ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t…
++
++ /* Get new glyph info. */
++ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun…
++ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly…
++
++ /* Fill the output. */
++ data->buffer = buffer;
++ data->glyphs = info;
++ data->positions = pos;
++ data->count = glyph_count;
++}
++
++void hbcleanup(HbTransformData *data) {
++ hb_buffer_destroy(data->buffer);
++ memset(data, 0, sizeof(HbTransformData));
++}
+diff --git a/hb.h b/hb.h
+new file mode 100644
+index 0000000..3b0ef44
+--- /dev/null
++++ b/hb.h
+@@ -0,0 +1,14 @@
++#include <X11/Xft/Xft.h>
++#include <hb.h>
++#include <hb-ft.h>
++
++typedef struct {
++ hb_buffer_t *buffer;
++ hb_glyph_info_t *glyphs;
++ hb_glyph_position_t *positions;
++ unsigned int count;
++} HbTransformData;
++
++void hbunloadfonts();
++void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
++void hbcleanup(HbTransformData *);
+diff --git a/st.c b/st.c
+index d9b163e..fbca4ba 100644
+--- a/st.c
++++ b/st.c
+@@ -2777,9 +2777,10 @@ draw(void)
+
+ drawregion(0, 0, term.col, term.row);
+ if (TSCREEN.off == 0)
+ xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx],
+- term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]…
++ term.ocx, term.ocy, TLINE(term.ocy)[term.ocx],
++ TLINE(term.ocy), term.col);
+ term.ocx = cx;
+ term.ocy = term.c.y;
+ xfinishdraw();
+ if (ocx != term.ocx || ocy != term.ocy)
+diff --git a/st.h b/st.h
+index 073851a..d0b071d 100644
+--- a/st.h
++++ b/st.h
+@@ -10,9 +10,10 @@
+ #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
+ #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
+ #define DEFAULT(a, b) (a) = (a) ? (a) : (b)
+ #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)…
+-#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f…
++#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode …
++ (a).fg != (b).fg || \
+ (a).bg != (b).bg)
+ #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
+ (t1.tv_nsec-t2.tv_nsec)/1E6)
+ #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
+diff --git a/win.h b/win.h
+index 6de960d..94679e4 100644
+--- a/win.h
++++ b/win.h
+@@ -24,9 +24,9 @@ enum win_mode {
+ };
+
+ void xbell(void);
+ void xclipcopy(void);
+-void xdrawcursor(int, int, Glyph, int, int, Glyph);
++void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int);
+ void xdrawline(Line, int, int, int);
+ void xfinishdraw(void);
+ void xloadcols(void);
+ int xsetcolorname(int, const char *);
+diff --git a/x.c b/x.c
+index c497e53..a213e52 100644
+--- a/x.c
++++ b/x.c
+@@ -18,8 +18,9 @@
+ char *argv0;
+ #include "arg.h"
+ #include "st.h"
+ #include "win.h"
++#include "hb.h"
+
+ /* types used in config.h */
+ typedef struct {
+ uint mod;
+@@ -143,10 +144,11 @@ typedef struct {
+ GC gc;
+ } DC;
+
+ static inline ushort sixd_to_16bit(int);
++static void xresetfontsettings(ushort mode, Font **font, int *frcflags);
+ static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i…
+-static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
++static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
+ static void xdrawglyph(Glyph, int, int);
+ static void xclear(int, int, int, int);
+ static int xgeommasktogravity(int);
+ static int ximopen(Display *);
+@@ -760,9 +762,9 @@ xresize(int col, int row)
+ XftDrawChange(xw.draw, xw.buf);
+ xclear(0, 0, win.w, win.h);
+
+ /* resize to new width */
+- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
++ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
+ }
+
+ ushort
+ sixd_to_16bit(int x)
+@@ -1072,8 +1074,11 @@ xunloadfont(Font *f)
+
+ void
+ xunloadfonts(void)
+ {
++ /* Clear Harfbuzz font cache. */
++ hbunloadfonts();
++
+ /* Free the loaded fonts in the font cache. */
+ while (frclen > 0)
+ XftFontClose(xw.dpy, frc[--frclen].font);
+
+@@ -1203,9 +1208,9 @@ xinit(int cols, int rows)
+ XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel);
+ XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
+
+ /* font spec buffer */
+- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
++ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
+
+ /* Xft rendering context */
+ xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
+
+@@ -1257,144 +1262,155 @@ xinit(int cols, int rows)
+ if (xsel.xtarget == None)
+ xsel.xtarget = XA_STRING;
+ }
+
++void
++xresetfontsettings(ushort mode, Font **font, int *frcflags)
++{
++ *font = &dc.font;
++ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
++ *font = &dc.ibfont;
++ *frcflags = FRC_ITALICBOLD;
++ } else if (mode & ATTR_ITALIC) {
++ *font = &dc.ifont;
++ *frcflags = FRC_ITALIC;
++ } else if (mode & ATTR_BOLD) {
++ *font = &dc.bfont;
++ *frcflags = FRC_BOLD;
++ }
++}
++
+ int
+ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in…
+ {
+ float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp,…
+- ushort mode, prevmode = USHRT_MAX;
++ ushort mode = glyphs[0].mode & ~ATTR_WRAP;
+ Font *font = &dc.font;
+ int frcflags = FRC_NORMAL;
+- float runewidth = win.cw;
++ float runewidth = win.cw * ((glyphs[0].mode & ATTR_WIDE) ? 2.0f : 1.0…
+ Rune rune;
+ FT_UInt glyphidx;
+ FcResult fcres;
+ FcPattern *fcpattern, *fontpattern;
+ FcFontSet *fcsets[] = { NULL };
+ FcCharSet *fccharset;
+- int i, f, numspecs = 0;
++ int f, code_idx, numspecs = 0;
++ float cluster_xp = xp, cluster_yp = yp;
++ HbTransformData shaped = { 0 };
+
+- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
+- /* Fetch rune and mode for current glyph. */
+- rune = glyphs[i].u;
+- mode = glyphs[i].mode;
++ /* Initial values. */
++ xresetfontsettings(mode, &font, &frcflags);
+
+- /* Skip dummy wide-character spacing. */
+- if (mode == ATTR_WDUMMY)
++ /* Shape the segment. */
++ hbtransform(&shaped, font->match, glyphs, 0, len);
++ xp = winx; yp = winy + font->ascent;
++ cluster_xp = xp; cluster_yp = yp;
++
++ for (code_idx = 0; code_idx < shaped.count; code_idx++) {
++ int idx = shaped.glyphs[code_idx].cluster;
++
++ if (glyphs[idx].mode & ATTR_WDUMMY)
+ continue;
+
+- /* Determine font for glyph if different from previous glyph.…
+- if (prevmode != mode) {
+- prevmode = mode;
+- font = &dc.font;
+- frcflags = FRC_NORMAL;
+- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0…
+- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
+- font = &dc.ibfont;
+- frcflags = FRC_ITALICBOLD;
+- } else if (mode & ATTR_ITALIC) {
+- font = &dc.ifont;
+- frcflags = FRC_ITALIC;
+- } else if (mode & ATTR_BOLD) {
+- font = &dc.bfont;
+- frcflags = FRC_BOLD;
+- }
+- yp = winy + font->ascent;
++ /* Advance the drawing cursor if we've moved to a new cluster…
++ if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluste…
++ xp += runewidth;
++ cluster_xp = xp;
++ cluster_yp = yp;
+ }
+
+- /* Lookup character index with default font. */
+- glyphidx = XftCharIndex(xw.dpy, font->match, rune);
+- if (glyphidx) {
++ if (shaped.glyphs[code_idx].codepoint != 0) {
++ /* If symbol is found, put it into the specs. */
+ specs[numspecs].font = font->match;
+- specs[numspecs].glyph = glyphidx;
+- specs[numspecs].x = (short)xp;
+- specs[numspecs].y = (short)yp;
+- xp += runewidth;
++ specs[numspecs].glyph = shaped.glyphs[code_idx].codep…
++ specs[numspecs].x = cluster_xp + (short)(shaped.posit…
++ specs[numspecs].y = cluster_yp - (short)(shaped.posit…
++ cluster_xp += shaped.positions[code_idx].x_advance / …
++ cluster_yp += shaped.positions[code_idx].y_advance / …
+ numspecs++;
+- continue;
+- }
+-
+- /* Fallback on font cache, search the font cache for match. */
+- for (f = 0; f < frclen; f++) {
+- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
+- /* Everything correct. */
+- if (glyphidx && frc[f].flags == frcflags)
+- break;
+- /* We got a default font for a not found glyph. */
+- if (!glyphidx && frc[f].flags == frcflags
+- && frc[f].unicodep == rune) {
+- break;
++ } else {
++ /* If it's not found, try to fetch it through the fon…
++ rune = glyphs[idx].u;
++ for (f = 0; f < frclen; f++) {
++ glyphidx = XftCharIndex(xw.dpy, frc[f].font, …
++ /* Everything correct. */
++ if (glyphidx && frc[f].flags == frcflags)
++ break;
++ /* We got a default font for a not found glyp…
++ if (!glyphidx && frc[f].flags == frcflags
++ && frc[f].unicodep == rune) {
++ break;
++ }
+ }
+- }
+
+- /* Nothing was found. Use fontconfig to find matching font. */
+- if (f >= frclen) {
+- if (!font->set)
+- font->set = FcFontSort(0, font->pattern,
+- 1, 0, &fcres);
+- fcsets[0] = font->set;
+-
+- /*
+- * Nothing was found in the cache. Now use
+- * some dozen of Fontconfig calls to get the
+- * font for one single character.
+- *
+- * Xft and fontconfig are design failures.
+- */
+- fcpattern = FcPatternDuplicate(font->pattern);
+- fccharset = FcCharSetCreate();
+-
+- FcCharSetAddChar(fccharset, rune);
+- FcPatternAddCharSet(fcpattern, FC_CHARSET,
+- fccharset);
+- FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
+-
+- FcConfigSubstitute(0, fcpattern,
+- FcMatchPattern);
+- FcDefaultSubstitute(fcpattern);
+-
+- fontpattern = FcFontSetMatch(0, fcsets, 1,
+- fcpattern, &fcres);
+-
+- /* Allocate memory for the new cache entry. */
+- if (frclen >= frccap) {
+- frccap += 16;
+- frc = xrealloc(frc, frccap * sizeof(Fontcache…
++ /* Nothing was found. Use fontconfig to find matching…
++ if (f >= frclen) {
++ if (!font->set)
++ font->set = FcFontSort(0, font->patte…
++ 1, 0, &fcres);
++ fcsets[0] = font->set;
++
++ /*
++ * Nothing was found in the cache. Now use
++ * some dozen of Fontconfig calls to get the
++ * font for one single character.
++ *
++ * Xft and fontconfig are design failures.
++ */
++ fcpattern = FcPatternDuplicate(font->pattern);
++ fccharset = FcCharSetCreate();
++
++ FcCharSetAddChar(fccharset, rune);
++ FcPatternAddCharSet(fcpattern, FC_CHARSET,
++ fccharset);
++ FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
++
++ FcConfigSubstitute(0, fcpattern,
++ FcMatchPattern);
++ FcDefaultSubstitute(fcpattern);
++
++ fontpattern = FcFontSetMatch(0, fcsets, 1,
++ fcpattern, &fcres);
++
++ /* Allocate memory for the new cache entry. */
++ if (frclen >= frccap) {
++ frccap += 16;
++ frc = xrealloc(frc, frccap * sizeof(F…
++ }
++
++ frc[frclen].font = XftFontOpenPattern(xw.dpy,
++ fontpattern);
++ if (!frc[frclen].font)
++ die("XftFontOpenPattern failed seekin…
++ strerror(errno));
++ frc[frclen].flags = frcflags;
++ frc[frclen].unicodep = rune;
++
++ glyphidx = XftCharIndex(xw.dpy, frc[frclen].f…
++
++ f = frclen;
++ frclen++;
++
++ FcPatternDestroy(fcpattern);
++ FcCharSetDestroy(fccharset);
+ }
+
+- frc[frclen].font = XftFontOpenPattern(xw.dpy,
+- fontpattern);
+- if (!frc[frclen].font)
+- die("XftFontOpenPattern failed seeking fallba…
+- strerror(errno));
+- frc[frclen].flags = frcflags;
+- frc[frclen].unicodep = rune;
+-
+- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run…
+-
+- f = frclen;
+- frclen++;
+-
+- FcPatternDestroy(fcpattern);
+- FcCharSetDestroy(fccharset);
++ specs[numspecs].font = frc[f].font;
++ specs[numspecs].glyph = glyphidx;
++ specs[numspecs].x = (short)xp;
++ specs[numspecs].y = (short)yp;
++ numspecs++;
+ }
+-
+- specs[numspecs].font = frc[f].font;
+- specs[numspecs].glyph = glyphidx;
+- specs[numspecs].x = (short)xp;
+- specs[numspecs].y = (short)yp;
+- xp += runewidth;
+- numspecs++;
+ }
+
++ /* Cleanup and get ready for next segment. */
++ hbcleanup(&shaped);
+ return numspecs;
+ }
+
+ void
+-xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
++xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
+ {
+- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
+ int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
+ width = charlen * win.cw;
+ Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
+ XRenderColor colfg, colbg;
+@@ -1528,23 +1544,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Gly…
+ void
+ xdrawglyph(Glyph g, int x, int y)
+ {
+ int numspecs;
+- XftGlyphFontSpec spec;
++ XftGlyphFontSpec *specs = xw.specbuf;
+
+- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
+- xdrawglyphfontspecs(&spec, g, numspecs, x, y);
++ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y);
++ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? …
+ }
+
+ void
+-xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
++xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int…
+ {
+ Color drawcol;
+
+ /* remove the old cursor */
+ if (selected(ox, oy))
+ og.mode ^= ATTR_REVERSE;
+- xdrawglyph(og, ox, oy);
++
++ /* Redraw the line where cursor was previously.
++ * It will restore the ligatures broken by the cursor. */
++ xdrawline(line, 0, oy, len);
+
+ if (IS_SET(MODE_HIDE))
+ return;
+
+@@ -1676,30 +1695,30 @@ xdrawline(Line line, int x1, int y1, int x2)
+ int i, x, ox, numspecs;
+ Glyph base, new;
+ XftGlyphFontSpec *specs = xw.specbuf;
+
+- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
+ i = ox = 0;
+- for (x = x1; x < x2 && i < numspecs; x++) {
++ for (x = x1; x < x2; x++) {
+ new = line[x];
+ if (new.mode == ATTR_WDUMMY)
+ continue;
+ if (selected(x, y1))
+ new.mode ^= ATTR_REVERSE;
+- if (i > 0 && ATTRCMP(base, new)) {
+- xdrawglyphfontspecs(specs, base, i, ox, y1);
+- specs += i;
+- numspecs -= i;
++ if ((i > 0) && ATTRCMP(base, new)) {
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - …
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x …
+ i = 0;
+ }
+ if (i == 0) {
+ ox = x;
+ base = new;
+ }
+ i++;
+ }
+- if (i > 0)
+- xdrawglyphfontspecs(specs, base, i, ox, y1);
++ if (i > 0) {
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,…
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox);
++ }
+ }
+
+ void
+ xfinishdraw(void)
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-boxdraw-20240…
@@ -1,647 +0,0 @@
-diff --git a/Makefile b/Makefile
-index 6dfa212..adfa07a 100644
---- a/Makefile
-+++ b/Makefile
-@@ -4,7 +4,7 @@
-
- include config.mk
-
--SRC = st.c x.c boxdraw.c
-+SRC = st.c x.c boxdraw.c hb.c
- OBJ = $(SRC:.c=.o)
-
- all: st
-@@ -22,8 +22,9 @@ config.h:
- $(CC) $(STCFLAGS) -c $<
-
- st.o: config.h st.h win.h
--x.o: arg.h config.h st.h win.h
-+x.o: arg.h config.h st.h win.h hb.h
- boxdraw.o: config.h st.h boxdraw_data.h
-+hb.o: st.h
-
- $(OBJ): config.h config.mk
-
-diff --git a/config.mk b/config.mk
-index 1e306f8..3e13e53 100644
---- a/config.mk
-+++ b/config.mk
-@@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config
- # includes and libs
- INCS = -I$(X11INC) \
- `$(PKG_CONFIG) --cflags fontconfig` \
-- `$(PKG_CONFIG) --cflags freetype2`
-+ `$(PKG_CONFIG) --cflags freetype2` \
-+ `$(PKG_CONFIG) --cflags harfbuzz`
- LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \
- `$(PKG_CONFIG) --libs fontconfig` \
-- `$(PKG_CONFIG) --libs freetype2`
-+ `$(PKG_CONFIG) --libs freetype2` \
-+ `$(PKG_CONFIG) --libs harfbuzz`
-
- # flags
- STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
-diff --git a/hb.c b/hb.c
-new file mode 100644
-index 0000000..99412c8
---- /dev/null
-+++ b/hb.c
-@@ -0,0 +1,125 @@
-+#include <stdlib.h>
-+#include <stdio.h>
-+#include <math.h>
-+#include <X11/Xft/Xft.h>
-+#include <X11/cursorfont.h>
-+#include <hb.h>
-+#include <hb-ft.h>
-+
-+#include "st.h"
-+#include "hb.h"
-+
-+#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start…
-+#define BUFFER_STEP 256
-+
-+hb_font_t *hbfindfont(XftFont *match);
-+
-+typedef struct {
-+ XftFont *match;
-+ hb_font_t *font;
-+} HbFontMatch;
-+
-+typedef struct {
-+ size_t capacity;
-+ HbFontMatch *fonts;
-+} HbFontCache;
-+
-+static HbFontCache hbfontcache = { 0, NULL };
-+
-+typedef struct {
-+ size_t capacity;
-+ Rune *runes;
-+} RuneBuffer;
-+
-+static RuneBuffer hbrunebuffer = { 0, NULL };
-+
-+/*
-+ * Poplulate the array with a list of font features, wrapped in FEATURE macro,
-+ * e. g.
-+ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g')
-+ */
-+hb_feature_t features[] = { };
-+
-+void
-+hbunloadfonts()
-+{
-+ for (int i = 0; i < hbfontcache.capacity; i++) {
-+ hb_font_destroy(hbfontcache.fonts[i].font);
-+ XftUnlockFace(hbfontcache.fonts[i].match);
-+ }
-+
-+ if (hbfontcache.fonts != NULL) {
-+ free(hbfontcache.fonts);
-+ hbfontcache.fonts = NULL;
-+ }
-+ hbfontcache.capacity = 0;
-+}
-+
-+hb_font_t *
-+hbfindfont(XftFont *match)
-+{
-+ for (int i = 0; i < hbfontcache.capacity; i++) {
-+ if (hbfontcache.fonts[i].match == match)
-+ return hbfontcache.fonts[i].font;
-+ }
-+
-+ /* Font not found in cache, caching it now. */
-+ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * …
-+ FT_Face face = XftLockFace(match);
-+ hb_font_t *font = hb_ft_font_create(face, NULL);
-+ if (font == NULL)
-+ die("Failed to load Harfbuzz font.");
-+
-+ hbfontcache.fonts[hbfontcache.capacity].match = match;
-+ hbfontcache.fonts[hbfontcache.capacity].font = font;
-+ hbfontcache.capacity += 1;
-+
-+ return font;
-+}
-+
-+void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, …
-+ ushort mode = USHRT_MAX;
-+ unsigned int glyph_count;
-+ int rune_idx, glyph_idx, end = start + length;
-+
-+ hb_font_t *font = hbfindfont(xfont);
-+ if (font == NULL)
-+ return;
-+
-+ hb_buffer_t *buffer = hb_buffer_create();
-+ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
-+ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_…
-+
-+ /* Resize the buffer if required length is larger. */
-+ if (hbrunebuffer.capacity < length) {
-+ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S…
-+ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer…
-+ }
-+
-+ /* Fill buffer with codepoints. */
-+ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r…
-+ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u;
-+ mode = glyphs[glyph_idx].mode;
-+ if (mode & ATTR_WDUMMY)
-+ hbrunebuffer.runes[rune_idx] = 0x0020;
-+ }
-+ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt…
-+
-+ /* Shape the segment. */
-+ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t…
-+
-+ /* Get new glyph info. */
-+ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun…
-+ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly…
-+
-+ /* Fill the output. */
-+ data->buffer = buffer;
-+ data->glyphs = info;
-+ data->positions = pos;
-+ data->count = glyph_count;
-+}
-+
-+void hbcleanup(HbTransformData *data) {
-+ hb_buffer_destroy(data->buffer);
-+ memset(data, 0, sizeof(HbTransformData));
-+}
-diff --git a/hb.h b/hb.h
-new file mode 100644
-index 0000000..3b0ef44
---- /dev/null
-+++ b/hb.h
-@@ -0,0 +1,14 @@
-+#include <X11/Xft/Xft.h>
-+#include <hb.h>
-+#include <hb-ft.h>
-+
-+typedef struct {
-+ hb_buffer_t *buffer;
-+ hb_glyph_info_t *glyphs;
-+ hb_glyph_position_t *positions;
-+ unsigned int count;
-+} HbTransformData;
-+
-+void hbunloadfonts();
-+void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
-+void hbcleanup(HbTransformData *);
-diff --git a/st.c b/st.c
-index 41d5ace..1c2edd6 100644
---- a/st.c
-+++ b/st.c
-@@ -2643,7 +2643,8 @@ draw(void)
-
- drawregion(0, 0, term.col, term.row);
- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
-- term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
-+ term.ocx, term.ocy, term.line[term.ocy][term.ocx],
-+ term.line[term.ocy], term.col);
- term.ocx = cx;
- term.ocy = term.c.y;
- xfinishdraw();
-diff --git a/st.h b/st.h
-index 808f5f7..ae41368 100644
---- a/st.h
-+++ b/st.h
-@@ -11,7 +11,8 @@
- #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
- #define DEFAULT(a, b) (a) = (a) ? (a) : (b)
- #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)…
--#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f…
-+#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode …
-+ (a).fg != (b).fg || \
- (a).bg != (b).bg)
- #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
- (t1.tv_nsec-t2.tv_nsec)/1E6)
-diff --git a/win.h b/win.h
-index 6de960d..94679e4 100644
---- a/win.h
-+++ b/win.h
-@@ -25,7 +25,7 @@ enum win_mode {
-
- void xbell(void);
- void xclipcopy(void);
--void xdrawcursor(int, int, Glyph, int, int, Glyph);
-+void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int);
- void xdrawline(Line, int, int, int);
- void xfinishdraw(void);
- void xloadcols(void);
-diff --git a/x.c b/x.c
-index bf6bbf9..96b117f 100644
---- a/x.c
-+++ b/x.c
-@@ -19,6 +19,7 @@ char *argv0;
- #include "arg.h"
- #include "st.h"
- #include "win.h"
-+#include "hb.h"
-
- /* types used in config.h */
- typedef struct {
-@@ -141,8 +142,9 @@ typedef struct {
- } DC;
-
- static inline ushort sixd_to_16bit(int);
-+static void xresetfontsettings(ushort mode, Font **font, int *frcflags);
- static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i…
--static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
-+static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
- static void xdrawglyph(Glyph, int, int);
- static void xclear(int, int, int, int);
- static int xgeommasktogravity(int);
-@@ -757,7 +759,7 @@ xresize(int col, int row)
- xclear(0, 0, win.w, win.h);
-
- /* resize to new width */
-- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
-+ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
- }
-
- ushort
-@@ -1062,6 +1064,9 @@ xunloadfont(Font *f)
- void
- xunloadfonts(void)
- {
-+ /* Clear Harfbuzz font cache. */
-+ hbunloadfonts();
-+
- /* Free the loaded fonts in the font cache. */
- while (frclen > 0)
- XftFontClose(xw.dpy, frc[--frclen].font);
-@@ -1185,7 +1190,7 @@ xinit(int cols, int rows)
- XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
-
- /* font spec buffer */
-- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
-+ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
-
- /* Xft rendering context */
- xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
-@@ -1241,6 +1246,22 @@ xinit(int cols, int rows)
- boxdraw_xinit(xw.dpy, xw.cmap, xw.draw, xw.vis);
- }
-
-+void
-+xresetfontsettings(ushort mode, Font **font, int *frcflags)
-+{
-+ *font = &dc.font;
-+ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
-+ *font = &dc.ibfont;
-+ *frcflags = FRC_ITALICBOLD;
-+ } else if (mode & ATTR_ITALIC) {
-+ *font = &dc.ifont;
-+ *frcflags = FRC_ITALIC;
-+ } else if (mode & ATTR_BOLD) {
-+ *font = &dc.bfont;
-+ *frcflags = FRC_BOLD;
-+ }
-+}
-+
- int
- xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in…
- {
-@@ -1255,133 +1276,164 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const G…
- FcPattern *fcpattern, *fontpattern;
- FcFontSet *fcsets[] = { NULL };
- FcCharSet *fccharset;
-- int i, f, numspecs = 0;
-+ int i, f, length = 0, start = 0, numspecs = 0;
-+ float cluster_xp = xp, cluster_yp = yp;
-+ HbTransformData shaped = { 0 };
-+
-+ /* Initial values. */
-+ mode = prevmode = glyphs[0].mode & ~ATTR_WRAP;
-+ xresetfontsettings(mode, &font, &frcflags);
-
- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
-- /* Fetch rune and mode for current glyph. */
-- rune = glyphs[i].u;
-- mode = glyphs[i].mode;
-+ mode = glyphs[i].mode & ~ATTR_WRAP;
-
- /* Skip dummy wide-character spacing. */
-- if (mode == ATTR_WDUMMY)
-+ if (mode & ATTR_WDUMMY && i < (len - 1))
- continue;
-
-- /* Determine font for glyph if different from previous glyph.…
-- if (prevmode != mode) {
-- prevmode = mode;
-- font = &dc.font;
-- frcflags = FRC_NORMAL;
-- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0…
-- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
-- font = &dc.ibfont;
-- frcflags = FRC_ITALICBOLD;
-- } else if (mode & ATTR_ITALIC) {
-- font = &dc.ifont;
-- frcflags = FRC_ITALIC;
-- } else if (mode & ATTR_BOLD) {
-- font = &dc.bfont;
-- frcflags = FRC_BOLD;
-+ if (
-+ prevmode != mode
-+ || ATTRCMP(glyphs[start], glyphs[i])
-+ || selected(x + i, y) != selected(x + start, y)
-+ || i == (len - 1)
-+ ) {
-+ /* Handle 1-character wide segments and end of line */
-+ length = i - start;
-+ if (i == start) {
-+ length = 1;
-+ } else if (i == (len - 1)) {
-+ length = (i - start + 1);
- }
-- yp = winy + font->ascent;
-- }
-
-- if (mode & ATTR_BOXDRAW) {
-- /* minor shoehorning: boxdraw uses only this ushort */
-- glyphidx = boxdrawindex(&glyphs[i]);
-- } else {
-- /* Lookup character index with default font. */
-- glyphidx = XftCharIndex(xw.dpy, font->match, rune);
-- }
-- if (glyphidx) {
-- specs[numspecs].font = font->match;
-- specs[numspecs].glyph = glyphidx;
-- specs[numspecs].x = (short)xp;
-- specs[numspecs].y = (short)yp;
-- xp += runewidth;
-- numspecs++;
-- continue;
-- }
--
-- /* Fallback on font cache, search the font cache for match. */
-- for (f = 0; f < frclen; f++) {
-- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
-- /* Everything correct. */
-- if (glyphidx && frc[f].flags == frcflags)
-- break;
-- /* We got a default font for a not found glyph. */
-- if (!glyphidx && frc[f].flags == frcflags
-- && frc[f].unicodep == rune) {
-- break;
-+ /* Shape the segment. */
-+ hbtransform(&shaped, font->match, glyphs, start, leng…
-+ runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE…
-+ cluster_xp = xp; cluster_yp = yp;
-+ for (int code_idx = 0; code_idx < shaped.count; code_…
-+ int idx = shaped.glyphs[code_idx].cluster;
-+
-+ if (glyphs[start + idx].mode & ATTR_WDUMMY)
-+ continue;
-+
-+ /* Advance the drawing cursor if we've moved …
-+ if (code_idx > 0 && idx != shaped.glyphs[code…
-+ xp += runewidth;
-+ cluster_xp = xp;
-+ cluster_yp = yp;
-+ runewidth = win.cw * ((glyphs[start +…
-+ }
-+
-+ if (glyphs[start + idx].mode & ATTR_BOXDRAW) {
-+ /* minor shoehorning: boxdraw uses on…
-+ specs[numspecs].font = font->match;
-+ specs[numspecs].glyph = boxdrawindex(…
-+ specs[numspecs].x = xp;
-+ specs[numspecs].y = yp;
-+ numspecs++;
-+ } else if (shaped.glyphs[code_idx].codepoint …
-+ /* If symbol is found, put it into th…
-+ specs[numspecs].font = font->match;
-+ specs[numspecs].glyph = shaped.glyphs…
-+ specs[numspecs].x = cluster_xp + (sho…
-+ specs[numspecs].y = cluster_yp - (sho…
-+ cluster_xp += shaped.positions[code_i…
-+ cluster_yp += shaped.positions[code_i…
-+ numspecs++;
-+ } else {
-+ /* If it's not found, try to fetch it…
-+ rune = glyphs[start + idx].u;
-+ for (f = 0; f < frclen; f++) {
-+ glyphidx = XftCharIndex(xw.dp…
-+ /* Everything correct. */
-+ if (glyphidx && frc[f].flags …
-+ break;
-+ /* We got a default font for …
-+ if (!glyphidx && frc[f].flags…
-+ && frc[f].uni…
-+ break;
-+ }
-+ }
-+
-+ /* Nothing was found. Use fontconfig …
-+ if (f >= frclen) {
-+ if (!font->set)
-+ font->set = FcFontSor…
-+ …
-+ fcsets[0] = font->set;
-+
-+ /*
-+ * Nothing was found in the c…
-+ * some dozen of Fontconfig c…
-+ * font for one single charac…
-+ *
-+ * Xft and fontconfig are des…
-+ */
-+ fcpattern = FcPatternDuplicat…
-+ fccharset = FcCharSetCreate();
-+
-+ FcCharSetAddChar(fccharset, r…
-+ FcPatternAddCharSet(fcpattern…
-+ fccharset);
-+ FcPatternAddBool(fcpattern, F…
-+
-+ FcConfigSubstitute(0, fcpatte…
-+ FcMatchPatter…
-+ FcDefaultSubstitute(fcpattern…
-+
-+ fontpattern = FcFontSetMatch(…
-+ fcpattern, &f…
-+
-+ /* Allocate memory for the ne…
-+ if (frclen >= frccap) {
-+ frccap += 16;
-+ frc = xrealloc(frc, f…
-+ }
-+
-+ frc[frclen].font = XftFontOpe…
-+ fontpattern);
-+ if (!frc[frclen].font)
-+ die("XftFontOpenPatte…
-+ strerror(errn…
-+ frc[frclen].flags = frcflags;
-+ frc[frclen].unicodep = rune;
-+
-+ glyphidx = XftCharIndex(xw.dp…
-+
-+ f = frclen;
-+ frclen++;
-+
-+ FcPatternDestroy(fcpattern);
-+ FcCharSetDestroy(fccharset);
-+ }
-+
-+ specs[numspecs].font = frc[f].font;
-+ specs[numspecs].glyph = glyphidx;
-+ specs[numspecs].x = (short)xp;
-+ specs[numspecs].y = (short)yp;
-+ numspecs++;
-+ }
- }
-- }
-
-- /* Nothing was found. Use fontconfig to find matching font. */
-- if (f >= frclen) {
-- if (!font->set)
-- font->set = FcFontSort(0, font->pattern,
-- 1, 0, &fcres);
-- fcsets[0] = font->set;
-+ /* Cleanup and get ready for next segment. */
-+ hbcleanup(&shaped);
-+ start = i;
-
-- /*
-- * Nothing was found in the cache. Now use
-- * some dozen of Fontconfig calls to get the
-- * font for one single character.
-- *
-- * Xft and fontconfig are design failures.
-- */
-- fcpattern = FcPatternDuplicate(font->pattern);
-- fccharset = FcCharSetCreate();
--
-- FcCharSetAddChar(fccharset, rune);
-- FcPatternAddCharSet(fcpattern, FC_CHARSET,
-- fccharset);
-- FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
--
-- FcConfigSubstitute(0, fcpattern,
-- FcMatchPattern);
-- FcDefaultSubstitute(fcpattern);
--
-- fontpattern = FcFontSetMatch(0, fcsets, 1,
-- fcpattern, &fcres);
--
-- /* Allocate memory for the new cache entry. */
-- if (frclen >= frccap) {
-- frccap += 16;
-- frc = xrealloc(frc, frccap * sizeof(Fontcache…
-+ /* Determine font for glyph if different from previou…
-+ if (prevmode != mode) {
-+ prevmode = mode;
-+ xresetfontsettings(mode, &font, &frcflags);
-+ yp = winy + font->ascent;
- }
--
-- frc[frclen].font = XftFontOpenPattern(xw.dpy,
-- fontpattern);
-- if (!frc[frclen].font)
-- die("XftFontOpenPattern failed seeking fallba…
-- strerror(errno));
-- frc[frclen].flags = frcflags;
-- frc[frclen].unicodep = rune;
--
-- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run…
--
-- f = frclen;
-- frclen++;
--
-- FcPatternDestroy(fcpattern);
-- FcCharSetDestroy(fccharset);
- }
--
-- specs[numspecs].font = frc[f].font;
-- specs[numspecs].glyph = glyphidx;
-- specs[numspecs].x = (short)xp;
-- specs[numspecs].y = (short)yp;
-- xp += runewidth;
-- numspecs++;
- }
-
-+ hbcleanup(&shaped);
- return numspecs;
- }
-
- void
--xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
-+xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
- {
-- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
- int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
- width = charlen * win.cw;
- Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
-@@ -1521,21 +1573,24 @@ void
- xdrawglyph(Glyph g, int x, int y)
- {
- int numspecs;
-- XftGlyphFontSpec spec;
-+ XftGlyphFontSpec *specs = xw.specbuf;
-
-- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
-- xdrawglyphfontspecs(&spec, g, numspecs, x, y);
-+ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y);
-+ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? …
- }
-
- void
--xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
-+xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int…
- {
- Color drawcol;
-
- /* remove the old cursor */
- if (selected(ox, oy))
- og.mode ^= ATTR_REVERSE;
-- xdrawglyph(og, ox, oy);
-+
-+ /* Redraw the line where cursor was previously.
-+ * It will restore the ligatures broken by the cursor. */
-+ xdrawline(line, 0, oy, len);
-
- if (IS_SET(MODE_HIDE))
- return;
-@@ -1663,18 +1718,16 @@ xdrawline(Line line, int x1, int y1, int x2)
- Glyph base, new;
- XftGlyphFontSpec *specs = xw.specbuf;
-
-- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
- i = ox = 0;
-- for (x = x1; x < x2 && i < numspecs; x++) {
-+ for (x = x1; x < x2; x++) {
- new = line[x];
- if (new.mode == ATTR_WDUMMY)
- continue;
- if (selected(x, y1))
- new.mode ^= ATTR_REVERSE;
-- if (i > 0 && ATTRCMP(base, new)) {
-- xdrawglyphfontspecs(specs, base, i, ox, y1);
-- specs += i;
-- numspecs -= i;
-+ if ((i > 0) && ATTRCMP(base, new)) {
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - …
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x …
- i = 0;
- }
- if (i == 0) {
-@@ -1683,8 +1736,10 @@ xdrawline(Line line, int x1, int y1, int x2)
- }
- i++;
- }
-- if (i > 0)
-- xdrawglyphfontspecs(specs, base, i, ox, y1);
-+ if (i > 0) {
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,…
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox);
-+ }
- }
-
- void
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-boxdraw-20241…
@@ -0,0 +1,662 @@
+diff --git a/Makefile b/Makefile
+index a64b4c2..05124bf 100644
+--- a/Makefile
++++ b/Makefile
+@@ -3,9 +3,9 @@
+ .POSIX:
+
+ include config.mk
+
+-SRC = st.c x.c boxdraw.c
++SRC = st.c x.c boxdraw.c hb.c
+ OBJ = $(SRC:.c=.o)
+
+ all: st
+
+@@ -15,10 +15,11 @@ config.h:
+ .c.o:
+ $(CC) $(STCFLAGS) -c $<
+
+ st.o: config.h st.h win.h
+-x.o: arg.h config.h st.h win.h
++x.o: arg.h config.h st.h win.h hb.h
+ boxdraw.o: config.h st.h boxdraw_data.h
++hb.o: st.h
+
+ $(OBJ): config.h config.mk
+
+ st: $(OBJ)
+diff --git a/config.mk b/config.mk
+index fdc29a7..6833b3b 100644
+--- a/config.mk
++++ b/config.mk
+@@ -14,12 +14,14 @@ PKG_CONFIG = pkg-config
+
+ # includes and libs
+ INCS = -I$(X11INC) \
+ `$(PKG_CONFIG) --cflags fontconfig` \
+- `$(PKG_CONFIG) --cflags freetype2`
++ `$(PKG_CONFIG) --cflags freetype2` \
++ `$(PKG_CONFIG) --cflags harfbuzz`
+ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \
+ `$(PKG_CONFIG) --libs fontconfig` \
+- `$(PKG_CONFIG) --libs freetype2`
++ `$(PKG_CONFIG) --libs freetype2` \
++ `$(PKG_CONFIG) --libs harfbuzz`
+
+ # flags
+ STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
+ STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS)
+@@ -28,9 +30,10 @@ STLDFLAGS = $(LIBS) $(LDFLAGS)
+ # OpenBSD:
+ #CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE
+ #LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \
+ # `$(PKG_CONFIG) --libs fontconfig` \
+-# `$(PKG_CONFIG) --libs freetype2`
++# `$(PKG_CONFIG) --libs freetype2` \
++# `$(PKG_CONFIG) --libs harfbuzz`
+ #MANPREFIX = ${PREFIX}/man
+
+ # compiler and linker
+ # CC = c99
+diff --git a/hb.c b/hb.c
+new file mode 100644
+index 0000000..99412c8
+--- /dev/null
++++ b/hb.c
+@@ -0,0 +1,125 @@
++#include <stdlib.h>
++#include <stdio.h>
++#include <math.h>
++#include <X11/Xft/Xft.h>
++#include <X11/cursorfont.h>
++#include <hb.h>
++#include <hb-ft.h>
++
++#include "st.h"
++#include "hb.h"
++
++#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start…
++#define BUFFER_STEP 256
++
++hb_font_t *hbfindfont(XftFont *match);
++
++typedef struct {
++ XftFont *match;
++ hb_font_t *font;
++} HbFontMatch;
++
++typedef struct {
++ size_t capacity;
++ HbFontMatch *fonts;
++} HbFontCache;
++
++static HbFontCache hbfontcache = { 0, NULL };
++
++typedef struct {
++ size_t capacity;
++ Rune *runes;
++} RuneBuffer;
++
++static RuneBuffer hbrunebuffer = { 0, NULL };
++
++/*
++ * Poplulate the array with a list of font features, wrapped in FEATURE macro,
++ * e. g.
++ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g')
++ */
++hb_feature_t features[] = { };
++
++void
++hbunloadfonts()
++{
++ for (int i = 0; i < hbfontcache.capacity; i++) {
++ hb_font_destroy(hbfontcache.fonts[i].font);
++ XftUnlockFace(hbfontcache.fonts[i].match);
++ }
++
++ if (hbfontcache.fonts != NULL) {
++ free(hbfontcache.fonts);
++ hbfontcache.fonts = NULL;
++ }
++ hbfontcache.capacity = 0;
++}
++
++hb_font_t *
++hbfindfont(XftFont *match)
++{
++ for (int i = 0; i < hbfontcache.capacity; i++) {
++ if (hbfontcache.fonts[i].match == match)
++ return hbfontcache.fonts[i].font;
++ }
++
++ /* Font not found in cache, caching it now. */
++ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * …
++ FT_Face face = XftLockFace(match);
++ hb_font_t *font = hb_ft_font_create(face, NULL);
++ if (font == NULL)
++ die("Failed to load Harfbuzz font.");
++
++ hbfontcache.fonts[hbfontcache.capacity].match = match;
++ hbfontcache.fonts[hbfontcache.capacity].font = font;
++ hbfontcache.capacity += 1;
++
++ return font;
++}
++
++void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, …
++ ushort mode = USHRT_MAX;
++ unsigned int glyph_count;
++ int rune_idx, glyph_idx, end = start + length;
++
++ hb_font_t *font = hbfindfont(xfont);
++ if (font == NULL)
++ return;
++
++ hb_buffer_t *buffer = hb_buffer_create();
++ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
++ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_…
++
++ /* Resize the buffer if required length is larger. */
++ if (hbrunebuffer.capacity < length) {
++ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S…
++ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer…
++ }
++
++ /* Fill buffer with codepoints. */
++ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r…
++ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u;
++ mode = glyphs[glyph_idx].mode;
++ if (mode & ATTR_WDUMMY)
++ hbrunebuffer.runes[rune_idx] = 0x0020;
++ }
++ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt…
++
++ /* Shape the segment. */
++ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t…
++
++ /* Get new glyph info. */
++ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun…
++ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly…
++
++ /* Fill the output. */
++ data->buffer = buffer;
++ data->glyphs = info;
++ data->positions = pos;
++ data->count = glyph_count;
++}
++
++void hbcleanup(HbTransformData *data) {
++ hb_buffer_destroy(data->buffer);
++ memset(data, 0, sizeof(HbTransformData));
++}
+diff --git a/hb.h b/hb.h
+new file mode 100644
+index 0000000..3b0ef44
+--- /dev/null
++++ b/hb.h
+@@ -0,0 +1,14 @@
++#include <X11/Xft/Xft.h>
++#include <hb.h>
++#include <hb-ft.h>
++
++typedef struct {
++ hb_buffer_t *buffer;
++ hb_glyph_info_t *glyphs;
++ hb_glyph_position_t *positions;
++ unsigned int count;
++} HbTransformData;
++
++void hbunloadfonts();
++void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
++void hbcleanup(HbTransformData *);
+diff --git a/st.c b/st.c
+index ec6fbf3..1385f77 100644
+--- a/st.c
++++ b/st.c
+@@ -2661,9 +2661,10 @@ draw(void)
+ cx--;
+
+ drawregion(0, 0, term.col, term.row);
+ xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
+- term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
++ term.ocx, term.ocy, term.line[term.ocy][term.ocx],
++ term.line[term.ocy], term.col);
+ term.ocx = cx;
+ term.ocy = term.c.y;
+ xfinishdraw();
+ if (ocx != term.ocx || ocy != term.ocy)
+diff --git a/st.h b/st.h
+index 808f5f7..ae41368 100644
+--- a/st.h
++++ b/st.h
+@@ -10,9 +10,10 @@
+ #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
+ #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
+ #define DEFAULT(a, b) (a) = (a) ? (a) : (b)
+ #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)…
+-#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f…
++#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode …
++ (a).fg != (b).fg || \
+ (a).bg != (b).bg)
+ #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
+ (t1.tv_nsec-t2.tv_nsec)/1E6)
+ #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
+diff --git a/win.h b/win.h
+index 6de960d..94679e4 100644
+--- a/win.h
++++ b/win.h
+@@ -24,9 +24,9 @@ enum win_mode {
+ };
+
+ void xbell(void);
+ void xclipcopy(void);
+-void xdrawcursor(int, int, Glyph, int, int, Glyph);
++void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int);
+ void xdrawline(Line, int, int, int);
+ void xfinishdraw(void);
+ void xloadcols(void);
+ int xsetcolorname(int, const char *);
+diff --git a/x.c b/x.c
+index 978a8fc..c2d9993 100644
+--- a/x.c
++++ b/x.c
+@@ -18,8 +18,9 @@
+ char *argv0;
+ #include "arg.h"
+ #include "st.h"
+ #include "win.h"
++#include "hb.h"
+
+ /* types used in config.h */
+ typedef struct {
+ uint mod;
+@@ -140,10 +141,11 @@ typedef struct {
+ GC gc;
+ } DC;
+
+ static inline ushort sixd_to_16bit(int);
++static void xresetfontsettings(ushort mode, Font **font, int *frcflags);
+ static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i…
+-static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
++static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
+ static void xdrawglyph(Glyph, int, int);
+ static void xclear(int, int, int, int);
+ static int xgeommasktogravity(int);
+ static int ximopen(Display *);
+@@ -756,9 +758,9 @@ xresize(int col, int row)
+ XftDrawChange(xw.draw, xw.buf);
+ xclear(0, 0, win.w, win.h);
+
+ /* resize to new width */
+- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
++ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
+ }
+
+ ushort
+ sixd_to_16bit(int x)
+@@ -1061,8 +1063,11 @@ xunloadfont(Font *f)
+
+ void
+ xunloadfonts(void)
+ {
++ /* Clear Harfbuzz font cache. */
++ hbunloadfonts();
++
+ /* Free the loaded fonts in the font cache. */
+ while (frclen > 0)
+ XftFontClose(xw.dpy, frc[--frclen].font);
+
+@@ -1184,9 +1189,9 @@ xinit(int cols, int rows)
+ XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel);
+ XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
+
+ /* font spec buffer */
+- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
++ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
+
+ /* Xft rendering context */
+ xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
+
+@@ -1240,149 +1245,162 @@ xinit(int cols, int rows)
+
+ boxdraw_xinit(xw.dpy, xw.cmap, xw.draw, xw.vis);
+ }
+
++void
++xresetfontsettings(ushort mode, Font **font, int *frcflags)
++{
++ *font = &dc.font;
++ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
++ *font = &dc.ibfont;
++ *frcflags = FRC_ITALICBOLD;
++ } else if (mode & ATTR_ITALIC) {
++ *font = &dc.ifont;
++ *frcflags = FRC_ITALIC;
++ } else if (mode & ATTR_BOLD) {
++ *font = &dc.bfont;
++ *frcflags = FRC_BOLD;
++ }
++}
++
+ int
+ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in…
+ {
+ float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp,…
+- ushort mode, prevmode = USHRT_MAX;
++ ushort mode = glyphs[0].mode & ~ATTR_WRAP;
+ Font *font = &dc.font;
+ int frcflags = FRC_NORMAL;
+- float runewidth = win.cw;
++ float runewidth = win.cw * ((glyphs[0].mode & ATTR_WIDE) ? 2.0f : 1.0…
+ Rune rune;
+ FT_UInt glyphidx;
+ FcResult fcres;
+ FcPattern *fcpattern, *fontpattern;
+ FcFontSet *fcsets[] = { NULL };
+ FcCharSet *fccharset;
+- int i, f, numspecs = 0;
++ int f, code_idx, numspecs = 0;
++ float cluster_xp = xp, cluster_yp = yp;
++ HbTransformData shaped = { 0 };
+
+- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
+- /* Fetch rune and mode for current glyph. */
+- rune = glyphs[i].u;
+- mode = glyphs[i].mode;
++ /* Initial values. */
++ xresetfontsettings(mode, &font, &frcflags);
+
+- /* Skip dummy wide-character spacing. */
+- if (mode == ATTR_WDUMMY)
++ /* Shape the segment. */
++ hbtransform(&shaped, font->match, glyphs, 0, len);
++ xp = winx; yp = winy + font->ascent;
++ cluster_xp = xp; cluster_yp = yp;
++
++ for (code_idx = 0; code_idx < shaped.count; code_idx++) {
++ int idx = shaped.glyphs[code_idx].cluster;
++
++ if (glyphs[idx].mode & ATTR_WDUMMY)
+ continue;
+
+- /* Determine font for glyph if different from previous glyph.…
+- if (prevmode != mode) {
+- prevmode = mode;
+- font = &dc.font;
+- frcflags = FRC_NORMAL;
+- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0…
+- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
+- font = &dc.ibfont;
+- frcflags = FRC_ITALICBOLD;
+- } else if (mode & ATTR_ITALIC) {
+- font = &dc.ifont;
+- frcflags = FRC_ITALIC;
+- } else if (mode & ATTR_BOLD) {
+- font = &dc.bfont;
+- frcflags = FRC_BOLD;
+- }
+- yp = winy + font->ascent;
++ /* Advance the drawing cursor if we've moved to a new cluster…
++ if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluste…
++ xp += runewidth;
++ cluster_xp = xp;
++ cluster_yp = yp;
+ }
+
+- if (mode & ATTR_BOXDRAW) {
++ if (glyphs[idx].mode & ATTR_BOXDRAW) {
+ /* minor shoehorning: boxdraw uses only this ushort */
+- glyphidx = boxdrawindex(&glyphs[i]);
+- } else {
+- /* Lookup character index with default font. */
+- glyphidx = XftCharIndex(xw.dpy, font->match, rune);
+- }
+- if (glyphidx) {
+ specs[numspecs].font = font->match;
+- specs[numspecs].glyph = glyphidx;
+- specs[numspecs].x = (short)xp;
+- specs[numspecs].y = (short)yp;
+- xp += runewidth;
++ specs[numspecs].glyph = boxdrawindex(&glyphs[idx]);
++ specs[numspecs].x = xp;
++ specs[numspecs].y = yp;
+ numspecs++;
+- continue;
+- }
+-
+- /* Fallback on font cache, search the font cache for match. */
+- for (f = 0; f < frclen; f++) {
+- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
+- /* Everything correct. */
+- if (glyphidx && frc[f].flags == frcflags)
+- break;
+- /* We got a default font for a not found glyph. */
+- if (!glyphidx && frc[f].flags == frcflags
+- && frc[f].unicodep == rune) {
+- break;
++ } else if (shaped.glyphs[code_idx].codepoint != 0) {
++ /* If symbol is found, put it into the specs. */
++ specs[numspecs].font = font->match;
++ specs[numspecs].glyph = shaped.glyphs[code_idx].codep…
++ specs[numspecs].x = cluster_xp + (short)(shaped.posit…
++ specs[numspecs].y = cluster_yp - (short)(shaped.posit…
++ cluster_xp += shaped.positions[code_idx].x_advance / …
++ cluster_yp += shaped.positions[code_idx].y_advance / …
++ numspecs++;
++ } else {
++ /* If it's not found, try to fetch it through the fon…
++ rune = glyphs[idx].u;
++ for (f = 0; f < frclen; f++) {
++ glyphidx = XftCharIndex(xw.dpy, frc[f].font, …
++ /* Everything correct. */
++ if (glyphidx && frc[f].flags == frcflags)
++ break;
++ /* We got a default font for a not found glyp…
++ if (!glyphidx && frc[f].flags == frcflags
++ && frc[f].unicodep == rune) {
++ break;
++ }
+ }
+- }
+-
+- /* Nothing was found. Use fontconfig to find matching font. */
+- if (f >= frclen) {
+- if (!font->set)
+- font->set = FcFontSort(0, font->pattern,
+- 1, 0, &fcres);
+- fcsets[0] = font->set;
+-
+- /*
+- * Nothing was found in the cache. Now use
+- * some dozen of Fontconfig calls to get the
+- * font for one single character.
+- *
+- * Xft and fontconfig are design failures.
+- */
+- fcpattern = FcPatternDuplicate(font->pattern);
+- fccharset = FcCharSetCreate();
+-
+- FcCharSetAddChar(fccharset, rune);
+- FcPatternAddCharSet(fcpattern, FC_CHARSET,
+- fccharset);
+- FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
+-
+- FcConfigSubstitute(0, fcpattern,
+- FcMatchPattern);
+- FcDefaultSubstitute(fcpattern);
+
+- fontpattern = FcFontSetMatch(0, fcsets, 1,
+- fcpattern, &fcres);
+-
+- /* Allocate memory for the new cache entry. */
+- if (frclen >= frccap) {
+- frccap += 16;
+- frc = xrealloc(frc, frccap * sizeof(Fontcache…
++ /* Nothing was found. Use fontconfig to find matching…
++ if (f >= frclen) {
++ if (!font->set)
++ font->set = FcFontSort(0, font->patte…
++ 1, 0, &fcres);
++ fcsets[0] = font->set;
++
++ /*
++ * Nothing was found in the cache. Now use
++ * some dozen of Fontconfig calls to get the
++ * font for one single character.
++ *
++ * Xft and fontconfig are design failures.
++ */
++ fcpattern = FcPatternDuplicate(font->pattern);
++ fccharset = FcCharSetCreate();
++
++ FcCharSetAddChar(fccharset, rune);
++ FcPatternAddCharSet(fcpattern, FC_CHARSET,
++ fccharset);
++ FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
++
++ FcConfigSubstitute(0, fcpattern,
++ FcMatchPattern);
++ FcDefaultSubstitute(fcpattern);
++
++ fontpattern = FcFontSetMatch(0, fcsets, 1,
++ fcpattern, &fcres);
++
++ /* Allocate memory for the new cache entry. */
++ if (frclen >= frccap) {
++ frccap += 16;
++ frc = xrealloc(frc, frccap * sizeof(F…
++ }
++
++ frc[frclen].font = XftFontOpenPattern(xw.dpy,
++ fontpattern);
++ if (!frc[frclen].font)
++ die("XftFontOpenPattern failed seekin…
++ strerror(errno));
++ frc[frclen].flags = frcflags;
++ frc[frclen].unicodep = rune;
++
++ glyphidx = XftCharIndex(xw.dpy, frc[frclen].f…
++
++ f = frclen;
++ frclen++;
++
++ FcPatternDestroy(fcpattern);
++ FcCharSetDestroy(fccharset);
+ }
+
+- frc[frclen].font = XftFontOpenPattern(xw.dpy,
+- fontpattern);
+- if (!frc[frclen].font)
+- die("XftFontOpenPattern failed seeking fallba…
+- strerror(errno));
+- frc[frclen].flags = frcflags;
+- frc[frclen].unicodep = rune;
+-
+- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run…
+-
+- f = frclen;
+- frclen++;
+-
+- FcPatternDestroy(fcpattern);
+- FcCharSetDestroy(fccharset);
++ specs[numspecs].font = frc[f].font;
++ specs[numspecs].glyph = glyphidx;
++ specs[numspecs].x = (short)xp;
++ specs[numspecs].y = (short)yp;
++ numspecs++;
+ }
+-
+- specs[numspecs].font = frc[f].font;
+- specs[numspecs].glyph = glyphidx;
+- specs[numspecs].x = (short)xp;
+- specs[numspecs].y = (short)yp;
+- xp += runewidth;
+- numspecs++;
+ }
+
++ /* Cleanup and get ready for next segment. */
++ hbcleanup(&shaped);
+ return numspecs;
+ }
+
+ void
+-xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
++xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
+ {
+- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
+ int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
+ width = charlen * win.cw;
+ Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
+ XRenderColor colfg, colbg;
+@@ -1520,23 +1538,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Gly…
+ void
+ xdrawglyph(Glyph g, int x, int y)
+ {
+ int numspecs;
+- XftGlyphFontSpec spec;
++ XftGlyphFontSpec *specs = xw.specbuf;
+
+- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
+- xdrawglyphfontspecs(&spec, g, numspecs, x, y);
++ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y);
++ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? …
+ }
+
+ void
+-xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
++xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int…
+ {
+ Color drawcol;
+
+ /* remove the old cursor */
+ if (selected(ox, oy))
+ og.mode ^= ATTR_REVERSE;
+- xdrawglyph(og, ox, oy);
++
++ /* Redraw the line where cursor was previously.
++ * It will restore the ligatures broken by the cursor. */
++ xdrawline(line, 0, oy, len);
+
+ if (IS_SET(MODE_HIDE))
+ return;
+
+@@ -1668,30 +1689,30 @@ xdrawline(Line line, int x1, int y1, int x2)
+ int i, x, ox, numspecs;
+ Glyph base, new;
+ XftGlyphFontSpec *specs = xw.specbuf;
+
+- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
+ i = ox = 0;
+- for (x = x1; x < x2 && i < numspecs; x++) {
++ for (x = x1; x < x2; x++) {
+ new = line[x];
+ if (new.mode == ATTR_WDUMMY)
+ continue;
+ if (selected(x, y1))
+ new.mode ^= ATTR_REVERSE;
+- if (i > 0 && ATTRCMP(base, new)) {
+- xdrawglyphfontspecs(specs, base, i, ox, y1);
+- specs += i;
+- numspecs -= i;
++ if ((i > 0) && ATTRCMP(base, new)) {
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - …
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x …
+ i = 0;
+ }
+ if (i == 0) {
+ ox = x;
+ base = new;
+ }
+ i++;
+ }
+- if (i > 0)
+- xdrawglyphfontspecs(specs, base, i, ox, y1);
++ if (i > 0) {
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,…
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox);
++ }
+ }
+
+ void
+ xfinishdraw(void)
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-scrollback-20…
@@ -1,634 +0,0 @@
-diff --git a/Makefile b/Makefile
-index 470ac86..38240da 100644
---- a/Makefile
-+++ b/Makefile
-@@ -4,7 +4,7 @@
-
- include config.mk
-
--SRC = st.c x.c
-+SRC = st.c x.c hb.c
- OBJ = $(SRC:.c=.o)
-
- all: st
-@@ -22,7 +22,8 @@ config.h:
- $(CC) $(STCFLAGS) -c $<
-
- st.o: config.h st.h win.h
--x.o: arg.h config.h st.h win.h
-+x.o: arg.h config.h st.h win.h hb.h
-+hb.o: st.h
-
- $(OBJ): config.h config.mk
-
-diff --git a/config.mk b/config.mk
-index 1e306f8..3e13e53 100644
---- a/config.mk
-+++ b/config.mk
-@@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config
- # includes and libs
- INCS = -I$(X11INC) \
- `$(PKG_CONFIG) --cflags fontconfig` \
-- `$(PKG_CONFIG) --cflags freetype2`
-+ `$(PKG_CONFIG) --cflags freetype2` \
-+ `$(PKG_CONFIG) --cflags harfbuzz`
- LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \
- `$(PKG_CONFIG) --libs fontconfig` \
-- `$(PKG_CONFIG) --libs freetype2`
-+ `$(PKG_CONFIG) --libs freetype2` \
-+ `$(PKG_CONFIG) --libs harfbuzz`
-
- # flags
- STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
-diff --git a/hb.c b/hb.c
-new file mode 100644
-index 0000000..99412c8
---- /dev/null
-+++ b/hb.c
-@@ -0,0 +1,125 @@
-+#include <stdlib.h>
-+#include <stdio.h>
-+#include <math.h>
-+#include <X11/Xft/Xft.h>
-+#include <X11/cursorfont.h>
-+#include <hb.h>
-+#include <hb-ft.h>
-+
-+#include "st.h"
-+#include "hb.h"
-+
-+#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start…
-+#define BUFFER_STEP 256
-+
-+hb_font_t *hbfindfont(XftFont *match);
-+
-+typedef struct {
-+ XftFont *match;
-+ hb_font_t *font;
-+} HbFontMatch;
-+
-+typedef struct {
-+ size_t capacity;
-+ HbFontMatch *fonts;
-+} HbFontCache;
-+
-+static HbFontCache hbfontcache = { 0, NULL };
-+
-+typedef struct {
-+ size_t capacity;
-+ Rune *runes;
-+} RuneBuffer;
-+
-+static RuneBuffer hbrunebuffer = { 0, NULL };
-+
-+/*
-+ * Poplulate the array with a list of font features, wrapped in FEATURE macro,
-+ * e. g.
-+ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g')
-+ */
-+hb_feature_t features[] = { };
-+
-+void
-+hbunloadfonts()
-+{
-+ for (int i = 0; i < hbfontcache.capacity; i++) {
-+ hb_font_destroy(hbfontcache.fonts[i].font);
-+ XftUnlockFace(hbfontcache.fonts[i].match);
-+ }
-+
-+ if (hbfontcache.fonts != NULL) {
-+ free(hbfontcache.fonts);
-+ hbfontcache.fonts = NULL;
-+ }
-+ hbfontcache.capacity = 0;
-+}
-+
-+hb_font_t *
-+hbfindfont(XftFont *match)
-+{
-+ for (int i = 0; i < hbfontcache.capacity; i++) {
-+ if (hbfontcache.fonts[i].match == match)
-+ return hbfontcache.fonts[i].font;
-+ }
-+
-+ /* Font not found in cache, caching it now. */
-+ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * …
-+ FT_Face face = XftLockFace(match);
-+ hb_font_t *font = hb_ft_font_create(face, NULL);
-+ if (font == NULL)
-+ die("Failed to load Harfbuzz font.");
-+
-+ hbfontcache.fonts[hbfontcache.capacity].match = match;
-+ hbfontcache.fonts[hbfontcache.capacity].font = font;
-+ hbfontcache.capacity += 1;
-+
-+ return font;
-+}
-+
-+void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, …
-+ ushort mode = USHRT_MAX;
-+ unsigned int glyph_count;
-+ int rune_idx, glyph_idx, end = start + length;
-+
-+ hb_font_t *font = hbfindfont(xfont);
-+ if (font == NULL)
-+ return;
-+
-+ hb_buffer_t *buffer = hb_buffer_create();
-+ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
-+ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_…
-+
-+ /* Resize the buffer if required length is larger. */
-+ if (hbrunebuffer.capacity < length) {
-+ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S…
-+ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer…
-+ }
-+
-+ /* Fill buffer with codepoints. */
-+ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r…
-+ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u;
-+ mode = glyphs[glyph_idx].mode;
-+ if (mode & ATTR_WDUMMY)
-+ hbrunebuffer.runes[rune_idx] = 0x0020;
-+ }
-+ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt…
-+
-+ /* Shape the segment. */
-+ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t…
-+
-+ /* Get new glyph info. */
-+ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun…
-+ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly…
-+
-+ /* Fill the output. */
-+ data->buffer = buffer;
-+ data->glyphs = info;
-+ data->positions = pos;
-+ data->count = glyph_count;
-+}
-+
-+void hbcleanup(HbTransformData *data) {
-+ hb_buffer_destroy(data->buffer);
-+ memset(data, 0, sizeof(HbTransformData));
-+}
-diff --git a/hb.h b/hb.h
-new file mode 100644
-index 0000000..3b0ef44
---- /dev/null
-+++ b/hb.h
-@@ -0,0 +1,14 @@
-+#include <X11/Xft/Xft.h>
-+#include <hb.h>
-+#include <hb-ft.h>
-+
-+typedef struct {
-+ hb_buffer_t *buffer;
-+ hb_glyph_info_t *glyphs;
-+ hb_glyph_position_t *positions;
-+ unsigned int count;
-+} HbTransformData;
-+
-+void hbunloadfonts();
-+void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
-+void hbcleanup(HbTransformData *);
-diff --git a/st.c b/st.c
-index 79ee9ba..7675db6 100644
---- a/st.c
-+++ b/st.c
-@@ -2711,7 +2711,8 @@ draw(void)
- drawregion(0, 0, term.col, term.row);
- if (term.scr == 0)
- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
-- term.ocx, term.ocy, term.line[term.ocy][term.…
-+ term.ocx, term.ocy, term.line[term.ocy][term.…
-+ term.line[term.ocy], term.col);
- term.ocx = cx;
- term.ocy = term.c.y;
- xfinishdraw();
-diff --git a/st.h b/st.h
-index 818a6f8..4e584b6 100644
---- a/st.h
-+++ b/st.h
-@@ -11,7 +11,8 @@
- #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
- #define DEFAULT(a, b) (a) = (a) ? (a) : (b)
- #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)…
--#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f…
-+#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode …
-+ (a).fg != (b).fg || \
- (a).bg != (b).bg)
- #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
- (t1.tv_nsec-t2.tv_nsec)/1E6)
-diff --git a/win.h b/win.h
-index 6de960d..94679e4 100644
---- a/win.h
-+++ b/win.h
-@@ -25,7 +25,7 @@ enum win_mode {
-
- void xbell(void);
- void xclipcopy(void);
--void xdrawcursor(int, int, Glyph, int, int, Glyph);
-+void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int);
- void xdrawline(Line, int, int, int);
- void xfinishdraw(void);
- void xloadcols(void);
-diff --git a/x.c b/x.c
-index 2a3bd38..0bb51ff 100644
---- a/x.c
-+++ b/x.c
-@@ -19,6 +19,7 @@ char *argv0;
- #include "arg.h"
- #include "st.h"
- #include "win.h"
-+#include "hb.h"
-
- /* types used in config.h */
- typedef struct {
-@@ -141,8 +142,9 @@ typedef struct {
- } DC;
-
- static inline ushort sixd_to_16bit(int);
-+static void xresetfontsettings(ushort mode, Font **font, int *frcflags);
- static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i…
--static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
-+static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
- static void xdrawglyph(Glyph, int, int);
- static void xclear(int, int, int, int);
- static int xgeommasktogravity(int);
-@@ -757,7 +759,7 @@ xresize(int col, int row)
- xclear(0, 0, win.w, win.h);
-
- /* resize to new width */
-- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
-+ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
- }
-
- ushort
-@@ -1062,6 +1064,9 @@ xunloadfont(Font *f)
- void
- xunloadfonts(void)
- {
-+ /* Clear Harfbuzz font cache. */
-+ hbunloadfonts();
-+
- /* Free the loaded fonts in the font cache. */
- while (frclen > 0)
- XftFontClose(xw.dpy, frc[--frclen].font);
-@@ -1185,7 +1190,7 @@ xinit(int cols, int rows)
- XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
-
- /* font spec buffer */
-- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
-+ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
-
- /* Xft rendering context */
- xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
-@@ -1239,6 +1244,22 @@ xinit(int cols, int rows)
- xsel.xtarget = XA_STRING;
- }
-
-+void
-+xresetfontsettings(ushort mode, Font **font, int *frcflags)
-+{
-+ *font = &dc.font;
-+ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
-+ *font = &dc.ibfont;
-+ *frcflags = FRC_ITALICBOLD;
-+ } else if (mode & ATTR_ITALIC) {
-+ *font = &dc.ifont;
-+ *frcflags = FRC_ITALIC;
-+ } else if (mode & ATTR_BOLD) {
-+ *font = &dc.bfont;
-+ *frcflags = FRC_BOLD;
-+ }
-+}
-+
- int
- xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in…
- {
-@@ -1253,128 +1274,157 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const G…
- FcPattern *fcpattern, *fontpattern;
- FcFontSet *fcsets[] = { NULL };
- FcCharSet *fccharset;
-- int i, f, numspecs = 0;
-+ int i, f, length = 0, start = 0, numspecs = 0;
-+ float cluster_xp = xp, cluster_yp = yp;
-+ HbTransformData shaped = { 0 };
-+
-+ /* Initial values. */
-+ mode = prevmode = glyphs[0].mode & ~ATTR_WRAP;
-+ xresetfontsettings(mode, &font, &frcflags);
-
- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
-- /* Fetch rune and mode for current glyph. */
-- rune = glyphs[i].u;
-- mode = glyphs[i].mode;
-+ mode = glyphs[i].mode & ~ATTR_WRAP;
-
- /* Skip dummy wide-character spacing. */
-- if (mode == ATTR_WDUMMY)
-+ if (mode & ATTR_WDUMMY && i < (len - 1))
- continue;
-
-- /* Determine font for glyph if different from previous glyph.…
-- if (prevmode != mode) {
-- prevmode = mode;
-- font = &dc.font;
-- frcflags = FRC_NORMAL;
-- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0…
-- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
-- font = &dc.ibfont;
-- frcflags = FRC_ITALICBOLD;
-- } else if (mode & ATTR_ITALIC) {
-- font = &dc.ifont;
-- frcflags = FRC_ITALIC;
-- } else if (mode & ATTR_BOLD) {
-- font = &dc.bfont;
-- frcflags = FRC_BOLD;
-+ if (
-+ prevmode != mode
-+ || ATTRCMP(glyphs[start], glyphs[i])
-+ || selected(x + i, y) != selected(x + start, y)
-+ || i == (len - 1)
-+ ) {
-+ /* Handle 1-character wide segments and end of line */
-+ length = i - start;
-+ if (i == start) {
-+ length = 1;
-+ } else if (i == (len - 1)) {
-+ length = (i - start + 1);
- }
-- yp = winy + font->ascent;
-- }
-
-- /* Lookup character index with default font. */
-- glyphidx = XftCharIndex(xw.dpy, font->match, rune);
-- if (glyphidx) {
-- specs[numspecs].font = font->match;
-- specs[numspecs].glyph = glyphidx;
-- specs[numspecs].x = (short)xp;
-- specs[numspecs].y = (short)yp;
-- xp += runewidth;
-- numspecs++;
-- continue;
-- }
--
-- /* Fallback on font cache, search the font cache for match. */
-- for (f = 0; f < frclen; f++) {
-- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
-- /* Everything correct. */
-- if (glyphidx && frc[f].flags == frcflags)
-- break;
-- /* We got a default font for a not found glyph. */
-- if (!glyphidx && frc[f].flags == frcflags
-- && frc[f].unicodep == rune) {
-- break;
-+ /* Shape the segment. */
-+ hbtransform(&shaped, font->match, glyphs, start, leng…
-+ runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE…
-+ cluster_xp = xp; cluster_yp = yp;
-+ for (int code_idx = 0; code_idx < shaped.count; code_…
-+ int idx = shaped.glyphs[code_idx].cluster;
-+
-+ if (glyphs[start + idx].mode & ATTR_WDUMMY)
-+ continue;
-+
-+ /* Advance the drawing cursor if we've moved …
-+ if (code_idx > 0 && idx != shaped.glyphs[code…
-+ xp += runewidth;
-+ cluster_xp = xp;
-+ cluster_yp = yp;
-+ runewidth = win.cw * ((glyphs[start +…
-+ }
-+
-+ if (shaped.glyphs[code_idx].codepoint != 0) {
-+ /* If symbol is found, put it into th…
-+ specs[numspecs].font = font->match;
-+ specs[numspecs].glyph = shaped.glyphs…
-+ specs[numspecs].x = cluster_xp + (sho…
-+ specs[numspecs].y = cluster_yp - (sho…
-+ cluster_xp += shaped.positions[code_i…
-+ cluster_yp += shaped.positions[code_i…
-+ numspecs++;
-+ } else {
-+ /* If it's not found, try to fetch it…
-+ rune = glyphs[start + idx].u;
-+ for (f = 0; f < frclen; f++) {
-+ glyphidx = XftCharIndex(xw.dp…
-+ /* Everything correct. */
-+ if (glyphidx && frc[f].flags …
-+ break;
-+ /* We got a default font for …
-+ if (!glyphidx && frc[f].flags…
-+ && frc[f].uni…
-+ break;
-+ }
-+ }
-+
-+ /* Nothing was found. Use fontconfig …
-+ if (f >= frclen) {
-+ if (!font->set)
-+ font->set = FcFontSor…
-+ …
-+ fcsets[0] = font->set;
-+
-+ /*
-+ * Nothing was found in the c…
-+ * some dozen of Fontconfig c…
-+ * font for one single charac…
-+ *
-+ * Xft and fontconfig are des…
-+ */
-+ fcpattern = FcPatternDuplicat…
-+ fccharset = FcCharSetCreate();
-+
-+ FcCharSetAddChar(fccharset, r…
-+ FcPatternAddCharSet(fcpattern…
-+ fccharset);
-+ FcPatternAddBool(fcpattern, F…
-+
-+ FcConfigSubstitute(0, fcpatte…
-+ FcMatchPatter…
-+ FcDefaultSubstitute(fcpattern…
-+
-+ fontpattern = FcFontSetMatch(…
-+ fcpattern, &f…
-+
-+ /* Allocate memory for the ne…
-+ if (frclen >= frccap) {
-+ frccap += 16;
-+ frc = xrealloc(frc, f…
-+ }
-+
-+ frc[frclen].font = XftFontOpe…
-+ fontpattern);
-+ if (!frc[frclen].font)
-+ die("XftFontOpenPatte…
-+ strerror(errn…
-+ frc[frclen].flags = frcflags;
-+ frc[frclen].unicodep = rune;
-+
-+ glyphidx = XftCharIndex(xw.dp…
-+
-+ f = frclen;
-+ frclen++;
-+
-+ FcPatternDestroy(fcpattern);
-+ FcCharSetDestroy(fccharset);
-+ }
-+
-+ specs[numspecs].font = frc[f].font;
-+ specs[numspecs].glyph = glyphidx;
-+ specs[numspecs].x = (short)xp;
-+ specs[numspecs].y = (short)yp;
-+ numspecs++;
-+ }
- }
-- }
-
-- /* Nothing was found. Use fontconfig to find matching font. */
-- if (f >= frclen) {
-- if (!font->set)
-- font->set = FcFontSort(0, font->pattern,
-- 1, 0, &fcres);
-- fcsets[0] = font->set;
-+ /* Cleanup and get ready for next segment. */
-+ hbcleanup(&shaped);
-+ start = i;
-
-- /*
-- * Nothing was found in the cache. Now use
-- * some dozen of Fontconfig calls to get the
-- * font for one single character.
-- *
-- * Xft and fontconfig are design failures.
-- */
-- fcpattern = FcPatternDuplicate(font->pattern);
-- fccharset = FcCharSetCreate();
--
-- FcCharSetAddChar(fccharset, rune);
-- FcPatternAddCharSet(fcpattern, FC_CHARSET,
-- fccharset);
-- FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
--
-- FcConfigSubstitute(0, fcpattern,
-- FcMatchPattern);
-- FcDefaultSubstitute(fcpattern);
--
-- fontpattern = FcFontSetMatch(0, fcsets, 1,
-- fcpattern, &fcres);
--
-- /* Allocate memory for the new cache entry. */
-- if (frclen >= frccap) {
-- frccap += 16;
-- frc = xrealloc(frc, frccap * sizeof(Fontcache…
-+ /* Determine font for glyph if different from previou…
-+ if (prevmode != mode) {
-+ prevmode = mode;
-+ xresetfontsettings(mode, &font, &frcflags);
-+ yp = winy + font->ascent;
- }
--
-- frc[frclen].font = XftFontOpenPattern(xw.dpy,
-- fontpattern);
-- if (!frc[frclen].font)
-- die("XftFontOpenPattern failed seeking fallba…
-- strerror(errno));
-- frc[frclen].flags = frcflags;
-- frc[frclen].unicodep = rune;
--
-- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run…
--
-- f = frclen;
-- frclen++;
--
-- FcPatternDestroy(fcpattern);
-- FcCharSetDestroy(fccharset);
- }
--
-- specs[numspecs].font = frc[f].font;
-- specs[numspecs].glyph = glyphidx;
-- specs[numspecs].x = (short)xp;
-- specs[numspecs].y = (short)yp;
-- xp += runewidth;
-- numspecs++;
- }
-
-+ hbcleanup(&shaped);
- return numspecs;
- }
-
- void
--xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
-+xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
- {
-- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
- int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
- width = charlen * win.cw;
- Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
-@@ -1510,21 +1560,24 @@ void
- xdrawglyph(Glyph g, int x, int y)
- {
- int numspecs;
-- XftGlyphFontSpec spec;
-+ XftGlyphFontSpec *specs = xw.specbuf;
-
-- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
-- xdrawglyphfontspecs(&spec, g, numspecs, x, y);
-+ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y);
-+ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? …
- }
-
- void
--xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
-+xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int…
- {
- Color drawcol;
-
- /* remove the old cursor */
- if (selected(ox, oy))
- og.mode ^= ATTR_REVERSE;
-- xdrawglyph(og, ox, oy);
-+
-+ /* Redraw the line where cursor was previously.
-+ * It will restore the ligatures broken by the cursor. */
-+ xdrawline(line, 0, oy, len);
-
- if (IS_SET(MODE_HIDE))
- return;
-@@ -1652,18 +1705,16 @@ xdrawline(Line line, int x1, int y1, int x2)
- Glyph base, new;
- XftGlyphFontSpec *specs = xw.specbuf;
-
-- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
- i = ox = 0;
-- for (x = x1; x < x2 && i < numspecs; x++) {
-+ for (x = x1; x < x2; x++) {
- new = line[x];
- if (new.mode == ATTR_WDUMMY)
- continue;
- if (selected(x, y1))
- new.mode ^= ATTR_REVERSE;
-- if (i > 0 && ATTRCMP(base, new)) {
-- xdrawglyphfontspecs(specs, base, i, ox, y1);
-- specs += i;
-- numspecs -= i;
-+ if ((i > 0) && ATTRCMP(base, new)) {
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - …
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x …
- i = 0;
- }
- if (i == 0) {
-@@ -1672,8 +1723,10 @@ xdrawline(Line line, int x1, int y1, int x2)
- }
- i++;
- }
-- if (i > 0)
-- xdrawglyphfontspecs(specs, base, i, ox, y1);
-+ if (i > 0) {
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,…
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox);
-+ }
- }
-
- void
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-scrollback-20…
@@ -0,0 +1,650 @@
+diff --git a/Makefile b/Makefile
+index 15db421..dfcea0f 100644
+--- a/Makefile
++++ b/Makefile
+@@ -3,9 +3,9 @@
+ .POSIX:
+
+ include config.mk
+
+-SRC = st.c x.c
++SRC = st.c x.c hb.c
+ OBJ = $(SRC:.c=.o)
+
+ all: st
+
+@@ -15,9 +15,10 @@ config.h:
+ .c.o:
+ $(CC) $(STCFLAGS) -c $<
+
+ st.o: config.h st.h win.h
+-x.o: arg.h config.h st.h win.h
++x.o: arg.h config.h st.h win.h hb.h
++hb.o: st.h
+
+ $(OBJ): config.h config.mk
+
+ st: $(OBJ)
+diff --git a/config.mk b/config.mk
+index fdc29a7..6833b3b 100644
+--- a/config.mk
++++ b/config.mk
+@@ -14,12 +14,14 @@ PKG_CONFIG = pkg-config
+
+ # includes and libs
+ INCS = -I$(X11INC) \
+ `$(PKG_CONFIG) --cflags fontconfig` \
+- `$(PKG_CONFIG) --cflags freetype2`
++ `$(PKG_CONFIG) --cflags freetype2` \
++ `$(PKG_CONFIG) --cflags harfbuzz`
+ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \
+ `$(PKG_CONFIG) --libs fontconfig` \
+- `$(PKG_CONFIG) --libs freetype2`
++ `$(PKG_CONFIG) --libs freetype2` \
++ `$(PKG_CONFIG) --libs harfbuzz`
+
+ # flags
+ STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
+ STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS)
+@@ -28,9 +30,10 @@ STLDFLAGS = $(LIBS) $(LDFLAGS)
+ # OpenBSD:
+ #CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE
+ #LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \
+ # `$(PKG_CONFIG) --libs fontconfig` \
+-# `$(PKG_CONFIG) --libs freetype2`
++# `$(PKG_CONFIG) --libs freetype2` \
++# `$(PKG_CONFIG) --libs harfbuzz`
+ #MANPREFIX = ${PREFIX}/man
+
+ # compiler and linker
+ # CC = c99
+diff --git a/hb.c b/hb.c
+new file mode 100644
+index 0000000..99412c8
+--- /dev/null
++++ b/hb.c
+@@ -0,0 +1,125 @@
++#include <stdlib.h>
++#include <stdio.h>
++#include <math.h>
++#include <X11/Xft/Xft.h>
++#include <X11/cursorfont.h>
++#include <hb.h>
++#include <hb-ft.h>
++
++#include "st.h"
++#include "hb.h"
++
++#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start…
++#define BUFFER_STEP 256
++
++hb_font_t *hbfindfont(XftFont *match);
++
++typedef struct {
++ XftFont *match;
++ hb_font_t *font;
++} HbFontMatch;
++
++typedef struct {
++ size_t capacity;
++ HbFontMatch *fonts;
++} HbFontCache;
++
++static HbFontCache hbfontcache = { 0, NULL };
++
++typedef struct {
++ size_t capacity;
++ Rune *runes;
++} RuneBuffer;
++
++static RuneBuffer hbrunebuffer = { 0, NULL };
++
++/*
++ * Poplulate the array with a list of font features, wrapped in FEATURE macro,
++ * e. g.
++ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g')
++ */
++hb_feature_t features[] = { };
++
++void
++hbunloadfonts()
++{
++ for (int i = 0; i < hbfontcache.capacity; i++) {
++ hb_font_destroy(hbfontcache.fonts[i].font);
++ XftUnlockFace(hbfontcache.fonts[i].match);
++ }
++
++ if (hbfontcache.fonts != NULL) {
++ free(hbfontcache.fonts);
++ hbfontcache.fonts = NULL;
++ }
++ hbfontcache.capacity = 0;
++}
++
++hb_font_t *
++hbfindfont(XftFont *match)
++{
++ for (int i = 0; i < hbfontcache.capacity; i++) {
++ if (hbfontcache.fonts[i].match == match)
++ return hbfontcache.fonts[i].font;
++ }
++
++ /* Font not found in cache, caching it now. */
++ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * …
++ FT_Face face = XftLockFace(match);
++ hb_font_t *font = hb_ft_font_create(face, NULL);
++ if (font == NULL)
++ die("Failed to load Harfbuzz font.");
++
++ hbfontcache.fonts[hbfontcache.capacity].match = match;
++ hbfontcache.fonts[hbfontcache.capacity].font = font;
++ hbfontcache.capacity += 1;
++
++ return font;
++}
++
++void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, …
++ ushort mode = USHRT_MAX;
++ unsigned int glyph_count;
++ int rune_idx, glyph_idx, end = start + length;
++
++ hb_font_t *font = hbfindfont(xfont);
++ if (font == NULL)
++ return;
++
++ hb_buffer_t *buffer = hb_buffer_create();
++ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
++ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_…
++
++ /* Resize the buffer if required length is larger. */
++ if (hbrunebuffer.capacity < length) {
++ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S…
++ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer…
++ }
++
++ /* Fill buffer with codepoints. */
++ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r…
++ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u;
++ mode = glyphs[glyph_idx].mode;
++ if (mode & ATTR_WDUMMY)
++ hbrunebuffer.runes[rune_idx] = 0x0020;
++ }
++ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt…
++
++ /* Shape the segment. */
++ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t…
++
++ /* Get new glyph info. */
++ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun…
++ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly…
++
++ /* Fill the output. */
++ data->buffer = buffer;
++ data->glyphs = info;
++ data->positions = pos;
++ data->count = glyph_count;
++}
++
++void hbcleanup(HbTransformData *data) {
++ hb_buffer_destroy(data->buffer);
++ memset(data, 0, sizeof(HbTransformData));
++}
+diff --git a/hb.h b/hb.h
+new file mode 100644
+index 0000000..3b0ef44
+--- /dev/null
++++ b/hb.h
+@@ -0,0 +1,14 @@
++#include <X11/Xft/Xft.h>
++#include <hb.h>
++#include <hb-ft.h>
++
++typedef struct {
++ hb_buffer_t *buffer;
++ hb_glyph_info_t *glyphs;
++ hb_glyph_position_t *positions;
++ unsigned int count;
++} HbTransformData;
++
++void hbunloadfonts();
++void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
++void hbcleanup(HbTransformData *);
+diff --git a/st.c b/st.c
+index 2478942..bba90d3 100644
+--- a/st.c
++++ b/st.c
+@@ -2729,9 +2729,10 @@ draw(void)
+
+ drawregion(0, 0, term.col, term.row);
+ if (term.scr == 0)
+ xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
+- term.ocx, term.ocy, term.line[term.ocy][term.…
++ term.ocx, term.ocy, term.line[term.ocy][term.…
++ term.line[term.ocy], term.col);
+ term.ocx = cx;
+ term.ocy = term.c.y;
+ xfinishdraw();
+ if (ocx != term.ocx || ocy != term.ocy)
+diff --git a/st.h b/st.h
+index 818a6f8..4e584b6 100644
+--- a/st.h
++++ b/st.h
+@@ -10,9 +10,10 @@
+ #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
+ #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
+ #define DEFAULT(a, b) (a) = (a) ? (a) : (b)
+ #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)…
+-#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f…
++#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode …
++ (a).fg != (b).fg || \
+ (a).bg != (b).bg)
+ #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
+ (t1.tv_nsec-t2.tv_nsec)/1E6)
+ #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
+diff --git a/win.h b/win.h
+index 6de960d..94679e4 100644
+--- a/win.h
++++ b/win.h
+@@ -24,9 +24,9 @@ enum win_mode {
+ };
+
+ void xbell(void);
+ void xclipcopy(void);
+-void xdrawcursor(int, int, Glyph, int, int, Glyph);
++void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int);
+ void xdrawline(Line, int, int, int);
+ void xfinishdraw(void);
+ void xloadcols(void);
+ int xsetcolorname(int, const char *);
+diff --git a/x.c b/x.c
+index bd23686..2bf3b72 100644
+--- a/x.c
++++ b/x.c
+@@ -18,8 +18,9 @@
+ char *argv0;
+ #include "arg.h"
+ #include "st.h"
+ #include "win.h"
++#include "hb.h"
+
+ /* types used in config.h */
+ typedef struct {
+ uint mod;
+@@ -140,10 +141,11 @@ typedef struct {
+ GC gc;
+ } DC;
+
+ static inline ushort sixd_to_16bit(int);
++static void xresetfontsettings(ushort mode, Font **font, int *frcflags);
+ static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i…
+-static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
++static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
+ static void xdrawglyph(Glyph, int, int);
+ static void xclear(int, int, int, int);
+ static int xgeommasktogravity(int);
+ static int ximopen(Display *);
+@@ -756,9 +758,9 @@ xresize(int col, int row)
+ XftDrawChange(xw.draw, xw.buf);
+ xclear(0, 0, win.w, win.h);
+
+ /* resize to new width */
+- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
++ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
+ }
+
+ ushort
+ sixd_to_16bit(int x)
+@@ -1061,8 +1063,11 @@ xunloadfont(Font *f)
+
+ void
+ xunloadfonts(void)
+ {
++ /* Clear Harfbuzz font cache. */
++ hbunloadfonts();
++
+ /* Free the loaded fonts in the font cache. */
+ while (frclen > 0)
+ XftFontClose(xw.dpy, frc[--frclen].font);
+
+@@ -1184,9 +1189,9 @@ xinit(int cols, int rows)
+ XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel);
+ XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
+
+ /* font spec buffer */
+- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
++ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
+
+ /* Xft rendering context */
+ xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
+
+@@ -1238,144 +1243,155 @@ xinit(int cols, int rows)
+ if (xsel.xtarget == None)
+ xsel.xtarget = XA_STRING;
+ }
+
++void
++xresetfontsettings(ushort mode, Font **font, int *frcflags)
++{
++ *font = &dc.font;
++ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
++ *font = &dc.ibfont;
++ *frcflags = FRC_ITALICBOLD;
++ } else if (mode & ATTR_ITALIC) {
++ *font = &dc.ifont;
++ *frcflags = FRC_ITALIC;
++ } else if (mode & ATTR_BOLD) {
++ *font = &dc.bfont;
++ *frcflags = FRC_BOLD;
++ }
++}
++
+ int
+ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in…
+ {
+ float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp,…
+- ushort mode, prevmode = USHRT_MAX;
++ ushort mode = glyphs[0].mode & ~ATTR_WRAP;
+ Font *font = &dc.font;
+ int frcflags = FRC_NORMAL;
+- float runewidth = win.cw;
++ float runewidth = win.cw * ((glyphs[0].mode & ATTR_WIDE) ? 2.0f : 1.0…
+ Rune rune;
+ FT_UInt glyphidx;
+ FcResult fcres;
+ FcPattern *fcpattern, *fontpattern;
+ FcFontSet *fcsets[] = { NULL };
+ FcCharSet *fccharset;
+- int i, f, numspecs = 0;
++ int f, code_idx, numspecs = 0;
++ float cluster_xp = xp, cluster_yp = yp;
++ HbTransformData shaped = { 0 };
+
+- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
+- /* Fetch rune and mode for current glyph. */
+- rune = glyphs[i].u;
+- mode = glyphs[i].mode;
++ /* Initial values. */
++ xresetfontsettings(mode, &font, &frcflags);
+
+- /* Skip dummy wide-character spacing. */
+- if (mode == ATTR_WDUMMY)
++ /* Shape the segment. */
++ hbtransform(&shaped, font->match, glyphs, 0, len);
++ xp = winx; yp = winy + font->ascent;
++ cluster_xp = xp; cluster_yp = yp;
++
++ for (code_idx = 0; code_idx < shaped.count; code_idx++) {
++ int idx = shaped.glyphs[code_idx].cluster;
++
++ if (glyphs[idx].mode & ATTR_WDUMMY)
+ continue;
+
+- /* Determine font for glyph if different from previous glyph.…
+- if (prevmode != mode) {
+- prevmode = mode;
+- font = &dc.font;
+- frcflags = FRC_NORMAL;
+- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0…
+- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
+- font = &dc.ibfont;
+- frcflags = FRC_ITALICBOLD;
+- } else if (mode & ATTR_ITALIC) {
+- font = &dc.ifont;
+- frcflags = FRC_ITALIC;
+- } else if (mode & ATTR_BOLD) {
+- font = &dc.bfont;
+- frcflags = FRC_BOLD;
+- }
+- yp = winy + font->ascent;
++ /* Advance the drawing cursor if we've moved to a new cluster…
++ if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluste…
++ xp += runewidth;
++ cluster_xp = xp;
++ cluster_yp = yp;
+ }
+
+- /* Lookup character index with default font. */
+- glyphidx = XftCharIndex(xw.dpy, font->match, rune);
+- if (glyphidx) {
++ if (shaped.glyphs[code_idx].codepoint != 0) {
++ /* If symbol is found, put it into the specs. */
+ specs[numspecs].font = font->match;
+- specs[numspecs].glyph = glyphidx;
+- specs[numspecs].x = (short)xp;
+- specs[numspecs].y = (short)yp;
+- xp += runewidth;
++ specs[numspecs].glyph = shaped.glyphs[code_idx].codep…
++ specs[numspecs].x = cluster_xp + (short)(shaped.posit…
++ specs[numspecs].y = cluster_yp - (short)(shaped.posit…
++ cluster_xp += shaped.positions[code_idx].x_advance / …
++ cluster_yp += shaped.positions[code_idx].y_advance / …
+ numspecs++;
+- continue;
+- }
+-
+- /* Fallback on font cache, search the font cache for match. */
+- for (f = 0; f < frclen; f++) {
+- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
+- /* Everything correct. */
+- if (glyphidx && frc[f].flags == frcflags)
+- break;
+- /* We got a default font for a not found glyph. */
+- if (!glyphidx && frc[f].flags == frcflags
+- && frc[f].unicodep == rune) {
+- break;
++ } else {
++ /* If it's not found, try to fetch it through the fon…
++ rune = glyphs[idx].u;
++ for (f = 0; f < frclen; f++) {
++ glyphidx = XftCharIndex(xw.dpy, frc[f].font, …
++ /* Everything correct. */
++ if (glyphidx && frc[f].flags == frcflags)
++ break;
++ /* We got a default font for a not found glyp…
++ if (!glyphidx && frc[f].flags == frcflags
++ && frc[f].unicodep == rune) {
++ break;
++ }
+ }
+- }
+
+- /* Nothing was found. Use fontconfig to find matching font. */
+- if (f >= frclen) {
+- if (!font->set)
+- font->set = FcFontSort(0, font->pattern,
+- 1, 0, &fcres);
+- fcsets[0] = font->set;
+-
+- /*
+- * Nothing was found in the cache. Now use
+- * some dozen of Fontconfig calls to get the
+- * font for one single character.
+- *
+- * Xft and fontconfig are design failures.
+- */
+- fcpattern = FcPatternDuplicate(font->pattern);
+- fccharset = FcCharSetCreate();
+-
+- FcCharSetAddChar(fccharset, rune);
+- FcPatternAddCharSet(fcpattern, FC_CHARSET,
+- fccharset);
+- FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
+-
+- FcConfigSubstitute(0, fcpattern,
+- FcMatchPattern);
+- FcDefaultSubstitute(fcpattern);
+-
+- fontpattern = FcFontSetMatch(0, fcsets, 1,
+- fcpattern, &fcres);
+-
+- /* Allocate memory for the new cache entry. */
+- if (frclen >= frccap) {
+- frccap += 16;
+- frc = xrealloc(frc, frccap * sizeof(Fontcache…
++ /* Nothing was found. Use fontconfig to find matching…
++ if (f >= frclen) {
++ if (!font->set)
++ font->set = FcFontSort(0, font->patte…
++ 1, 0, &fcres);
++ fcsets[0] = font->set;
++
++ /*
++ * Nothing was found in the cache. Now use
++ * some dozen of Fontconfig calls to get the
++ * font for one single character.
++ *
++ * Xft and fontconfig are design failures.
++ */
++ fcpattern = FcPatternDuplicate(font->pattern);
++ fccharset = FcCharSetCreate();
++
++ FcCharSetAddChar(fccharset, rune);
++ FcPatternAddCharSet(fcpattern, FC_CHARSET,
++ fccharset);
++ FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
++
++ FcConfigSubstitute(0, fcpattern,
++ FcMatchPattern);
++ FcDefaultSubstitute(fcpattern);
++
++ fontpattern = FcFontSetMatch(0, fcsets, 1,
++ fcpattern, &fcres);
++
++ /* Allocate memory for the new cache entry. */
++ if (frclen >= frccap) {
++ frccap += 16;
++ frc = xrealloc(frc, frccap * sizeof(F…
++ }
++
++ frc[frclen].font = XftFontOpenPattern(xw.dpy,
++ fontpattern);
++ if (!frc[frclen].font)
++ die("XftFontOpenPattern failed seekin…
++ strerror(errno));
++ frc[frclen].flags = frcflags;
++ frc[frclen].unicodep = rune;
++
++ glyphidx = XftCharIndex(xw.dpy, frc[frclen].f…
++
++ f = frclen;
++ frclen++;
++
++ FcPatternDestroy(fcpattern);
++ FcCharSetDestroy(fccharset);
+ }
+
+- frc[frclen].font = XftFontOpenPattern(xw.dpy,
+- fontpattern);
+- if (!frc[frclen].font)
+- die("XftFontOpenPattern failed seeking fallba…
+- strerror(errno));
+- frc[frclen].flags = frcflags;
+- frc[frclen].unicodep = rune;
+-
+- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run…
+-
+- f = frclen;
+- frclen++;
+-
+- FcPatternDestroy(fcpattern);
+- FcCharSetDestroy(fccharset);
++ specs[numspecs].font = frc[f].font;
++ specs[numspecs].glyph = glyphidx;
++ specs[numspecs].x = (short)xp;
++ specs[numspecs].y = (short)yp;
++ numspecs++;
+ }
+-
+- specs[numspecs].font = frc[f].font;
+- specs[numspecs].glyph = glyphidx;
+- specs[numspecs].x = (short)xp;
+- specs[numspecs].y = (short)yp;
+- xp += runewidth;
+- numspecs++;
+ }
+
++ /* Cleanup and get ready for next segment. */
++ hbcleanup(&shaped);
+ return numspecs;
+ }
+
+ void
+-xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
++xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
+ {
+- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
+ int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
+ width = charlen * win.cw;
+ Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
+ XRenderColor colfg, colbg;
+@@ -1509,23 +1525,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Gly…
+ void
+ xdrawglyph(Glyph g, int x, int y)
+ {
+ int numspecs;
+- XftGlyphFontSpec spec;
++ XftGlyphFontSpec *specs = xw.specbuf;
+
+- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
+- xdrawglyphfontspecs(&spec, g, numspecs, x, y);
++ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y);
++ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? …
+ }
+
+ void
+-xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
++xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int…
+ {
+ Color drawcol;
+
+ /* remove the old cursor */
+ if (selected(ox, oy))
+ og.mode ^= ATTR_REVERSE;
+- xdrawglyph(og, ox, oy);
++
++ /* Redraw the line where cursor was previously.
++ * It will restore the ligatures broken by the cursor. */
++ xdrawline(line, 0, oy, len);
+
+ if (IS_SET(MODE_HIDE))
+ return;
+
+@@ -1657,30 +1676,30 @@ xdrawline(Line line, int x1, int y1, int x2)
+ int i, x, ox, numspecs;
+ Glyph base, new;
+ XftGlyphFontSpec *specs = xw.specbuf;
+
+- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
+ i = ox = 0;
+- for (x = x1; x < x2 && i < numspecs; x++) {
++ for (x = x1; x < x2; x++) {
+ new = line[x];
+ if (new.mode == ATTR_WDUMMY)
+ continue;
+ if (selected(x, y1))
+ new.mode ^= ATTR_REVERSE;
+- if (i > 0 && ATTRCMP(base, new)) {
+- xdrawglyphfontspecs(specs, base, i, ox, y1);
+- specs += i;
+- numspecs -= i;
++ if ((i > 0) && ATTRCMP(base, new)) {
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - …
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x …
+ i = 0;
+ }
+ if (i == 0) {
+ ox = x;
+ base = new;
+ }
+ i++;
+ }
+- if (i > 0)
+- xdrawglyphfontspecs(specs, base, i, ox, y1);
++ if (i > 0) {
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,…
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox);
++ }
+ }
+
+ void
+ xfinishdraw(void)
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-scrollback-ri…
@@ -1,633 +0,0 @@
-diff --git a/Makefile b/Makefile
-index 470ac86..38240da 100644
---- a/Makefile
-+++ b/Makefile
-@@ -4,7 +4,7 @@
-
- include config.mk
-
--SRC = st.c x.c
-+SRC = st.c x.c hb.c
- OBJ = $(SRC:.c=.o)
-
- all: st
-@@ -22,7 +22,8 @@ config.h:
- $(CC) $(STCFLAGS) -c $<
-
- st.o: config.h st.h win.h
--x.o: arg.h config.h st.h win.h
-+x.o: arg.h config.h st.h win.h hb.h
-+hb.o: st.h
-
- $(OBJ): config.h config.mk
-
-diff --git a/config.mk b/config.mk
-index 1e306f8..3e13e53 100644
---- a/config.mk
-+++ b/config.mk
-@@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config
- # includes and libs
- INCS = -I$(X11INC) \
- `$(PKG_CONFIG) --cflags fontconfig` \
-- `$(PKG_CONFIG) --cflags freetype2`
-+ `$(PKG_CONFIG) --cflags freetype2` \
-+ `$(PKG_CONFIG) --cflags harfbuzz`
- LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \
- `$(PKG_CONFIG) --libs fontconfig` \
-- `$(PKG_CONFIG) --libs freetype2`
-+ `$(PKG_CONFIG) --libs freetype2` \
-+ `$(PKG_CONFIG) --libs harfbuzz`
-
- # flags
- STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
-diff --git a/hb.c b/hb.c
-new file mode 100644
-index 0000000..99412c8
---- /dev/null
-+++ b/hb.c
-@@ -0,0 +1,125 @@
-+#include <stdlib.h>
-+#include <stdio.h>
-+#include <math.h>
-+#include <X11/Xft/Xft.h>
-+#include <X11/cursorfont.h>
-+#include <hb.h>
-+#include <hb-ft.h>
-+
-+#include "st.h"
-+#include "hb.h"
-+
-+#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start…
-+#define BUFFER_STEP 256
-+
-+hb_font_t *hbfindfont(XftFont *match);
-+
-+typedef struct {
-+ XftFont *match;
-+ hb_font_t *font;
-+} HbFontMatch;
-+
-+typedef struct {
-+ size_t capacity;
-+ HbFontMatch *fonts;
-+} HbFontCache;
-+
-+static HbFontCache hbfontcache = { 0, NULL };
-+
-+typedef struct {
-+ size_t capacity;
-+ Rune *runes;
-+} RuneBuffer;
-+
-+static RuneBuffer hbrunebuffer = { 0, NULL };
-+
-+/*
-+ * Poplulate the array with a list of font features, wrapped in FEATURE macro,
-+ * e. g.
-+ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g')
-+ */
-+hb_feature_t features[] = { };
-+
-+void
-+hbunloadfonts()
-+{
-+ for (int i = 0; i < hbfontcache.capacity; i++) {
-+ hb_font_destroy(hbfontcache.fonts[i].font);
-+ XftUnlockFace(hbfontcache.fonts[i].match);
-+ }
-+
-+ if (hbfontcache.fonts != NULL) {
-+ free(hbfontcache.fonts);
-+ hbfontcache.fonts = NULL;
-+ }
-+ hbfontcache.capacity = 0;
-+}
-+
-+hb_font_t *
-+hbfindfont(XftFont *match)
-+{
-+ for (int i = 0; i < hbfontcache.capacity; i++) {
-+ if (hbfontcache.fonts[i].match == match)
-+ return hbfontcache.fonts[i].font;
-+ }
-+
-+ /* Font not found in cache, caching it now. */
-+ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * …
-+ FT_Face face = XftLockFace(match);
-+ hb_font_t *font = hb_ft_font_create(face, NULL);
-+ if (font == NULL)
-+ die("Failed to load Harfbuzz font.");
-+
-+ hbfontcache.fonts[hbfontcache.capacity].match = match;
-+ hbfontcache.fonts[hbfontcache.capacity].font = font;
-+ hbfontcache.capacity += 1;
-+
-+ return font;
-+}
-+
-+void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, …
-+ ushort mode = USHRT_MAX;
-+ unsigned int glyph_count;
-+ int rune_idx, glyph_idx, end = start + length;
-+
-+ hb_font_t *font = hbfindfont(xfont);
-+ if (font == NULL)
-+ return;
-+
-+ hb_buffer_t *buffer = hb_buffer_create();
-+ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
-+ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_…
-+
-+ /* Resize the buffer if required length is larger. */
-+ if (hbrunebuffer.capacity < length) {
-+ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S…
-+ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer…
-+ }
-+
-+ /* Fill buffer with codepoints. */
-+ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r…
-+ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u;
-+ mode = glyphs[glyph_idx].mode;
-+ if (mode & ATTR_WDUMMY)
-+ hbrunebuffer.runes[rune_idx] = 0x0020;
-+ }
-+ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt…
-+
-+ /* Shape the segment. */
-+ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t…
-+
-+ /* Get new glyph info. */
-+ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun…
-+ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly…
-+
-+ /* Fill the output. */
-+ data->buffer = buffer;
-+ data->glyphs = info;
-+ data->positions = pos;
-+ data->count = glyph_count;
-+}
-+
-+void hbcleanup(HbTransformData *data) {
-+ hb_buffer_destroy(data->buffer);
-+ memset(data, 0, sizeof(HbTransformData));
-+}
-diff --git a/hb.h b/hb.h
-new file mode 100644
-index 0000000..3b0ef44
---- /dev/null
-+++ b/hb.h
-@@ -0,0 +1,14 @@
-+#include <X11/Xft/Xft.h>
-+#include <hb.h>
-+#include <hb-ft.h>
-+
-+typedef struct {
-+ hb_buffer_t *buffer;
-+ hb_glyph_info_t *glyphs;
-+ hb_glyph_position_t *positions;
-+ unsigned int count;
-+} HbTransformData;
-+
-+void hbunloadfonts();
-+void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
-+void hbcleanup(HbTransformData *);
-diff --git a/st.c b/st.c
-index c44797b..91f54dc 100644
---- a/st.c
-+++ b/st.c
-@@ -2759,7 +2759,8 @@ draw(void)
- drawregion(0, 0, term.col, term.row);
- if (TSCREEN.off == 0)
- xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx],
-- term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]…
-+ term.ocx, term.ocy, TLINE(term.ocy)[term.ocx],
-+ TLINE(term.ocy), term.col);
- term.ocx = cx;
- term.ocy = term.c.y;
- xfinishdraw();
-diff --git a/st.h b/st.h
-index 3cea73b..709a369 100644
---- a/st.h
-+++ b/st.h
-@@ -11,7 +11,8 @@
- #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
- #define DEFAULT(a, b) (a) = (a) ? (a) : (b)
- #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)…
--#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f…
-+#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode …
-+ (a).fg != (b).fg || \
- (a).bg != (b).bg)
- #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
- (t1.tv_nsec-t2.tv_nsec)/1E6)
-diff --git a/win.h b/win.h
-index 6de960d..94679e4 100644
---- a/win.h
-+++ b/win.h
-@@ -25,7 +25,7 @@ enum win_mode {
-
- void xbell(void);
- void xclipcopy(void);
--void xdrawcursor(int, int, Glyph, int, int, Glyph);
-+void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int);
- void xdrawline(Line, int, int, int);
- void xfinishdraw(void);
- void xloadcols(void);
-diff --git a/x.c b/x.c
-index 9891e91..7d42790 100644
---- a/x.c
-+++ b/x.c
-@@ -19,6 +19,7 @@ char *argv0;
- #include "arg.h"
- #include "st.h"
- #include "win.h"
-+#include "hb.h"
-
- /* types used in config.h */
- typedef struct {
-@@ -143,8 +144,9 @@ typedef struct {
- } DC;
-
- static inline ushort sixd_to_16bit(int);
-+static void xresetfontsettings(ushort mode, Font **font, int *frcflags);
- static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i…
--static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
-+static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
- static void xdrawglyph(Glyph, int, int);
- static void xclear(int, int, int, int);
- static int xgeommasktogravity(int);
-@@ -759,7 +761,7 @@ xresize(int col, int row)
- xclear(0, 0, win.w, win.h);
-
- /* resize to new width */
-- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
-+ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
- }
-
- ushort
-@@ -1064,6 +1066,9 @@ xunloadfont(Font *f)
- void
- xunloadfonts(void)
- {
-+ /* Clear Harfbuzz font cache. */
-+ hbunloadfonts();
-+
- /* Free the loaded fonts in the font cache. */
- while (frclen > 0)
- XftFontClose(xw.dpy, frc[--frclen].font);
-@@ -1187,7 +1192,7 @@ xinit(int cols, int rows)
- XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
-
- /* font spec buffer */
-- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
-+ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
-
- /* Xft rendering context */
- xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
-@@ -1241,6 +1246,22 @@ xinit(int cols, int rows)
- xsel.xtarget = XA_STRING;
- }
-
-+void
-+xresetfontsettings(ushort mode, Font **font, int *frcflags)
-+{
-+ *font = &dc.font;
-+ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
-+ *font = &dc.ibfont;
-+ *frcflags = FRC_ITALICBOLD;
-+ } else if (mode & ATTR_ITALIC) {
-+ *font = &dc.ifont;
-+ *frcflags = FRC_ITALIC;
-+ } else if (mode & ATTR_BOLD) {
-+ *font = &dc.bfont;
-+ *frcflags = FRC_BOLD;
-+ }
-+}
-+
- int
- xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in…
- {
-@@ -1255,128 +1276,156 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const G…
- FcPattern *fcpattern, *fontpattern;
- FcFontSet *fcsets[] = { NULL };
- FcCharSet *fccharset;
-- int i, f, numspecs = 0;
-+ int i, f, length = 0, start = 0, numspecs = 0;
-+ float cluster_xp = xp, cluster_yp = yp;
-+ HbTransformData shaped = { 0 };
-+
-+ /* Initial values. */
-+ mode = prevmode = glyphs[0].mode & ~ATTR_WRAP;
-+ xresetfontsettings(mode, &font, &frcflags);
-
- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
-- /* Fetch rune and mode for current glyph. */
-- rune = glyphs[i].u;
-- mode = glyphs[i].mode;
-+ mode = glyphs[i].mode & ~ATTR_WRAP;
-
- /* Skip dummy wide-character spacing. */
-- if (mode == ATTR_WDUMMY)
-+ if (mode & ATTR_WDUMMY && i < (len - 1))
- continue;
-
-- /* Determine font for glyph if different from previous glyph.…
-- if (prevmode != mode) {
-- prevmode = mode;
-- font = &dc.font;
-- frcflags = FRC_NORMAL;
-- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0…
-- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
-- font = &dc.ibfont;
-- frcflags = FRC_ITALICBOLD;
-- } else if (mode & ATTR_ITALIC) {
-- font = &dc.ifont;
-- frcflags = FRC_ITALIC;
-- } else if (mode & ATTR_BOLD) {
-- font = &dc.bfont;
-- frcflags = FRC_BOLD;
-+ if (
-+ prevmode != mode
-+ || ATTRCMP(glyphs[start], glyphs[i])
-+ || selected(x + i, y) != selected(x + start, y)
-+ || i == (len - 1)
-+ ) {
-+ /* Handle 1-character wide segments and end of line */
-+ length = i - start;
-+ if (i == start) {
-+ length = 1;
-+ } else if (i == (len - 1)) {
-+ length = (i - start + 1);
- }
-- yp = winy + font->ascent;
-- }
-
-- /* Lookup character index with default font. */
-- glyphidx = XftCharIndex(xw.dpy, font->match, rune);
-- if (glyphidx) {
-- specs[numspecs].font = font->match;
-- specs[numspecs].glyph = glyphidx;
-- specs[numspecs].x = (short)xp;
-- specs[numspecs].y = (short)yp;
-- xp += runewidth;
-- numspecs++;
-- continue;
-- }
--
-- /* Fallback on font cache, search the font cache for match. */
-- for (f = 0; f < frclen; f++) {
-- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
-- /* Everything correct. */
-- if (glyphidx && frc[f].flags == frcflags)
-- break;
-- /* We got a default font for a not found glyph. */
-- if (!glyphidx && frc[f].flags == frcflags
-- && frc[f].unicodep == rune) {
-- break;
-+ /* Shape the segment. */
-+ hbtransform(&shaped, font->match, glyphs, start, leng…
-+ runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE…
-+ cluster_xp = xp; cluster_yp = yp;
-+ for (int code_idx = 0; code_idx < shaped.count; code_…
-+ int idx = shaped.glyphs[code_idx].cluster;
-+
-+ if (glyphs[start + idx].mode & ATTR_WDUMMY)
-+ continue;
-+
-+ /* Advance the drawing cursor if we've moved …
-+ if (code_idx > 0 && idx != shaped.glyphs[code…
-+ xp += runewidth;
-+ cluster_xp = xp;
-+ cluster_yp = yp;
-+ runewidth = win.cw * ((glyphs[start +…
-+ }
-+
-+ if (shaped.glyphs[code_idx].codepoint != 0) {
-+ /* If symbol is found, put it into th…
-+ specs[numspecs].font = font->match;
-+ specs[numspecs].glyph = shaped.glyphs…
-+ specs[numspecs].x = cluster_xp + (sho…
-+ specs[numspecs].y = cluster_yp - (sho…
-+ cluster_xp += shaped.positions[code_i…
-+ cluster_yp += shaped.positions[code_i…
-+ numspecs++;
-+ } else {
-+ /* If it's not found, try to fetch it…
-+ rune = glyphs[start + idx].u;
-+ for (f = 0; f < frclen; f++) {
-+ glyphidx = XftCharIndex(xw.dp…
-+ /* Everything correct. */
-+ if (glyphidx && frc[f].flags …
-+ break;
-+ /* We got a default font for …
-+ if (!glyphidx && frc[f].flags…
-+ && frc[f].uni…
-+ break;
-+ }
-+ }
-+
-+ /* Nothing was found. Use fontconfig …
-+ if (f >= frclen) {
-+ if (!font->set)
-+ font->set = FcFontSor…
-+ …
-+ fcsets[0] = font->set;
-+
-+ /*
-+ * Nothing was found in the c…
-+ * some dozen of Fontconfig c…
-+ * font for one single charac…
-+ *
-+ * Xft and fontconfig are des…
-+ */
-+ fcpattern = FcPatternDuplicat…
-+ fccharset = FcCharSetCreate();
-+
-+ FcCharSetAddChar(fccharset, r…
-+ FcPatternAddCharSet(fcpattern…
-+ fccharset);
-+ FcPatternAddBool(fcpattern, F…
-+
-+ FcConfigSubstitute(0, fcpatte…
-+ FcMatchPatter…
-+ FcDefaultSubstitute(fcpattern…
-+
-+ fontpattern = FcFontSetMatch(…
-+ fcpattern, &f…
-+
-+ /* Allocate memory for the ne…
-+ if (frclen >= frccap) {
-+ frccap += 16;
-+ frc = xrealloc(frc, f…
-+ }
-+
-+ frc[frclen].font = XftFontOpe…
-+ fontpattern);
-+ if (!frc[frclen].font)
-+ die("XftFontOpenPatte…
-+ strerror(errn…
-+ frc[frclen].flags = frcflags;
-+ frc[frclen].unicodep = rune;
-+
-+ glyphidx = XftCharIndex(xw.dp…
-+
-+ f = frclen;
-+ frclen++;
-+
-+ FcPatternDestroy(fcpattern);
-+ FcCharSetDestroy(fccharset);
-+ }
-+
-+ specs[numspecs].font = frc[f].font;
-+ specs[numspecs].glyph = glyphidx;
-+ specs[numspecs].x = (short)xp;
-+ specs[numspecs].y = (short)yp;
-+ numspecs++;
-+ }
- }
-- }
-
-- /* Nothing was found. Use fontconfig to find matching font. */
-- if (f >= frclen) {
-- if (!font->set)
-- font->set = FcFontSort(0, font->pattern,
-- 1, 0, &fcres);
-- fcsets[0] = font->set;
-+ /* Cleanup and get ready for next segment. */
-+ hbcleanup(&shaped);
-+ start = i;
-
-- /*
-- * Nothing was found in the cache. Now use
-- * some dozen of Fontconfig calls to get the
-- * font for one single character.
-- *
-- * Xft and fontconfig are design failures.
-- */
-- fcpattern = FcPatternDuplicate(font->pattern);
-- fccharset = FcCharSetCreate();
--
-- FcCharSetAddChar(fccharset, rune);
-- FcPatternAddCharSet(fcpattern, FC_CHARSET,
-- fccharset);
-- FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
--
-- FcConfigSubstitute(0, fcpattern,
-- FcMatchPattern);
-- FcDefaultSubstitute(fcpattern);
--
-- fontpattern = FcFontSetMatch(0, fcsets, 1,
-- fcpattern, &fcres);
--
-- /* Allocate memory for the new cache entry. */
-- if (frclen >= frccap) {
-- frccap += 16;
-- frc = xrealloc(frc, frccap * sizeof(Fontcache…
-+ /* Determine font for glyph if different from previou…
-+ if (prevmode != mode) {
-+ prevmode = mode;
-+ xresetfontsettings(mode, &font, &frcflags);
-+ yp = winy + font->ascent;
- }
--
-- frc[frclen].font = XftFontOpenPattern(xw.dpy,
-- fontpattern);
-- if (!frc[frclen].font)
-- die("XftFontOpenPattern failed seeking fallba…
-- strerror(errno));
-- frc[frclen].flags = frcflags;
-- frc[frclen].unicodep = rune;
--
-- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run…
--
-- f = frclen;
-- frclen++;
--
-- FcPatternDestroy(fcpattern);
-- FcCharSetDestroy(fccharset);
- }
--
-- specs[numspecs].font = frc[f].font;
-- specs[numspecs].glyph = glyphidx;
-- specs[numspecs].x = (short)xp;
-- specs[numspecs].y = (short)yp;
-- xp += runewidth;
-- numspecs++;
- }
-
- return numspecs;
- }
-
- void
--xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
-+xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
- {
-- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
- int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
- width = charlen * win.cw;
- Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
-@@ -1512,21 +1561,24 @@ void
- xdrawglyph(Glyph g, int x, int y)
- {
- int numspecs;
-- XftGlyphFontSpec spec;
-+ XftGlyphFontSpec *specs = xw.specbuf;
-
-- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
-- xdrawglyphfontspecs(&spec, g, numspecs, x, y);
-+ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y);
-+ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? …
- }
-
- void
--xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
-+xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int…
- {
- Color drawcol;
-
- /* remove the old cursor */
- if (selected(ox, oy))
- og.mode ^= ATTR_REVERSE;
-- xdrawglyph(og, ox, oy);
-+
-+ /* Redraw the line where cursor was previously.
-+ * It will restore the ligatures broken by the cursor. */
-+ xdrawline(line, 0, oy, len);
-
- if (IS_SET(MODE_HIDE))
- return;
-@@ -1654,18 +1706,16 @@ xdrawline(Line line, int x1, int y1, int x2)
- Glyph base, new;
- XftGlyphFontSpec *specs = xw.specbuf;
-
-- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
- i = ox = 0;
-- for (x = x1; x < x2 && i < numspecs; x++) {
-+ for (x = x1; x < x2; x++) {
- new = line[x];
- if (new.mode == ATTR_WDUMMY)
- continue;
- if (selected(x, y1))
- new.mode ^= ATTR_REVERSE;
-- if (i > 0 && ATTRCMP(base, new)) {
-- xdrawglyphfontspecs(specs, base, i, ox, y1);
-- specs += i;
-- numspecs -= i;
-+ if ((i > 0) && ATTRCMP(base, new)) {
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - …
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x …
- i = 0;
- }
- if (i == 0) {
-@@ -1674,8 +1724,10 @@ xdrawline(Line line, int x1, int y1, int x2)
- }
- i++;
- }
-- if (i > 0)
-- xdrawglyphfontspecs(specs, base, i, ox, y1);
-+ if (i > 0) {
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,…
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox);
-+ }
- }
-
- void
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-scrollback-ri…
@@ -0,0 +1,650 @@
+diff --git a/Makefile b/Makefile
+index 15db421..dfcea0f 100644
+--- a/Makefile
++++ b/Makefile
+@@ -3,9 +3,9 @@
+ .POSIX:
+
+ include config.mk
+
+-SRC = st.c x.c
++SRC = st.c x.c hb.c
+ OBJ = $(SRC:.c=.o)
+
+ all: st
+
+@@ -15,9 +15,10 @@ config.h:
+ .c.o:
+ $(CC) $(STCFLAGS) -c $<
+
+ st.o: config.h st.h win.h
+-x.o: arg.h config.h st.h win.h
++x.o: arg.h config.h st.h win.h hb.h
++hb.o: st.h
+
+ $(OBJ): config.h config.mk
+
+ st: $(OBJ)
+diff --git a/config.mk b/config.mk
+index fdc29a7..6833b3b 100644
+--- a/config.mk
++++ b/config.mk
+@@ -14,12 +14,14 @@ PKG_CONFIG = pkg-config
+
+ # includes and libs
+ INCS = -I$(X11INC) \
+ `$(PKG_CONFIG) --cflags fontconfig` \
+- `$(PKG_CONFIG) --cflags freetype2`
++ `$(PKG_CONFIG) --cflags freetype2` \
++ `$(PKG_CONFIG) --cflags harfbuzz`
+ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \
+ `$(PKG_CONFIG) --libs fontconfig` \
+- `$(PKG_CONFIG) --libs freetype2`
++ `$(PKG_CONFIG) --libs freetype2` \
++ `$(PKG_CONFIG) --libs harfbuzz`
+
+ # flags
+ STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
+ STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS)
+@@ -28,9 +30,10 @@ STLDFLAGS = $(LIBS) $(LDFLAGS)
+ # OpenBSD:
+ #CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE
+ #LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \
+ # `$(PKG_CONFIG) --libs fontconfig` \
+-# `$(PKG_CONFIG) --libs freetype2`
++# `$(PKG_CONFIG) --libs freetype2` \
++# `$(PKG_CONFIG) --libs harfbuzz`
+ #MANPREFIX = ${PREFIX}/man
+
+ # compiler and linker
+ # CC = c99
+diff --git a/hb.c b/hb.c
+new file mode 100644
+index 0000000..99412c8
+--- /dev/null
++++ b/hb.c
+@@ -0,0 +1,125 @@
++#include <stdlib.h>
++#include <stdio.h>
++#include <math.h>
++#include <X11/Xft/Xft.h>
++#include <X11/cursorfont.h>
++#include <hb.h>
++#include <hb-ft.h>
++
++#include "st.h"
++#include "hb.h"
++
++#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start…
++#define BUFFER_STEP 256
++
++hb_font_t *hbfindfont(XftFont *match);
++
++typedef struct {
++ XftFont *match;
++ hb_font_t *font;
++} HbFontMatch;
++
++typedef struct {
++ size_t capacity;
++ HbFontMatch *fonts;
++} HbFontCache;
++
++static HbFontCache hbfontcache = { 0, NULL };
++
++typedef struct {
++ size_t capacity;
++ Rune *runes;
++} RuneBuffer;
++
++static RuneBuffer hbrunebuffer = { 0, NULL };
++
++/*
++ * Poplulate the array with a list of font features, wrapped in FEATURE macro,
++ * e. g.
++ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g')
++ */
++hb_feature_t features[] = { };
++
++void
++hbunloadfonts()
++{
++ for (int i = 0; i < hbfontcache.capacity; i++) {
++ hb_font_destroy(hbfontcache.fonts[i].font);
++ XftUnlockFace(hbfontcache.fonts[i].match);
++ }
++
++ if (hbfontcache.fonts != NULL) {
++ free(hbfontcache.fonts);
++ hbfontcache.fonts = NULL;
++ }
++ hbfontcache.capacity = 0;
++}
++
++hb_font_t *
++hbfindfont(XftFont *match)
++{
++ for (int i = 0; i < hbfontcache.capacity; i++) {
++ if (hbfontcache.fonts[i].match == match)
++ return hbfontcache.fonts[i].font;
++ }
++
++ /* Font not found in cache, caching it now. */
++ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * …
++ FT_Face face = XftLockFace(match);
++ hb_font_t *font = hb_ft_font_create(face, NULL);
++ if (font == NULL)
++ die("Failed to load Harfbuzz font.");
++
++ hbfontcache.fonts[hbfontcache.capacity].match = match;
++ hbfontcache.fonts[hbfontcache.capacity].font = font;
++ hbfontcache.capacity += 1;
++
++ return font;
++}
++
++void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, …
++ ushort mode = USHRT_MAX;
++ unsigned int glyph_count;
++ int rune_idx, glyph_idx, end = start + length;
++
++ hb_font_t *font = hbfindfont(xfont);
++ if (font == NULL)
++ return;
++
++ hb_buffer_t *buffer = hb_buffer_create();
++ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
++ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_…
++
++ /* Resize the buffer if required length is larger. */
++ if (hbrunebuffer.capacity < length) {
++ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S…
++ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer…
++ }
++
++ /* Fill buffer with codepoints. */
++ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r…
++ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u;
++ mode = glyphs[glyph_idx].mode;
++ if (mode & ATTR_WDUMMY)
++ hbrunebuffer.runes[rune_idx] = 0x0020;
++ }
++ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt…
++
++ /* Shape the segment. */
++ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t…
++
++ /* Get new glyph info. */
++ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun…
++ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly…
++
++ /* Fill the output. */
++ data->buffer = buffer;
++ data->glyphs = info;
++ data->positions = pos;
++ data->count = glyph_count;
++}
++
++void hbcleanup(HbTransformData *data) {
++ hb_buffer_destroy(data->buffer);
++ memset(data, 0, sizeof(HbTransformData));
++}
+diff --git a/hb.h b/hb.h
+new file mode 100644
+index 0000000..3b0ef44
+--- /dev/null
++++ b/hb.h
+@@ -0,0 +1,14 @@
++#include <X11/Xft/Xft.h>
++#include <hb.h>
++#include <hb-ft.h>
++
++typedef struct {
++ hb_buffer_t *buffer;
++ hb_glyph_info_t *glyphs;
++ hb_glyph_position_t *positions;
++ unsigned int count;
++} HbTransformData;
++
++void hbunloadfonts();
++void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
++void hbcleanup(HbTransformData *);
+diff --git a/st.c b/st.c
+index d9b163e..fbca4ba 100644
+--- a/st.c
++++ b/st.c
+@@ -2777,9 +2777,10 @@ draw(void)
+
+ drawregion(0, 0, term.col, term.row);
+ if (TSCREEN.off == 0)
+ xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx],
+- term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]…
++ term.ocx, term.ocy, TLINE(term.ocy)[term.ocx],
++ TLINE(term.ocy), term.col);
+ term.ocx = cx;
+ term.ocy = term.c.y;
+ xfinishdraw();
+ if (ocx != term.ocx || ocy != term.ocy)
+diff --git a/st.h b/st.h
+index 3cea73b..709a369 100644
+--- a/st.h
++++ b/st.h
+@@ -10,9 +10,10 @@
+ #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
+ #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d))
+ #define DEFAULT(a, b) (a) = (a) ? (a) : (b)
+ #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)…
+-#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f…
++#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode …
++ (a).fg != (b).fg || \
+ (a).bg != (b).bg)
+ #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
+ (t1.tv_nsec-t2.tv_nsec)/1E6)
+ #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
+diff --git a/win.h b/win.h
+index 6de960d..94679e4 100644
+--- a/win.h
++++ b/win.h
+@@ -24,9 +24,9 @@ enum win_mode {
+ };
+
+ void xbell(void);
+ void xclipcopy(void);
+-void xdrawcursor(int, int, Glyph, int, int, Glyph);
++void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int);
+ void xdrawline(Line, int, int, int);
+ void xfinishdraw(void);
+ void xloadcols(void);
+ int xsetcolorname(int, const char *);
+diff --git a/x.c b/x.c
+index 25785a6..16d7a3a 100644
+--- a/x.c
++++ b/x.c
+@@ -18,8 +18,9 @@
+ char *argv0;
+ #include "arg.h"
+ #include "st.h"
+ #include "win.h"
++#include "hb.h"
+
+ /* types used in config.h */
+ typedef struct {
+ uint mod;
+@@ -142,10 +143,11 @@ typedef struct {
+ GC gc;
+ } DC;
+
+ static inline ushort sixd_to_16bit(int);
++static void xresetfontsettings(ushort mode, Font **font, int *frcflags);
+ static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i…
+-static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
++static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in…
+ static void xdrawglyph(Glyph, int, int);
+ static void xclear(int, int, int, int);
+ static int xgeommasktogravity(int);
+ static int ximopen(Display *);
+@@ -758,9 +760,9 @@ xresize(int col, int row)
+ XftDrawChange(xw.draw, xw.buf);
+ xclear(0, 0, win.w, win.h);
+
+ /* resize to new width */
+- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
++ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
+ }
+
+ ushort
+ sixd_to_16bit(int x)
+@@ -1063,8 +1065,11 @@ xunloadfont(Font *f)
+
+ void
+ xunloadfonts(void)
+ {
++ /* Clear Harfbuzz font cache. */
++ hbunloadfonts();
++
+ /* Free the loaded fonts in the font cache. */
+ while (frclen > 0)
+ XftFontClose(xw.dpy, frc[--frclen].font);
+
+@@ -1186,9 +1191,9 @@ xinit(int cols, int rows)
+ XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel);
+ XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
+
+ /* font spec buffer */
+- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
++ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
+
+ /* Xft rendering context */
+ xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
+
+@@ -1240,144 +1245,155 @@ xinit(int cols, int rows)
+ if (xsel.xtarget == None)
+ xsel.xtarget = XA_STRING;
+ }
+
++void
++xresetfontsettings(ushort mode, Font **font, int *frcflags)
++{
++ *font = &dc.font;
++ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
++ *font = &dc.ibfont;
++ *frcflags = FRC_ITALICBOLD;
++ } else if (mode & ATTR_ITALIC) {
++ *font = &dc.ifont;
++ *frcflags = FRC_ITALIC;
++ } else if (mode & ATTR_BOLD) {
++ *font = &dc.bfont;
++ *frcflags = FRC_BOLD;
++ }
++}
++
+ int
+ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in…
+ {
+ float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp,…
+- ushort mode, prevmode = USHRT_MAX;
++ ushort mode = glyphs[0].mode & ~ATTR_WRAP;
+ Font *font = &dc.font;
+ int frcflags = FRC_NORMAL;
+- float runewidth = win.cw;
++ float runewidth = win.cw * ((glyphs[0].mode & ATTR_WIDE) ? 2.0f : 1.0…
+ Rune rune;
+ FT_UInt glyphidx;
+ FcResult fcres;
+ FcPattern *fcpattern, *fontpattern;
+ FcFontSet *fcsets[] = { NULL };
+ FcCharSet *fccharset;
+- int i, f, numspecs = 0;
++ int f, code_idx, numspecs = 0;
++ float cluster_xp = xp, cluster_yp = yp;
++ HbTransformData shaped = { 0 };
+
+- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
+- /* Fetch rune and mode for current glyph. */
+- rune = glyphs[i].u;
+- mode = glyphs[i].mode;
++ /* Initial values. */
++ xresetfontsettings(mode, &font, &frcflags);
+
+- /* Skip dummy wide-character spacing. */
+- if (mode == ATTR_WDUMMY)
++ /* Shape the segment. */
++ hbtransform(&shaped, font->match, glyphs, 0, len);
++ xp = winx; yp = winy + font->ascent;
++ cluster_xp = xp; cluster_yp = yp;
++
++ for (code_idx = 0; code_idx < shaped.count; code_idx++) {
++ int idx = shaped.glyphs[code_idx].cluster;
++
++ if (glyphs[idx].mode & ATTR_WDUMMY)
+ continue;
+
+- /* Determine font for glyph if different from previous glyph.…
+- if (prevmode != mode) {
+- prevmode = mode;
+- font = &dc.font;
+- frcflags = FRC_NORMAL;
+- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0…
+- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
+- font = &dc.ibfont;
+- frcflags = FRC_ITALICBOLD;
+- } else if (mode & ATTR_ITALIC) {
+- font = &dc.ifont;
+- frcflags = FRC_ITALIC;
+- } else if (mode & ATTR_BOLD) {
+- font = &dc.bfont;
+- frcflags = FRC_BOLD;
+- }
+- yp = winy + font->ascent;
++ /* Advance the drawing cursor if we've moved to a new cluster…
++ if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluste…
++ xp += runewidth;
++ cluster_xp = xp;
++ cluster_yp = yp;
+ }
+
+- /* Lookup character index with default font. */
+- glyphidx = XftCharIndex(xw.dpy, font->match, rune);
+- if (glyphidx) {
++ if (shaped.glyphs[code_idx].codepoint != 0) {
++ /* If symbol is found, put it into the specs. */
+ specs[numspecs].font = font->match;
+- specs[numspecs].glyph = glyphidx;
+- specs[numspecs].x = (short)xp;
+- specs[numspecs].y = (short)yp;
+- xp += runewidth;
++ specs[numspecs].glyph = shaped.glyphs[code_idx].codep…
++ specs[numspecs].x = cluster_xp + (short)(shaped.posit…
++ specs[numspecs].y = cluster_yp - (short)(shaped.posit…
++ cluster_xp += shaped.positions[code_idx].x_advance / …
++ cluster_yp += shaped.positions[code_idx].y_advance / …
+ numspecs++;
+- continue;
+- }
+-
+- /* Fallback on font cache, search the font cache for match. */
+- for (f = 0; f < frclen; f++) {
+- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
+- /* Everything correct. */
+- if (glyphidx && frc[f].flags == frcflags)
+- break;
+- /* We got a default font for a not found glyph. */
+- if (!glyphidx && frc[f].flags == frcflags
+- && frc[f].unicodep == rune) {
+- break;
++ } else {
++ /* If it's not found, try to fetch it through the fon…
++ rune = glyphs[idx].u;
++ for (f = 0; f < frclen; f++) {
++ glyphidx = XftCharIndex(xw.dpy, frc[f].font, …
++ /* Everything correct. */
++ if (glyphidx && frc[f].flags == frcflags)
++ break;
++ /* We got a default font for a not found glyp…
++ if (!glyphidx && frc[f].flags == frcflags
++ && frc[f].unicodep == rune) {
++ break;
++ }
+ }
+- }
+
+- /* Nothing was found. Use fontconfig to find matching font. */
+- if (f >= frclen) {
+- if (!font->set)
+- font->set = FcFontSort(0, font->pattern,
+- 1, 0, &fcres);
+- fcsets[0] = font->set;
+-
+- /*
+- * Nothing was found in the cache. Now use
+- * some dozen of Fontconfig calls to get the
+- * font for one single character.
+- *
+- * Xft and fontconfig are design failures.
+- */
+- fcpattern = FcPatternDuplicate(font->pattern);
+- fccharset = FcCharSetCreate();
+-
+- FcCharSetAddChar(fccharset, rune);
+- FcPatternAddCharSet(fcpattern, FC_CHARSET,
+- fccharset);
+- FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
+-
+- FcConfigSubstitute(0, fcpattern,
+- FcMatchPattern);
+- FcDefaultSubstitute(fcpattern);
+-
+- fontpattern = FcFontSetMatch(0, fcsets, 1,
+- fcpattern, &fcres);
+-
+- /* Allocate memory for the new cache entry. */
+- if (frclen >= frccap) {
+- frccap += 16;
+- frc = xrealloc(frc, frccap * sizeof(Fontcache…
++ /* Nothing was found. Use fontconfig to find matching…
++ if (f >= frclen) {
++ if (!font->set)
++ font->set = FcFontSort(0, font->patte…
++ 1, 0, &fcres);
++ fcsets[0] = font->set;
++
++ /*
++ * Nothing was found in the cache. Now use
++ * some dozen of Fontconfig calls to get the
++ * font for one single character.
++ *
++ * Xft and fontconfig are design failures.
++ */
++ fcpattern = FcPatternDuplicate(font->pattern);
++ fccharset = FcCharSetCreate();
++
++ FcCharSetAddChar(fccharset, rune);
++ FcPatternAddCharSet(fcpattern, FC_CHARSET,
++ fccharset);
++ FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
++
++ FcConfigSubstitute(0, fcpattern,
++ FcMatchPattern);
++ FcDefaultSubstitute(fcpattern);
++
++ fontpattern = FcFontSetMatch(0, fcsets, 1,
++ fcpattern, &fcres);
++
++ /* Allocate memory for the new cache entry. */
++ if (frclen >= frccap) {
++ frccap += 16;
++ frc = xrealloc(frc, frccap * sizeof(F…
++ }
++
++ frc[frclen].font = XftFontOpenPattern(xw.dpy,
++ fontpattern);
++ if (!frc[frclen].font)
++ die("XftFontOpenPattern failed seekin…
++ strerror(errno));
++ frc[frclen].flags = frcflags;
++ frc[frclen].unicodep = rune;
++
++ glyphidx = XftCharIndex(xw.dpy, frc[frclen].f…
++
++ f = frclen;
++ frclen++;
++
++ FcPatternDestroy(fcpattern);
++ FcCharSetDestroy(fccharset);
+ }
+
+- frc[frclen].font = XftFontOpenPattern(xw.dpy,
+- fontpattern);
+- if (!frc[frclen].font)
+- die("XftFontOpenPattern failed seeking fallba…
+- strerror(errno));
+- frc[frclen].flags = frcflags;
+- frc[frclen].unicodep = rune;
+-
+- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run…
+-
+- f = frclen;
+- frclen++;
+-
+- FcPatternDestroy(fcpattern);
+- FcCharSetDestroy(fccharset);
++ specs[numspecs].font = frc[f].font;
++ specs[numspecs].glyph = glyphidx;
++ specs[numspecs].x = (short)xp;
++ specs[numspecs].y = (short)yp;
++ numspecs++;
+ }
+-
+- specs[numspecs].font = frc[f].font;
+- specs[numspecs].glyph = glyphidx;
+- specs[numspecs].x = (short)xp;
+- specs[numspecs].y = (short)yp;
+- xp += runewidth;
+- numspecs++;
+ }
+
++ /* Cleanup and get ready for next segment. */
++ hbcleanup(&shaped);
+ return numspecs;
+ }
+
+ void
+-xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
++xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x…
+ {
+- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
+ int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
+ width = charlen * win.cw;
+ Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
+ XRenderColor colfg, colbg;
+@@ -1511,23 +1527,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Gly…
+ void
+ xdrawglyph(Glyph g, int x, int y)
+ {
+ int numspecs;
+- XftGlyphFontSpec spec;
++ XftGlyphFontSpec *specs = xw.specbuf;
+
+- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
+- xdrawglyphfontspecs(&spec, g, numspecs, x, y);
++ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y);
++ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? …
+ }
+
+ void
+-xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
++xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int…
+ {
+ Color drawcol;
+
+ /* remove the old cursor */
+ if (selected(ox, oy))
+ og.mode ^= ATTR_REVERSE;
+- xdrawglyph(og, ox, oy);
++
++ /* Redraw the line where cursor was previously.
++ * It will restore the ligatures broken by the cursor. */
++ xdrawline(line, 0, oy, len);
+
+ if (IS_SET(MODE_HIDE))
+ return;
+
+@@ -1659,30 +1678,30 @@ xdrawline(Line line, int x1, int y1, int x2)
+ int i, x, ox, numspecs;
+ Glyph base, new;
+ XftGlyphFontSpec *specs = xw.specbuf;
+
+- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
+ i = ox = 0;
+- for (x = x1; x < x2 && i < numspecs; x++) {
++ for (x = x1; x < x2; x++) {
+ new = line[x];
+ if (new.mode == ATTR_WDUMMY)
+ continue;
+ if (selected(x, y1))
+ new.mode ^= ATTR_REVERSE;
+- if (i > 0 && ATTRCMP(base, new)) {
+- xdrawglyphfontspecs(specs, base, i, ox, y1);
+- specs += i;
+- numspecs -= i;
++ if ((i > 0) && ATTRCMP(base, new)) {
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - …
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x …
+ i = 0;
+ }
+ if (i == 0) {
+ ox = x;
+ base = new;
+ }
+ i++;
+ }
+- if (i > 0)
+- xdrawglyphfontspecs(specs, base, i, ox, y1);
++ if (i > 0) {
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,…
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox);
++ }
+ }
+
+ void
+ xfinishdraw(void)
diff --git a/st.suckless.org/patches/ligatures/index.md b/st.suckless.org/patch…
@@ -28,13 +28,13 @@ Boxdraw
Download
--------
**0.9.2**:
-* [st-ligatures-0.9.2](0.9.2/st-ligatures-20240427-0.9.2.diff)
-* [st-ligatures-scrollback-0.9.2](0.9.2/st-ligatures-scrollback-20240427-0.9.2…
-* [st-ligatures-scrollback-ringbuffer-0.9.2](0.9.2/st-ligatures-scrollback-rin…
-* [st-ligatures-alpha-0.9.2](0.9.2/st-ligatures-alpha-20240427-0.9.2.diff)
-* [st-ligatures-alpha-scrollback-0.9.2](0.9.2/st-ligatures-alpha-scrollback-20…
-* [st-ligatures-alpha-scrollback-ringbuffer-0.9.2](0.9.2/st-ligatures-alpha-sc…
-* [st-ligatures-boxdraw-0.9.2](0.9.2/st-ligatures-boxdraw-20240427-0.9.2.diff)
+* [st-ligatures-0.9.2](0.9.2/st-ligatures-20241226-0.9.2.diff)
+* [st-ligatures-scrollback-0.9.2](0.9.2/st-ligatures-scrollback-20241226-0.9.2…
+* [st-ligatures-scrollback-ringbuffer-0.9.2](0.9.2/st-ligatures-scrollback-rin…
+* [st-ligatures-alpha-0.9.2](0.9.2/st-ligatures-alpha-20241226-0.9.2.diff)
+* [st-ligatures-alpha-scrollback-0.9.2](0.9.2/st-ligatures-alpha-scrollback-20…
+* [st-ligatures-alpha-scrollback-ringbuffer-0.9.2](0.9.2/st-ligatures-alpha-sc…
+* [st-ligatures-boxdraw-0.9.2](0.9.2/st-ligatures-boxdraw-20241226-0.9.2.diff)
**0.9**:
* [st-ligatures-0.9](0.9/st-ligatures-20240105-0.9.diff)
You are viewing proxied material from suckless.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.