[wiki] [sites] [st][patches][ligatures] || Alexander Rogachev

From: <git_AT_suckless.org>
Date: Thu, 09 Jan 2025 22:55:03 +0100

commit 77456c4698f33511b0652c9dfd6f2904b4797584
Author: Alexander Rogachev <sorryforbadname_AT_gmail.com>
Date: Fri Jan 10 01:55:48 2025 +0400

    [st][patches][ligatures]
    
    Some code cleanup and refactoring.

diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-20240427-0.9.2.diff b/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-20241226-0.9.2.diff
similarity index 71%
rename from st.suckless.org/patches/ligatures/0.9.2/st-ligatures-20240427-0.9.2.diff
rename to st.suckless.org/patches/ligatures/0.9.2/st-ligatures-20241226-0.9.2.diff
index b6832a67..698516fe 100644
--- a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-20240427-0.9.2.diff
+++ b/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-20241226-0.9.2.diff
_AT_@ -1,8 +1,9 @@
 diff --git a/Makefile b/Makefile
-index 470ac86..38240da 100644
+index 15db421..dfcea0f 100644
 --- a/Makefile
 +++ b/Makefile
-_AT_@ -4,7 +4,7 @@
+_AT_@ -3,9 +3,9 @@
+ .POSIX:
  
  include config.mk
  
_AT_@ -11,7 +12,9 @@ index 470ac86..38240da 100644
  OBJ = $(SRC:.c=.o)
  
  all: st
-_AT_@ -22,7 +22,8 @@ config.h:
+
+_AT_@ -15,9 +15,10 @@ config.h:
+ .c.o:
          $(CC) $(STCFLAGS) -c $<
  
  st.o: config.h st.h win.h
_AT_@ -21,11 +24,13 @@ index 470ac86..38240da 100644
  
  $(OBJ): config.h config.mk
  
+ st: $(OBJ)
 diff --git a/config.mk b/config.mk
-index 1e306f8..3e13e53 100644
+index fdc29a7..6833b3b 100644
 --- a/config.mk
 +++ b/config.mk
-_AT_@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config
+_AT_@ -14,12 +14,14 @@ PKG_CONFIG = pkg-config
+
  # includes and libs
  INCS = -I$(X11INC) \
         `$(PKG_CONFIG) --cflags fontconfig` \
_AT_@ -40,6 +45,19 @@ index 1e306f8..3e13e53 100644
  
  # flags
  STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
+ STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS)
+_AT_@ -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
_AT_@ -192,10 +210,11 @@ index 0000000..3b0ef44
 +void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
 +void hbcleanup(HbTransformData *);
 diff --git a/st.c b/st.c
-index 62def59..041c6d8 100644
+index b9f66e7..da33a85 100644
 --- a/st.c
 +++ b/st.c
-_AT_@ -2640,7 +2640,8 @@ draw(void)
+_AT_@ -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],
_AT_@ -205,11 +224,13 @@ index 62def59..041c6d8 100644
          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
-_AT_@ -11,7 +11,8 @@
+_AT_@ -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) : (x)
_AT_@ -219,11 +240,13 @@ index fd3b0d8..142fdfe 100644
                                  (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
-_AT_@ -25,7 +25,7 @@ enum win_mode {
+_AT_@ -24,9 +24,9 @@ enum win_mode {
+ };
  
  void xbell(void);
  void xclipcopy(void);
_AT_@ -232,11 +255,13 @@ index 6de960d..94679e4 100644
  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 2a3bd38..66605ae 100644
+index bd23686..2bf3b72 100644
 --- a/x.c
 +++ b/x.c
-_AT_@ -19,6 +19,7 @@ char *argv0;
+_AT_@ -18,8 +18,9 @@
+ char *argv0;
  #include "arg.h"
  #include "st.h"
  #include "win.h"
_AT_@ -244,7 +269,9 @@ index 2a3bd38..66605ae 100644
  
  /* types used in config.h */
  typedef struct {
-_AT_@ -141,8 +142,9 @@ typedef struct {
+ uint mod;
+_AT_@ -140,10 +141,11 @@ typedef struct {
+ GC gc;
  } DC;
  
  static inline ushort sixd_to_16bit(int);
_AT_@ -255,7 +282,9 @@ index 2a3bd38..66605ae 100644
  static void xdrawglyph(Glyph, int, int);
  static void xclear(int, int, int, int);
  static int xgeommasktogravity(int);
-_AT_@ -757,7 +759,7 @@ xresize(int col, int row)
+ static int ximopen(Display *);
+_AT_@ -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 */
_AT_@ -264,7 +293,9 @@ index 2a3bd38..66605ae 100644
  }
  
  ushort
-_AT_@ -1062,6 +1064,9 @@ xunloadfont(Font *f)
+ sixd_to_16bit(int x)
+_AT_@ -1061,8 +1063,11 @@ xunloadfont(Font *f)
+
  void
  xunloadfonts(void)
  {
_AT_@ -274,7 +305,9 @@ index 2a3bd38..66605ae 100644
          /* Free the loaded fonts in the font cache. */
          while (frclen > 0)
                  XftFontClose(xw.dpy, frc[--frclen].font);
-_AT_@ -1185,7 +1190,7 @@ xinit(int cols, int rows)
+
+_AT_@ -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 */
_AT_@ -283,7 +316,9 @@ index 2a3bd38..66605ae 100644
  
          /* Xft rendering context */
          xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
-_AT_@ -1239,6 +1244,22 @@ xinit(int cols, int rows)
+
+_AT_@ -1238,144 +1243,155 @@ xinit(int cols, int rows)
+ if (xsel.xtarget == None)
                  xsel.xtarget = XA_STRING;
  }
  
_AT_@ -306,28 +341,42 @@ index 2a3bd38..66605ae 100644
  int
  xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
  {
-_AT_@ -1253,128 +1274,156 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
+ float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp;
+- 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.0f);
+ Rune rune;
+ FT_UInt glyphidx;
+ FcResult fcres;
          FcPattern *fcpattern, *fontpattern;
          FcFontSet *fcsets[] = { NULL };
          FcCharSet *fccharset;
 - int i, f, numspecs = 0;
-+ int i, f, length = 0, start = 0, numspecs = 0;
++ int f, code_idx, 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) {
+- 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;
++ /* Initial values. */
++ xresetfontsettings(mode, &font, &frcflags);
  
- /* Skip dummy wide-character spacing. */
+- /* Skip dummy wide-character spacing. */
 - if (mode == ATTR_WDUMMY)
-+ if (mode & ATTR_WDUMMY && i < (len - 1))
++ /* 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. */
_AT_@ -345,31 +394,31 @@ index 2a3bd38..66605ae 100644
 - } 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;
-- }
++ /* Advance the drawing cursor if we've moved to a new cluster */
++ if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluster) {
++ xp += runewidth;
++ cluster_xp = xp;
++ cluster_yp = yp;
+ }
  
 - /* Lookup character index with default font. */
 - glyphidx = XftCharIndex(xw.dpy, font->match, rune);
 - if (glyphidx) {
-- specs[numspecs].font = font->match;
++ 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;
-- numspecs++;
++ specs[numspecs].glyph = shaped.glyphs[code_idx].codepoint;
++ specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.);
++ specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.);
++ cluster_xp += shaped.positions[code_idx].x_advance / 64.;
++ cluster_yp += shaped.positions[code_idx].y_advance / 64.;
+ numspecs++;
 - continue;
 - }
 -
_AT_@ -383,105 +432,18 @@ index 2a3bd38..66605ae 100644
 - if (!glyphidx && frc[f].flags == frcflags
 - && frc[f].unicodep == rune) {
 - break;
-+ /* Shape the segment. */
-+ hbtransform(&shaped, font->match, glyphs, start, length);
-+ runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE) ? 2.0f : 1.0f);
-+ cluster_xp = xp; cluster_yp = yp;
-+ for (int code_idx = 0; code_idx < shaped.count; code_idx++) {
-+ int idx = shaped.glyphs[code_idx].cluster;
-+
-+ if (glyphs[start + idx].mode & ATTR_WDUMMY)
-+ continue;
-+
-+ /* Advance the drawing cursor if we've moved to a new cluster */
-+ if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluster) {
-+ xp += runewidth;
-+ cluster_xp = xp;
-+ cluster_yp = yp;
-+ runewidth = win.cw * ((glyphs[start + idx].mode & ATTR_WIDE) ? 2.0f : 1.0f);
-+ }
-+
-+ 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].codepoint;
-+ specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.);
-+ specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.);
-+ cluster_xp += shaped.positions[code_idx].x_advance / 64.;
-+ cluster_yp += shaped.positions[code_idx].y_advance / 64.;
-+ numspecs++;
-+ } else {
-+ /* If it's not found, try to fetch it through the font cache. */
-+ rune = glyphs[start + idx].u;
-+ 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;
-+ }
-+ }
-+
-+ /* 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));
-+ }
-+
-+ frc[frclen].font = XftFontOpenPattern(xw.dpy,
-+ fontpattern);
-+ if (!frc[frclen].font)
-+ die("XftFontOpenPattern failed seeking fallback font: %s
",
-+ strerror(errno));
-+ frc[frclen].flags = frcflags;
-+ frc[frclen].unicodep = rune;
-+
-+ glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
-+
-+ 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++;
++ } else {
++ /* If it's not found, try to fetch it through the font cache. */
++ rune = glyphs[idx].u;
++ 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;
 + }
                          }
 - }
_AT_@ -492,10 +454,7 @@ index 2a3bd38..66605ae 100644
 - 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
_AT_@ -522,13 +481,58 @@ index 2a3bd38..66605ae 100644
 - if (frclen >= frccap) {
 - frccap += 16;
 - frc = xrealloc(frc, frccap * sizeof(Fontcache));
-+ /* Determine font for glyph if different from previous glyph. */
-+ if (prevmode != mode) {
-+ prevmode = mode;
-+ xresetfontsettings(mode, &font, &frcflags);
-+ yp = winy + font->ascent;
++ /* 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));
++ }
++
++ frc[frclen].font = XftFontOpenPattern(xw.dpy,
++ fontpattern);
++ if (!frc[frclen].font)
++ die("XftFontOpenPattern failed seeking fallback font: %s
",
++ strerror(errno));
++ frc[frclen].flags = frcflags;
++ frc[frclen].unicodep = rune;
++
++ glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
++
++ f = frclen;
++ frclen++;
++
++ FcPatternDestroy(fcpattern);
++ FcCharSetDestroy(fccharset);
                          }
--
+ 
 -			frc[frclen].font = XftFontOpenPattern(xw.dpy,
 -					fontpattern);
 -			if (!frc[frclen].font)
_AT_@ -544,6 +548,11 @@ index 2a3bd38..66605ae 100644
 -
 -			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;
_AT_@ -554,6 +563,8 @@ index 2a3bd38..66605ae 100644
 -		numspecs++;
  	}
  
++	/* Cleanup and get ready for next segment. */
++	hbcleanup(&shaped);
  	return numspecs;
  }
  
_AT_@ -565,7 +576,9 @@ index 2a3bd38..66605ae 100644
  	int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
  	    width = charlen * win.cw;
  	Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
-_AT_@ -1510,21 +1559,24 @@ void
+ 	XRenderColor colfg, colbg;
+_AT_@ -1509,23 +1525,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
+ void
  xdrawglyph(Glyph g, int x, int y)
  {
  	int numspecs;
_AT_@ -595,7 +608,9 @@ index 2a3bd38..66605ae 100644
  
  	if (IS_SET(MODE_HIDE))
  		return;
-_AT_@ -1652,18 +1704,16 @@ xdrawline(Line line, int x1, int y1, int x2)
+ 
+_AT_@ -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;
  
_AT_@ -618,7 +633,8 @@ index 2a3bd38..66605ae 100644
  			i = 0;
  		}
  		if (i == 0) {
-_AT_@ -1672,8 +1722,10 @@ xdrawline(Line line, int x1, int y1, int x2)
+ 			ox = x;
+ 			base = new;
  		}
  		i++;
  	}
_AT_@ -631,3 +647,4 @@ index 2a3bd38..66605ae 100644
  }
  
  void
+ xfinishdraw(void)
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-20240427-0.9.2.diff b/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-20241226-0.9.2.diff
similarity index 71%
rename from st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-20240427-0.9.2.diff
rename to st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-20241226-0.9.2.diff
index a4ac3f7d..7a8aa2ff 100644
--- a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-20240427-0.9.2.diff
+++ b/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-20241226-0.9.2.diff
_AT_@ -1,8 +1,9 @@
 diff --git a/Makefile b/Makefile
-index 470ac86..38240da 100644
+index 15db421..dfcea0f 100644
 --- a/Makefile
 +++ b/Makefile
-_AT_@ -4,7 +4,7 @@
+_AT_@ -3,9 +3,9 @@
+ .POSIX:
  
  include config.mk
  
_AT_@ -11,7 +12,9 @@ index 470ac86..38240da 100644
  OBJ = $(SRC:.c=.o)
  
  all: st
-_AT_@ -22,7 +22,8 @@ config.h:
+ 
+_AT_@ -15,9 +15,10 @@ config.h:
+ .c.o:
  	$(CC) $(STCFLAGS) -c $<
  
  st.o: config.h st.h win.h
_AT_@ -21,19 +24,20 @@ index 470ac86..38240da 100644
  
  $(OBJ): config.h config.mk
  
+ st: $(OBJ)
 diff --git a/config.mk b/config.mk
-index 47c615e..d7439a3 100644
+index 069a6c2..977b7c7 100644
 --- a/config.mk
 +++ b/config.mk
-_AT_@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config
+_AT_@ -14,12 +14,14 @@ 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 \
+ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\
         `$(PKG_CONFIG) --libs fontconfig` \
 -       `$(PKG_CONFIG) --libs freetype2`
 +       `$(PKG_CONFIG) --libs freetype2` \
_AT_@ -41,9 +45,22 @@ index 47c615e..d7439a3 100644
  
  # flags
  STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
+ STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS)
+_AT_@ -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..58d534b
+index 0000000..99412c8
 --- /dev/null
 +++ b/hb.c
 _AT_@ -0,0 +1,125 @@
_AT_@ -193,10 +210,11 @@ index 0000000..3b0ef44
 +void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
 +void hbcleanup(HbTransformData *);
 diff --git a/st.c b/st.c
-index 62def59..041c6d8 100644
+index b9f66e7..da33a85 100644
 --- a/st.c
 +++ b/st.c
-_AT_@ -2640,7 +2640,8 @@ draw(void)
+_AT_@ -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],
_AT_@ -206,11 +224,13 @@ index 62def59..041c6d8 100644
  	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
-_AT_@ -11,7 +11,8 @@
+_AT_@ -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) : (x)
_AT_@ -220,11 +240,13 @@ index 9f91e2a..b1a6256 100644
  				(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
-_AT_@ -25,7 +25,7 @@ enum win_mode {
+_AT_@ -24,9 +24,9 @@ enum win_mode {
+ };
  
  void xbell(void);
  void xclipcopy(void);
_AT_@ -233,11 +255,13 @@ index 6de960d..94679e4 100644
  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 27e81d1..0632636 100644
+index 1e12ac8..b0819ac 100644
 --- a/x.c
 +++ b/x.c
-_AT_@ -19,6 +19,7 @@ char *argv0;
+_AT_@ -18,8 +18,9 @@
+ char *argv0;
  #include "arg.h"
  #include "st.h"
  #include "win.h"
_AT_@ -245,7 +269,9 @@ index 27e81d1..0632636 100644
  
  /* types used in config.h */
  typedef struct {
-_AT_@ -142,8 +143,9 @@ typedef struct {
+ 	uint mod;
+_AT_@ -141,10 +142,11 @@ typedef struct {
+ 	GC gc;
  } DC;
  
  static inline ushort sixd_to_16bit(int);
_AT_@ -256,7 +282,9 @@ index 27e81d1..0632636 100644
  static void xdrawglyph(Glyph, int, int);
  static void xclear(int, int, int, int);
  static int xgeommasktogravity(int);
-_AT_@ -759,7 +761,7 @@ xresize(int col, int row)
+ static int ximopen(Display *);
+_AT_@ -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 */
_AT_@ -265,7 +293,9 @@ index 27e81d1..0632636 100644
  }
  
  ushort
-_AT_@ -1071,6 +1073,9 @@ xunloadfont(Font *f)
+ sixd_to_16bit(int x)
+_AT_@ -1070,8 +1072,11 @@ xunloadfont(Font *f)
+ 
  void
  xunloadfonts(void)
  {
_AT_@ -275,7 +305,9 @@ index 27e81d1..0632636 100644
  	/* Free the loaded fonts in the font cache.  */
  	while (frclen > 0)
  		XftFontClose(xw.dpy, frc[--frclen].font);
-_AT_@ -1202,7 +1207,7 @@ xinit(int cols, int rows)
+ 
+_AT_@ -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 */
_AT_@ -284,7 +316,9 @@ index 27e81d1..0632636 100644
  
  	/* Xft rendering context */
  	xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
-_AT_@ -1256,6 +1261,22 @@ xinit(int cols, int rows)
+ 
+_AT_@ -1255,144 +1260,155 @@ xinit(int cols, int rows)
+ 	if (xsel.xtarget == None)
  		xsel.xtarget = XA_STRING;
  }
  
_AT_@ -307,28 +341,42 @@ index 27e81d1..0632636 100644
  int
  xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
  {
-_AT_@ -1270,128 +1291,157 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
+ 	float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp;
+-	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.0f);
+ 	Rune rune;
+ 	FT_UInt glyphidx;
+ 	FcResult fcres;
  	FcPattern *fcpattern, *fontpattern;
  	FcFontSet *fcsets[] = { NULL };
  	FcCharSet *fccharset;
 -	int i, f, numspecs = 0;
-+	int i, f, length = 0, start = 0, numspecs = 0;
++	int f, code_idx, 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) {
+-	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;
++	/* Initial values. */
++	xresetfontsettings(mode, &font, &frcflags);
  
- 		/* Skip dummy wide-character spacing. */
+-		/* Skip dummy wide-character spacing. */
 -		if (mode == ATTR_WDUMMY)
-+		if (mode & ATTR_WDUMMY && i < (len - 1))
++	/* 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. */
_AT_@ -346,31 +394,31 @@ index 27e81d1..0632636 100644
 -			} 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;
--		}
++		/* Advance the drawing cursor if we've moved to a new cluster */
++		if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluster) {
++			xp += runewidth;
++			cluster_xp = xp;
++			cluster_yp = yp;
+ 		}
  
 -		/* Lookup character index with default font. */
 -		glyphidx = XftCharIndex(xw.dpy, font->match, rune);
 -		if (glyphidx) {
--			specs[numspecs].font = font->match;
++		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;
--			numspecs++;
++			specs[numspecs].glyph = shaped.glyphs[code_idx].codepoint;
++			specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.);
++			specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.);
++			cluster_xp += shaped.positions[code_idx].x_advance / 64.;
++			cluster_yp += shaped.positions[code_idx].y_advance / 64.;
+ 			numspecs++;
 -			continue;
 -		}
 -
_AT_@ -384,105 +432,18 @@ index 27e81d1..0632636 100644
 -			if (!glyphidx && frc[f].flags == frcflags
 -					&& frc[f].unicodep == rune) {
 -				break;
-+			/* Shape the segment. */
-+			hbtransform(&shaped, font->match, glyphs, start, length);
-+			runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE) ? 2.0f : 1.0f);
-+			cluster_xp = xp; cluster_yp = yp;
-+			for (int code_idx = 0; code_idx < shaped.count; code_idx++) {
-+				int idx = shaped.glyphs[code_idx].cluster;
-+
-+				if (glyphs[start + idx].mode & ATTR_WDUMMY)
-+					continue;
-+
-+				/* Advance the drawing cursor if we've moved to a new cluster */
-+				if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluster) {
-+					xp += runewidth;
-+					cluster_xp = xp;
-+					cluster_yp = yp;
-+					runewidth = win.cw * ((glyphs[start + idx].mode & ATTR_WIDE) ? 2.0f : 1.0f);
-+				}
-+
-+				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].codepoint;
-+					specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.);
-+					specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.);
-+					cluster_xp += shaped.positions[code_idx].x_advance / 64.;
-+					cluster_yp += shaped.positions[code_idx].y_advance / 64.;
-+					numspecs++;
-+				} else {
-+					/* If it's not found, try to fetch it through the font cache. */
-+					rune = glyphs[start + idx].u;
-+					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;
-+						}
-+					}
-+
-+					/* 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));
-+						}
-+
-+						frc[frclen].font = XftFontOpenPattern(xw.dpy,
-+								fontpattern);
-+						if (!frc[frclen].font)
-+							die("XftFontOpenPattern failed seeking fallback font: %s
",
-+								strerror(errno));
-+						frc[frclen].flags = frcflags;
-+						frc[frclen].unicodep = rune;
-+
-+						glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
-+
-+						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++;
++		} else {
++			/* If it's not found, try to fetch it through the font cache. */
++			rune = glyphs[idx].u;
++			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;
 +				}
  			}
 -		}
_AT_@ -493,10 +454,7 @@ index 27e81d1..0632636 100644
 -				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
_AT_@ -523,13 +481,58 @@ index 27e81d1..0632636 100644
 -			if (frclen >= frccap) {
 -				frccap += 16;
 -				frc = xrealloc(frc, frccap * sizeof(Fontcache));
-+			/* Determine font for glyph if different from previous glyph. */
-+			if (prevmode != mode) {
-+				prevmode = mode;
-+				xresetfontsettings(mode, &font, &frcflags);
-+				yp = winy + font->ascent;
++			/* 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));
++				}
++
++				frc[frclen].font = XftFontOpenPattern(xw.dpy,
++						fontpattern);
++				if (!frc[frclen].font)
++					die("XftFontOpenPattern failed seeking fallback font: %s
",
++						strerror(errno));
++				frc[frclen].flags = frcflags;
++				frc[frclen].unicodep = rune;
++
++				glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
++
++				f = frclen;
++				frclen++;
++
++				FcPatternDestroy(fcpattern);
++				FcCharSetDestroy(fccharset);
  			}
--
+ 
 -			frc[frclen].font = XftFontOpenPattern(xw.dpy,
 -					fontpattern);
 -			if (!frc[frclen].font)
_AT_@ -545,6 +548,11 @@ index 27e81d1..0632636 100644
 -
 -			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;
_AT_@ -555,6 +563,7 @@ index 27e81d1..0632636 100644
 -		numspecs++;
  	}
  
++	/* Cleanup and get ready for next segment. */
 +	hbcleanup(&shaped);
  	return numspecs;
  }
_AT_@ -567,7 +576,9 @@ index 27e81d1..0632636 100644
  	int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
  	    width = charlen * win.cw;
  	Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
-_AT_@ -1527,21 +1577,24 @@ void
+ 	XRenderColor colfg, colbg;
+_AT_@ -1526,23 +1542,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
+ void
  xdrawglyph(Glyph g, int x, int y)
  {
  	int numspecs;
_AT_@ -597,7 +608,9 @@ index 27e81d1..0632636 100644
  
  	if (IS_SET(MODE_HIDE))
  		return;
-_AT_@ -1669,18 +1722,16 @@ xdrawline(Line line, int x1, int y1, int x2)
+ 
+_AT_@ -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;
  
_AT_@ -620,7 +633,8 @@ index 27e81d1..0632636 100644
  			i = 0;
  		}
  		if (i == 0) {
-_AT_@ -1689,8 +1740,10 @@ xdrawline(Line line, int x1, int y1, int x2)
+ 			ox = x;
+ 			base = new;
  		}
  		i++;
  	}
_AT_@ -633,3 +647,4 @@ index 27e81d1..0632636 100644
  }
  
  void
+ xfinishdraw(void)
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-scrollback-20240427-0.9.2.diff b/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-scrollback-20241226-0.9.2.diff
similarity index 71%
rename from st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-scrollback-20240427-0.9.2.diff
rename to st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-scrollback-20241226-0.9.2.diff
index 3e9d6751..00abd127 100644
--- a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-scrollback-20240427-0.9.2.diff
+++ b/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-scrollback-20241226-0.9.2.diff
_AT_@ -1,8 +1,9 @@
 diff --git a/Makefile b/Makefile
-index 470ac86..38240da 100644
+index 15db421..dfcea0f 100644
 --- a/Makefile
 +++ b/Makefile
-_AT_@ -4,7 +4,7 @@
+_AT_@ -3,9 +3,9 @@
+ .POSIX:
  
  include config.mk
  
_AT_@ -11,7 +12,9 @@ index 470ac86..38240da 100644
  OBJ = $(SRC:.c=.o)
  
  all: st
-_AT_@ -22,7 +22,8 @@ config.h:
+ 
+_AT_@ -15,9 +15,10 @@ config.h:
+ .c.o:
  	$(CC) $(STCFLAGS) -c $<
  
  st.o: config.h st.h win.h
_AT_@ -21,19 +24,20 @@ index 470ac86..38240da 100644
  
  $(OBJ): config.h config.mk
  
+ st: $(OBJ)
 diff --git a/config.mk b/config.mk
-index 47c615e..d7439a3 100644
+index 069a6c2..977b7c7 100644
 --- a/config.mk
 +++ b/config.mk
-_AT_@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config
+_AT_@ -14,12 +14,14 @@ 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 \
+ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\
         `$(PKG_CONFIG) --libs fontconfig` \
 -       `$(PKG_CONFIG) --libs freetype2`
 +       `$(PKG_CONFIG) --libs freetype2` \
_AT_@ -41,6 +45,19 @@ index 47c615e..d7439a3 100644
  
  # flags
  STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
+ STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS)
+_AT_@ -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
_AT_@ -193,25 +210,27 @@ index 0000000..3b0ef44
 +void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
 +void hbcleanup(HbTransformData *);
 diff --git a/st.c b/st.c
-index 79ee9ba..454771d 100644
+index 2478942..bba90d3 100644
 --- a/st.c
 +++ b/st.c
-_AT_@ -2711,7 +2711,9 @@ draw(void)
+_AT_@ -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.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 78762a2..01eea49 100644
 --- a/st.h
 +++ b/st.h
-_AT_@ -11,7 +11,8 @@
+_AT_@ -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) : (x)
_AT_@ -221,11 +240,13 @@ index 78762a2..01eea49 100644
  				(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
-_AT_@ -25,7 +25,7 @@ enum win_mode {
+_AT_@ -24,9 +24,9 @@ enum win_mode {
+ };
  
  void xbell(void);
  void xclipcopy(void);
_AT_@ -234,11 +255,13 @@ index 6de960d..94679e4 100644
  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 27e81d1..5e11c1f 100644
+index 1e12ac8..b0819ac 100644
 --- a/x.c
 +++ b/x.c
-_AT_@ -19,6 +19,7 @@ char *argv0;
+_AT_@ -18,8 +18,9 @@
+ char *argv0;
  #include "arg.h"
  #include "st.h"
  #include "win.h"
_AT_@ -246,7 +269,9 @@ index 27e81d1..5e11c1f 100644
  
  /* types used in config.h */
  typedef struct {
-_AT_@ -142,8 +143,9 @@ typedef struct {
+ 	uint mod;
+_AT_@ -141,10 +142,11 @@ typedef struct {
+ 	GC gc;
  } DC;
  
  static inline ushort sixd_to_16bit(int);
_AT_@ -257,7 +282,9 @@ index 27e81d1..5e11c1f 100644
  static void xdrawglyph(Glyph, int, int);
  static void xclear(int, int, int, int);
  static int xgeommasktogravity(int);
-_AT_@ -759,7 +761,7 @@ xresize(int col, int row)
+ static int ximopen(Display *);
+_AT_@ -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 */
_AT_@ -266,7 +293,9 @@ index 27e81d1..5e11c1f 100644
  }
  
  ushort
-_AT_@ -1071,6 +1073,9 @@ xunloadfont(Font *f)
+ sixd_to_16bit(int x)
+_AT_@ -1070,8 +1072,11 @@ xunloadfont(Font *f)
+ 
  void
  xunloadfonts(void)
  {
_AT_@ -276,7 +305,9 @@ index 27e81d1..5e11c1f 100644
  	/* Free the loaded fonts in the font cache.  */
  	while (frclen > 0)
  		XftFontClose(xw.dpy, frc[--frclen].font);
-_AT_@ -1202,7 +1207,7 @@ xinit(int cols, int rows)
+ 
+_AT_@ -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 */
_AT_@ -285,7 +316,9 @@ index 27e81d1..5e11c1f 100644
  
  	/* Xft rendering context */
  	xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
-_AT_@ -1256,6 +1261,22 @@ xinit(int cols, int rows)
+ 
+_AT_@ -1255,144 +1260,155 @@ xinit(int cols, int rows)
+ 	if (xsel.xtarget == None)
  		xsel.xtarget = XA_STRING;
  }
  
_AT_@ -308,28 +341,42 @@ index 27e81d1..5e11c1f 100644
  int
  xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
  {
-_AT_@ -1270,128 +1291,156 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
+ 	float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp;
+-	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.0f);
+ 	Rune rune;
+ 	FT_UInt glyphidx;
+ 	FcResult fcres;
  	FcPattern *fcpattern, *fontpattern;
  	FcFontSet *fcsets[] = { NULL };
  	FcCharSet *fccharset;
 -	int i, f, numspecs = 0;
-+	int i, f, length = 0, start = 0, numspecs = 0;
++	int f, code_idx, 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) {
+-	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;
++	/* Initial values. */
++	xresetfontsettings(mode, &font, &frcflags);
  
- 		/* Skip dummy wide-character spacing. */
+-		/* Skip dummy wide-character spacing. */
 -		if (mode == ATTR_WDUMMY)
-+		if (mode & ATTR_WDUMMY && i < (len - 1))
++	/* 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. */
_AT_@ -347,31 +394,31 @@ index 27e81d1..5e11c1f 100644
 -			} 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;
--		}
++		/* Advance the drawing cursor if we've moved to a new cluster */
++		if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluster) {
++			xp += runewidth;
++			cluster_xp = xp;
++			cluster_yp = yp;
+ 		}
  
 -		/* Lookup character index with default font. */
 -		glyphidx = XftCharIndex(xw.dpy, font->match, rune);
 -		if (glyphidx) {
--			specs[numspecs].font = font->match;
++		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;
--			numspecs++;
++			specs[numspecs].glyph = shaped.glyphs[code_idx].codepoint;
++			specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.);
++			specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.);
++			cluster_xp += shaped.positions[code_idx].x_advance / 64.;
++			cluster_yp += shaped.positions[code_idx].y_advance / 64.;
+ 			numspecs++;
 -			continue;
 -		}
 -
_AT_@ -385,105 +432,18 @@ index 27e81d1..5e11c1f 100644
 -			if (!glyphidx && frc[f].flags == frcflags
 -					&& frc[f].unicodep == rune) {
 -				break;
-+			/* Shape the segment. */
-+			hbtransform(&shaped, font->match, glyphs, start, length);
-+			runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE) ? 2.0f : 1.0f);
-+			cluster_xp = xp; cluster_yp = yp;
-+			for (int code_idx = 0; code_idx < shaped.count; code_idx++) {
-+				int idx = shaped.glyphs[code_idx].cluster;
-+
-+				if (glyphs[start + idx].mode & ATTR_WDUMMY)
-+					continue;
-+
-+				/* Advance the drawing cursor if we've moved to a new cluster */
-+				if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluster) {
-+					xp += runewidth;
-+					cluster_xp = xp;
-+					cluster_yp = yp;
-+					runewidth = win.cw * ((glyphs[start + idx].mode & ATTR_WIDE) ? 2.0f : 1.0f);
-+				}
-+
-+				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].codepoint;
-+					specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.);
-+					specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.);
-+					cluster_xp += shaped.positions[code_idx].x_advance / 64.;
-+					cluster_yp += shaped.positions[code_idx].y_advance / 64.;
-+					numspecs++;
-+				} else {
-+					/* If it's not found, try to fetch it through the font cache. */
-+					rune = glyphs[start + idx].u;
-+					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;
-+						}
-+					}
-+
-+					/* 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));
-+						}
-+
-+						frc[frclen].font = XftFontOpenPattern(xw.dpy,
-+								fontpattern);
-+						if (!frc[frclen].font)
-+							die("XftFontOpenPattern failed seeking fallback font: %s
",
-+								strerror(errno));
-+						frc[frclen].flags = frcflags;
-+						frc[frclen].unicodep = rune;
-+
-+						glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
-+
-+						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++;
++		} else {
++			/* If it's not found, try to fetch it through the font cache. */
++			rune = glyphs[idx].u;
++			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;
 +				}
  			}
 -		}
_AT_@ -494,10 +454,7 @@ index 27e81d1..5e11c1f 100644
 -				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
_AT_@ -524,13 +481,58 @@ index 27e81d1..5e11c1f 100644
 -			if (frclen >= frccap) {
 -				frccap += 16;
 -				frc = xrealloc(frc, frccap * sizeof(Fontcache));
-+			/* Determine font for glyph if different from previous glyph. */
-+			if (prevmode != mode) {
-+				prevmode = mode;
-+				xresetfontsettings(mode, &font, &frcflags);
-+				yp = winy + font->ascent;
++			/* 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));
++				}
++
++				frc[frclen].font = XftFontOpenPattern(xw.dpy,
++						fontpattern);
++				if (!frc[frclen].font)
++					die("XftFontOpenPattern failed seeking fallback font: %s
",
++						strerror(errno));
++				frc[frclen].flags = frcflags;
++				frc[frclen].unicodep = rune;
++
++				glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
++
++				f = frclen;
++				frclen++;
++
++				FcPatternDestroy(fcpattern);
++				FcCharSetDestroy(fccharset);
  			}
--
+ 
 -			frc[frclen].font = XftFontOpenPattern(xw.dpy,
 -					fontpattern);
 -			if (!frc[frclen].font)
_AT_@ -546,6 +548,11 @@ index 27e81d1..5e11c1f 100644
 -
 -			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;
_AT_@ -556,6 +563,8 @@ index 27e81d1..5e11c1f 100644
 -		numspecs++;
  	}
  
++	/* Cleanup and get ready for next segment. */
++	hbcleanup(&shaped);
  	return numspecs;
  }
  
_AT_@ -567,7 +576,9 @@ index 27e81d1..5e11c1f 100644
  	int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
  	    width = charlen * win.cw;
  	Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
-_AT_@ -1527,21 +1576,24 @@ void
+ 	XRenderColor colfg, colbg;
+_AT_@ -1526,23 +1542,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
+ void
  xdrawglyph(Glyph g, int x, int y)
  {
  	int numspecs;
_AT_@ -597,7 +608,9 @@ index 27e81d1..5e11c1f 100644
  
  	if (IS_SET(MODE_HIDE))
  		return;
-_AT_@ -1669,18 +1721,16 @@ xdrawline(Line line, int x1, int y1, int x2)
+ 
+_AT_@ -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;
  
_AT_@ -620,7 +633,8 @@ index 27e81d1..5e11c1f 100644
  			i = 0;
  		}
  		if (i == 0) {
-_AT_@ -1689,8 +1739,10 @@ xdrawline(Line line, int x1, int y1, int x2)
+ 			ox = x;
+ 			base = new;
  		}
  		i++;
  	}
_AT_@ -633,3 +647,4 @@ index 27e81d1..5e11c1f 100644
  }
  
  void
+ xfinishdraw(void)
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-scrollback-ringbuffer-20240427-0.9.2.diff b/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-scrollback-ringbuffer-20241226-0.9.2.diff
similarity index 71%
rename from st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-scrollback-ringbuffer-20240427-0.9.2.diff
rename to st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-scrollback-ringbuffer-20241226-0.9.2.diff
index 5d03b4df..49f33829 100644
--- a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-scrollback-ringbuffer-20240427-0.9.2.diff
+++ b/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-scrollback-ringbuffer-20241226-0.9.2.diff
_AT_@ -1,8 +1,9 @@
 diff --git a/Makefile b/Makefile
-index 470ac86..38240da 100644
+index 15db421..dfcea0f 100644
 --- a/Makefile
 +++ b/Makefile
-_AT_@ -4,7 +4,7 @@
+_AT_@ -3,9 +3,9 @@
+ .POSIX:
  
  include config.mk
  
_AT_@ -11,7 +12,9 @@ index 470ac86..38240da 100644
  OBJ = $(SRC:.c=.o)
  
  all: st
-_AT_@ -22,7 +22,8 @@ config.h:
+ 
+_AT_@ -15,9 +15,10 @@ config.h:
+ .c.o:
  	$(CC) $(STCFLAGS) -c $<
  
  st.o: config.h st.h win.h
_AT_@ -21,19 +24,20 @@ index 470ac86..38240da 100644
  
  $(OBJ): config.h config.mk
  
+ st: $(OBJ)
 diff --git a/config.mk b/config.mk
-index 47c615e..d7439a3 100644
+index 069a6c2..977b7c7 100644
 --- a/config.mk
 +++ b/config.mk
-_AT_@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config
+_AT_@ -14,12 +14,14 @@ 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 \
+ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\
         `$(PKG_CONFIG) --libs fontconfig` \
 -       `$(PKG_CONFIG) --libs freetype2`
 +       `$(PKG_CONFIG) --libs freetype2` \
_AT_@ -41,6 +45,19 @@ index 47c615e..d7439a3 100644
  
  # flags
  STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
+ STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS)
+_AT_@ -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
_AT_@ -193,25 +210,27 @@ index 0000000..3b0ef44
 +void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
 +void hbcleanup(HbTransformData *);
 diff --git a/st.c b/st.c
-index c44797b..18aa1bf 100644
+index d9b163e..fbca4ba 100644
 --- a/st.c
 +++ b/st.c
-_AT_@ -2759,7 +2759,9 @@ draw(void)
+_AT_@ -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
-_AT_@ -11,7 +11,8 @@
+_AT_@ -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) : (x)
_AT_@ -221,11 +240,13 @@ index 073851a..d0b071d 100644
  				(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
-_AT_@ -25,7 +25,7 @@ enum win_mode {
+_AT_@ -24,9 +24,9 @@ enum win_mode {
+ };
  
  void xbell(void);
  void xclipcopy(void);
_AT_@ -234,11 +255,13 @@ index 6de960d..94679e4 100644
  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 b81f5be..c1611bb 100644
+index c497e53..a213e52 100644
 --- a/x.c
 +++ b/x.c
-_AT_@ -19,6 +19,7 @@ char *argv0;
+_AT_@ -18,8 +18,9 @@
+ char *argv0;
  #include "arg.h"
  #include "st.h"
  #include "win.h"
_AT_@ -246,7 +269,9 @@ index b81f5be..c1611bb 100644
  
  /* types used in config.h */
  typedef struct {
-_AT_@ -144,8 +145,9 @@ typedef struct {
+ 	uint mod;
+_AT_@ -143,10 +144,11 @@ typedef struct {
+ 	GC gc;
  } DC;
  
  static inline ushort sixd_to_16bit(int);
_AT_@ -257,7 +282,9 @@ index b81f5be..c1611bb 100644
  static void xdrawglyph(Glyph, int, int);
  static void xclear(int, int, int, int);
  static int xgeommasktogravity(int);
-_AT_@ -761,7 +763,7 @@ xresize(int col, int row)
+ static int ximopen(Display *);
+_AT_@ -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 */
_AT_@ -266,7 +293,9 @@ index b81f5be..c1611bb 100644
  }
  
  ushort
-_AT_@ -1073,6 +1075,9 @@ xunloadfont(Font *f)
+ sixd_to_16bit(int x)
+_AT_@ -1072,8 +1074,11 @@ xunloadfont(Font *f)
+ 
  void
  xunloadfonts(void)
  {
_AT_@ -276,7 +305,9 @@ index b81f5be..c1611bb 100644
  	/* Free the loaded fonts in the font cache.  */
  	while (frclen > 0)
  		XftFontClose(xw.dpy, frc[--frclen].font);
-_AT_@ -1204,7 +1209,7 @@ xinit(int cols, int rows)
+ 
+_AT_@ -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 */
_AT_@ -285,7 +316,9 @@ index b81f5be..c1611bb 100644
  
  	/* Xft rendering context */
  	xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
-_AT_@ -1258,6 +1263,22 @@ xinit(int cols, int rows)
+ 
+_AT_@ -1257,144 +1262,155 @@ xinit(int cols, int rows)
+ 	if (xsel.xtarget == None)
  		xsel.xtarget = XA_STRING;
  }
  
_AT_@ -308,28 +341,42 @@ index b81f5be..c1611bb 100644
  int
  xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
  {
-_AT_@ -1272,128 +1293,156 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
+ 	float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp;
+-	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.0f);
+ 	Rune rune;
+ 	FT_UInt glyphidx;
+ 	FcResult fcres;
  	FcPattern *fcpattern, *fontpattern;
  	FcFontSet *fcsets[] = { NULL };
  	FcCharSet *fccharset;
 -	int i, f, numspecs = 0;
-+	int i, f, length = 0, start = 0, numspecs = 0;
++	int f, code_idx, 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) {
+-	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;
++	/* Initial values. */
++	xresetfontsettings(mode, &font, &frcflags);
  
- 		/* Skip dummy wide-character spacing. */
+-		/* Skip dummy wide-character spacing. */
 -		if (mode == ATTR_WDUMMY)
-+		if (mode & ATTR_WDUMMY && i < (len - 1))
++	/* 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. */
_AT_@ -347,31 +394,31 @@ index b81f5be..c1611bb 100644
 -			} 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;
--		}
++		/* Advance the drawing cursor if we've moved to a new cluster */
++		if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluster) {
++			xp += runewidth;
++			cluster_xp = xp;
++			cluster_yp = yp;
+ 		}
  
 -		/* Lookup character index with default font. */
 -		glyphidx = XftCharIndex(xw.dpy, font->match, rune);
 -		if (glyphidx) {
--			specs[numspecs].font = font->match;
++		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;
--			numspecs++;
++			specs[numspecs].glyph = shaped.glyphs[code_idx].codepoint;
++			specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.);
++			specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.);
++			cluster_xp += shaped.positions[code_idx].x_advance / 64.;
++			cluster_yp += shaped.positions[code_idx].y_advance / 64.;
+ 			numspecs++;
 -			continue;
 -		}
 -
_AT_@ -385,105 +432,18 @@ index b81f5be..c1611bb 100644
 -			if (!glyphidx && frc[f].flags == frcflags
 -					&& frc[f].unicodep == rune) {
 -				break;
-+			/* Shape the segment. */
-+			hbtransform(&shaped, font->match, glyphs, start, length);
-+			runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE) ? 2.0f : 1.0f);
-+			cluster_xp = xp; cluster_yp = yp;
-+			for (int code_idx = 0; code_idx < shaped.count; code_idx++) {
-+				int idx = shaped.glyphs[code_idx].cluster;
-+
-+				if (glyphs[start + idx].mode & ATTR_WDUMMY)
-+					continue;
-+
-+				/* Advance the drawing cursor if we've moved to a new cluster */
-+				if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluster) {
-+					xp += runewidth;
-+					cluster_xp = xp;
-+					cluster_yp = yp;
-+					runewidth = win.cw * ((glyphs[start + idx].mode & ATTR_WIDE) ? 2.0f : 1.0f);
-+				}
-+
-+				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].codepoint;
-+					specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.);
-+					specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.);
-+					cluster_xp += shaped.positions[code_idx].x_advance / 64.;
-+					cluster_yp += shaped.positions[code_idx].y_advance / 64.;
-+					numspecs++;
-+				} else {
-+					/* If it's not found, try to fetch it through the font cache. */
-+					rune = glyphs[start + idx].u;
-+					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;
-+						}
-+					}
-+
-+					/* 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));
-+						}
-+
-+						frc[frclen].font = XftFontOpenPattern(xw.dpy,
-+								fontpattern);
-+						if (!frc[frclen].font)
-+							die("XftFontOpenPattern failed seeking fallback font: %s
",
-+								strerror(errno));
-+						frc[frclen].flags = frcflags;
-+						frc[frclen].unicodep = rune;
-+
-+						glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
-+
-+						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++;
++		} else {
++			/* If it's not found, try to fetch it through the font cache. */
++			rune = glyphs[idx].u;
++			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;
 +				}
  			}
 -		}
_AT_@ -494,10 +454,7 @@ index b81f5be..c1611bb 100644
 -				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
_AT_@ -524,13 +481,58 @@ index b81f5be..c1611bb 100644
 -			if (frclen >= frccap) {
 -				frccap += 16;
 -				frc = xrealloc(frc, frccap * sizeof(Fontcache));
-+			/* Determine font for glyph if different from previous glyph. */
-+			if (prevmode != mode) {
-+				prevmode = mode;
-+				xresetfontsettings(mode, &font, &frcflags);
-+				yp = winy + font->ascent;
++			/* 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));
++				}
++
++				frc[frclen].font = XftFontOpenPattern(xw.dpy,
++						fontpattern);
++				if (!frc[frclen].font)
++					die("XftFontOpenPattern failed seeking fallback font: %s
",
++						strerror(errno));
++				frc[frclen].flags = frcflags;
++				frc[frclen].unicodep = rune;
++
++				glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
++
++				f = frclen;
++				frclen++;
++
++				FcPatternDestroy(fcpattern);
++				FcCharSetDestroy(fccharset);
  			}
--
+ 
 -			frc[frclen].font = XftFontOpenPattern(xw.dpy,
 -					fontpattern);
 -			if (!frc[frclen].font)
_AT_@ -546,6 +548,11 @@ index b81f5be..c1611bb 100644
 -
 -			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;
_AT_@ -556,6 +563,8 @@ index b81f5be..c1611bb 100644
 -		numspecs++;
  	}
  
++	/* Cleanup and get ready for next segment. */
++	hbcleanup(&shaped);
  	return numspecs;
  }
  
_AT_@ -567,7 +576,9 @@ index b81f5be..c1611bb 100644
  	int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
  	    width = charlen * win.cw;
  	Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
-_AT_@ -1529,21 +1578,24 @@ void
+ 	XRenderColor colfg, colbg;
+_AT_@ -1528,23 +1544,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
+ void
  xdrawglyph(Glyph g, int x, int y)
  {
  	int numspecs;
_AT_@ -597,7 +608,9 @@ index b81f5be..c1611bb 100644
  
  	if (IS_SET(MODE_HIDE))
  		return;
-_AT_@ -1671,18 +1723,16 @@ xdrawline(Line line, int x1, int y1, int x2)
+ 
+_AT_@ -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;
  
_AT_@ -620,7 +633,8 @@ index b81f5be..c1611bb 100644
  			i = 0;
  		}
  		if (i == 0) {
-_AT_@ -1691,8 +1741,10 @@ xdrawline(Line line, int x1, int y1, int x2)
+ 			ox = x;
+ 			base = new;
  		}
  		i++;
  	}
_AT_@ -633,3 +647,4 @@ index b81f5be..c1611bb 100644
  }
  
  void
+ xfinishdraw(void)
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-boxdraw-20240427-0.9.2.diff b/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-boxdraw-20241226-0.9.2.diff
similarity index 70%
rename from st.suckless.org/patches/ligatures/0.9.2/st-ligatures-boxdraw-20240427-0.9.2.diff
rename to st.suckless.org/patches/ligatures/0.9.2/st-ligatures-boxdraw-20241226-0.9.2.diff
index 2a92a30b..1f8799c9 100644
--- a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-boxdraw-20240427-0.9.2.diff
+++ b/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-boxdraw-20241226-0.9.2.diff
_AT_@ -1,8 +1,9 @@
 diff --git a/Makefile b/Makefile
-index 6dfa212..adfa07a 100644
+index a64b4c2..05124bf 100644
 --- a/Makefile
 +++ b/Makefile
-_AT_@ -4,7 +4,7 @@
+_AT_@ -3,9 +3,9 @@
+ .POSIX:
  
  include config.mk
  
_AT_@ -11,7 +12,9 @@ index 6dfa212..adfa07a 100644
  OBJ = $(SRC:.c=.o)
  
  all: st
-_AT_@ -22,8 +22,9 @@ config.h:
+ 
+_AT_@ -15,10 +15,11 @@ config.h:
+ .c.o:
  	$(CC) $(STCFLAGS) -c $<
  
  st.o: config.h st.h win.h
_AT_@ -22,11 +25,13 @@ index 6dfa212..adfa07a 100644
  
  $(OBJ): config.h config.mk
  
+ st: $(OBJ)
 diff --git a/config.mk b/config.mk
-index 1e306f8..3e13e53 100644
+index fdc29a7..6833b3b 100644
 --- a/config.mk
 +++ b/config.mk
-_AT_@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config
+_AT_@ -14,12 +14,14 @@ PKG_CONFIG = pkg-config
+ 
  # includes and libs
  INCS = -I$(X11INC) \
         `$(PKG_CONFIG) --cflags fontconfig` \
_AT_@ -41,6 +46,19 @@ index 1e306f8..3e13e53 100644
  
  # flags
  STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
+ STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS)
+_AT_@ -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
_AT_@ -193,10 +211,11 @@ index 0000000..3b0ef44
 +void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
 +void hbcleanup(HbTransformData *);
 diff --git a/st.c b/st.c
-index 41d5ace..1c2edd6 100644
+index ec6fbf3..1385f77 100644
 --- a/st.c
 +++ b/st.c
-_AT_@ -2643,7 +2643,8 @@ draw(void)
+_AT_@ -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],
_AT_@ -206,11 +225,13 @@ index 41d5ace..1c2edd6 100644
  	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
-_AT_@ -11,7 +11,8 @@
+_AT_@ -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) : (x)
_AT_@ -220,11 +241,13 @@ index 808f5f7..ae41368 100644
  				(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
-_AT_@ -25,7 +25,7 @@ enum win_mode {
+_AT_@ -24,9 +24,9 @@ enum win_mode {
+ };
  
  void xbell(void);
  void xclipcopy(void);
_AT_@ -233,11 +256,13 @@ index 6de960d..94679e4 100644
  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 bf6bbf9..96b117f 100644
+index 978a8fc..c2d9993 100644
 --- a/x.c
 +++ b/x.c
-_AT_@ -19,6 +19,7 @@ char *argv0;
+_AT_@ -18,8 +18,9 @@
+ char *argv0;
  #include "arg.h"
  #include "st.h"
  #include "win.h"
_AT_@ -245,7 +270,9 @@ index bf6bbf9..96b117f 100644
  
  /* types used in config.h */
  typedef struct {
-_AT_@ -141,8 +142,9 @@ typedef struct {
+ 	uint mod;
+_AT_@ -140,10 +141,11 @@ typedef struct {
+ 	GC gc;
  } DC;
  
  static inline ushort sixd_to_16bit(int);
_AT_@ -256,7 +283,9 @@ index bf6bbf9..96b117f 100644
  static void xdrawglyph(Glyph, int, int);
  static void xclear(int, int, int, int);
  static int xgeommasktogravity(int);
-_AT_@ -757,7 +759,7 @@ xresize(int col, int row)
+ static int ximopen(Display *);
+_AT_@ -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 */
_AT_@ -265,7 +294,9 @@ index bf6bbf9..96b117f 100644
  }
  
  ushort
-_AT_@ -1062,6 +1064,9 @@ xunloadfont(Font *f)
+ sixd_to_16bit(int x)
+_AT_@ -1061,8 +1063,11 @@ xunloadfont(Font *f)
+ 
  void
  xunloadfonts(void)
  {
_AT_@ -275,7 +306,9 @@ index bf6bbf9..96b117f 100644
  	/* Free the loaded fonts in the font cache.  */
  	while (frclen > 0)
  		XftFontClose(xw.dpy, frc[--frclen].font);
-_AT_@ -1185,7 +1190,7 @@ xinit(int cols, int rows)
+ 
+_AT_@ -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 */
_AT_@ -284,7 +317,9 @@ index bf6bbf9..96b117f 100644
  
  	/* Xft rendering context */
  	xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
-_AT_@ -1241,6 +1246,22 @@ xinit(int cols, int rows)
+ 
+_AT_@ -1240,149 +1245,162 @@ xinit(int cols, int rows)
+ 
  	boxdraw_xinit(xw.dpy, xw.cmap, xw.draw, xw.vis);
  }
  
_AT_@ -307,28 +342,42 @@ index bf6bbf9..96b117f 100644
  int
  xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
  {
-_AT_@ -1255,133 +1276,164 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
+ 	float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp;
+-	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.0f);
+ 	Rune rune;
+ 	FT_UInt glyphidx;
+ 	FcResult fcres;
  	FcPattern *fcpattern, *fontpattern;
  	FcFontSet *fcsets[] = { NULL };
  	FcCharSet *fccharset;
 -	int i, f, numspecs = 0;
-+	int i, f, length = 0, start = 0, numspecs = 0;
++	int f, code_idx, 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) {
+-	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;
++	/* Initial values. */
++	xresetfontsettings(mode, &font, &frcflags);
  
- 		/* Skip dummy wide-character spacing. */
+-		/* Skip dummy wide-character spacing. */
 -		if (mode == ATTR_WDUMMY)
-+		if (mode & ATTR_WDUMMY && i < (len - 1))
++	/* 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. */
_AT_@ -346,36 +395,33 @@ index bf6bbf9..96b117f 100644
 -			} 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;
--		}
++		/* Advance the drawing cursor if we've moved to a new cluster */
++		if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluster) {
++			xp += runewidth;
++			cluster_xp = xp;
++			cluster_yp = yp;
+ 		}
  
 -		if (mode & ATTR_BOXDRAW) {
--			/* minor shoehorning: boxdraw uses only this ushort */
++		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].font = font->match;
 -			specs[numspecs].glyph = glyphidx;
 -			specs[numspecs].x = (short)xp;
 -			specs[numspecs].y = (short)yp;
 -			xp += runewidth;
--			numspecs++;
++			specs[numspecs].glyph = boxdrawindex(&glyphs[idx]);
++			specs[numspecs].x = xp;
++			specs[numspecs].y = yp;
+ 			numspecs++;
 -			continue;
 -		}
 -
_AT_@ -389,126 +435,38 @@ index bf6bbf9..96b117f 100644
 -			if (!glyphidx && frc[f].flags == frcflags
 -					&& frc[f].unicodep == rune) {
 -				break;
-+			/* Shape the segment. */
-+			hbtransform(&shaped, font->match, glyphs, start, length);
-+			runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE) ? 2.0f : 1.0f);
-+			cluster_xp = xp; cluster_yp = yp;
-+			for (int code_idx = 0; code_idx < shaped.count; code_idx++) {
-+				int idx = shaped.glyphs[code_idx].cluster;
-+
-+				if (glyphs[start + idx].mode & ATTR_WDUMMY)
-+					continue;
-+
-+				/* Advance the drawing cursor if we've moved to a new cluster */
-+				if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluster) {
-+					xp += runewidth;
-+					cluster_xp = xp;
-+					cluster_yp = yp;
-+					runewidth = win.cw * ((glyphs[start + idx].mode & ATTR_WIDE) ? 2.0f : 1.0f);
-+				}
-+
-+				if (glyphs[start + idx].mode & ATTR_BOXDRAW) {
-+					/* minor shoehorning: boxdraw uses only this ushort */
-+					specs[numspecs].font = font->match;
-+					specs[numspecs].glyph = boxdrawindex(&glyphs[start + idx]);
-+					specs[numspecs].x = xp;
-+					specs[numspecs].y = yp;
-+					numspecs++;
-+				} 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].codepoint;
-+					specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.);
-+					specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.);
-+					cluster_xp += shaped.positions[code_idx].x_advance / 64.;
-+					cluster_yp += shaped.positions[code_idx].y_advance / 64.;
-+					numspecs++;
-+				} else {
-+					/* If it's not found, try to fetch it through the font cache. */
-+					rune = glyphs[start + idx].u;
-+					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;
-+						}
-+					}
-+
-+					/* 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));
-+						}
-+
-+						frc[frclen].font = XftFontOpenPattern(xw.dpy,
-+								fontpattern);
-+						if (!frc[frclen].font)
-+							die("XftFontOpenPattern failed seeking fallback font: %s
",
-+								strerror(errno));
-+						frc[frclen].flags = frcflags;
-+						frc[frclen].unicodep = rune;
-+
-+						glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
-+
-+						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++;
++		} 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].codepoint;
++			specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.);
++			specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.);
++			cluster_xp += shaped.positions[code_idx].x_advance / 64.;
++			cluster_yp += shaped.positions[code_idx].y_advance / 64.;
++			numspecs++;
++		} else {
++			/* If it's not found, try to fetch it through the font cache. */
++			rune = glyphs[idx].u;
++			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;
 +				}
  			}
 -		}
- 
+-
 -		/* 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
_AT_@ -527,7 +485,7 @@ index bf6bbf9..96b117f 100644
 -			FcConfigSubstitute(0, fcpattern,
 -					FcMatchPattern);
 -			FcDefaultSubstitute(fcpattern);
--
+ 
 -			fontpattern = FcFontSetMatch(0, fcsets, 1,
 -					fcpattern, &fcres);
 -
_AT_@ -535,13 +493,58 @@ index bf6bbf9..96b117f 100644
 -			if (frclen >= frccap) {
 -				frccap += 16;
 -				frc = xrealloc(frc, frccap * sizeof(Fontcache));
-+			/* Determine font for glyph if different from previous glyph. */
-+			if (prevmode != mode) {
-+				prevmode = mode;
-+				xresetfontsettings(mode, &font, &frcflags);
-+				yp = winy + font->ascent;
++			/* 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));
++				}
++
++				frc[frclen].font = XftFontOpenPattern(xw.dpy,
++						fontpattern);
++				if (!frc[frclen].font)
++					die("XftFontOpenPattern failed seeking fallback font: %s
",
++						strerror(errno));
++				frc[frclen].flags = frcflags;
++				frc[frclen].unicodep = rune;
++
++				glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
++
++				f = frclen;
++				frclen++;
++
++				FcPatternDestroy(fcpattern);
++				FcCharSetDestroy(fccharset);
  			}
--
+ 
 -			frc[frclen].font = XftFontOpenPattern(xw.dpy,
 -					fontpattern);
 -			if (!frc[frclen].font)
_AT_@ -557,6 +560,11 @@ index bf6bbf9..96b117f 100644
 -
 -			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;
_AT_@ -567,6 +575,7 @@ index bf6bbf9..96b117f 100644
 -		numspecs++;
  	}
  
++	/* Cleanup and get ready for next segment. */
 +	hbcleanup(&shaped);
  	return numspecs;
  }
_AT_@ -579,7 +588,9 @@ index bf6bbf9..96b117f 100644
  	int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
  	    width = charlen * win.cw;
  	Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
-_AT_@ -1521,21 +1573,24 @@ void
+ 	XRenderColor colfg, colbg;
+_AT_@ -1520,23 +1538,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
+ void
  xdrawglyph(Glyph g, int x, int y)
  {
  	int numspecs;
_AT_@ -609,7 +620,9 @@ index bf6bbf9..96b117f 100644
  
  	if (IS_SET(MODE_HIDE))
  		return;
-_AT_@ -1663,18 +1718,16 @@ xdrawline(Line line, int x1, int y1, int x2)
+ 
+_AT_@ -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;
  
_AT_@ -632,7 +645,8 @@ index bf6bbf9..96b117f 100644
  			i = 0;
  		}
  		if (i == 0) {
-_AT_@ -1683,8 +1736,10 @@ xdrawline(Line line, int x1, int y1, int x2)
+ 			ox = x;
+ 			base = new;
  		}
  		i++;
  	}
_AT_@ -645,3 +659,4 @@ index bf6bbf9..96b117f 100644
  }
  
  void
+ xfinishdraw(void)
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-scrollback-20240427-0.9.2.diff b/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-scrollback-20241226-0.9.2.diff
similarity index 71%
rename from st.suckless.org/patches/ligatures/0.9.2/st-ligatures-scrollback-20240427-0.9.2.diff
rename to st.suckless.org/patches/ligatures/0.9.2/st-ligatures-scrollback-20241226-0.9.2.diff
index 92edf9a1..86d3939f 100644
--- a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-scrollback-20240427-0.9.2.diff
+++ b/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-scrollback-20241226-0.9.2.diff
_AT_@ -1,8 +1,9 @@
 diff --git a/Makefile b/Makefile
-index 470ac86..38240da 100644
+index 15db421..dfcea0f 100644
 --- a/Makefile
 +++ b/Makefile
-_AT_@ -4,7 +4,7 @@
+_AT_@ -3,9 +3,9 @@
+ .POSIX:
  
  include config.mk
  
_AT_@ -11,7 +12,9 @@ index 470ac86..38240da 100644
  OBJ = $(SRC:.c=.o)
  
  all: st
-_AT_@ -22,7 +22,8 @@ config.h:
+ 
+_AT_@ -15,9 +15,10 @@ config.h:
+ .c.o:
  	$(CC) $(STCFLAGS) -c $<
  
  st.o: config.h st.h win.h
_AT_@ -21,11 +24,13 @@ index 470ac86..38240da 100644
  
  $(OBJ): config.h config.mk
  
+ st: $(OBJ)
 diff --git a/config.mk b/config.mk
-index 1e306f8..3e13e53 100644
+index fdc29a7..6833b3b 100644
 --- a/config.mk
 +++ b/config.mk
-_AT_@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config
+_AT_@ -14,12 +14,14 @@ PKG_CONFIG = pkg-config
+ 
  # includes and libs
  INCS = -I$(X11INC) \
         `$(PKG_CONFIG) --cflags fontconfig` \
_AT_@ -40,6 +45,19 @@ index 1e306f8..3e13e53 100644
  
  # flags
  STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
+ STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS)
+_AT_@ -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
_AT_@ -192,10 +210,11 @@ index 0000000..3b0ef44
 +void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
 +void hbcleanup(HbTransformData *);
 diff --git a/st.c b/st.c
-index 79ee9ba..7675db6 100644
+index 2478942..bba90d3 100644
 --- a/st.c
 +++ b/st.c
-_AT_@ -2711,7 +2711,8 @@ draw(void)
+_AT_@ -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],
_AT_@ -205,11 +224,13 @@ index 79ee9ba..7675db6 100644
  	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
-_AT_@ -11,7 +11,8 @@
+_AT_@ -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) : (x)
_AT_@ -219,11 +240,13 @@ index 818a6f8..4e584b6 100644
  				(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
-_AT_@ -25,7 +25,7 @@ enum win_mode {
+_AT_@ -24,9 +24,9 @@ enum win_mode {
+ };
  
  void xbell(void);
  void xclipcopy(void);
_AT_@ -232,11 +255,13 @@ index 6de960d..94679e4 100644
  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 2a3bd38..0bb51ff 100644
+index bd23686..2bf3b72 100644
 --- a/x.c
 +++ b/x.c
-_AT_@ -19,6 +19,7 @@ char *argv0;
+_AT_@ -18,8 +18,9 @@
+ char *argv0;
  #include "arg.h"
  #include "st.h"
  #include "win.h"
_AT_@ -244,7 +269,9 @@ index 2a3bd38..0bb51ff 100644
  
  /* types used in config.h */
  typedef struct {
-_AT_@ -141,8 +142,9 @@ typedef struct {
+ 	uint mod;
+_AT_@ -140,10 +141,11 @@ typedef struct {
+ 	GC gc;
  } DC;
  
  static inline ushort sixd_to_16bit(int);
_AT_@ -255,7 +282,9 @@ index 2a3bd38..0bb51ff 100644
  static void xdrawglyph(Glyph, int, int);
  static void xclear(int, int, int, int);
  static int xgeommasktogravity(int);
-_AT_@ -757,7 +759,7 @@ xresize(int col, int row)
+ static int ximopen(Display *);
+_AT_@ -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 */
_AT_@ -264,7 +293,9 @@ index 2a3bd38..0bb51ff 100644
  }
  
  ushort
-_AT_@ -1062,6 +1064,9 @@ xunloadfont(Font *f)
+ sixd_to_16bit(int x)
+_AT_@ -1061,8 +1063,11 @@ xunloadfont(Font *f)
+ 
  void
  xunloadfonts(void)
  {
_AT_@ -274,7 +305,9 @@ index 2a3bd38..0bb51ff 100644
  	/* Free the loaded fonts in the font cache.  */
  	while (frclen > 0)
  		XftFontClose(xw.dpy, frc[--frclen].font);
-_AT_@ -1185,7 +1190,7 @@ xinit(int cols, int rows)
+ 
+_AT_@ -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 */
_AT_@ -283,7 +316,9 @@ index 2a3bd38..0bb51ff 100644
  
  	/* Xft rendering context */
  	xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
-_AT_@ -1239,6 +1244,22 @@ xinit(int cols, int rows)
+ 
+_AT_@ -1238,144 +1243,155 @@ xinit(int cols, int rows)
+ 	if (xsel.xtarget == None)
  		xsel.xtarget = XA_STRING;
  }
  
_AT_@ -306,28 +341,42 @@ index 2a3bd38..0bb51ff 100644
  int
  xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
  {
-_AT_@ -1253,128 +1274,157 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
+ 	float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp;
+-	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.0f);
+ 	Rune rune;
+ 	FT_UInt glyphidx;
+ 	FcResult fcres;
  	FcPattern *fcpattern, *fontpattern;
  	FcFontSet *fcsets[] = { NULL };
  	FcCharSet *fccharset;
 -	int i, f, numspecs = 0;
-+	int i, f, length = 0, start = 0, numspecs = 0;
++	int f, code_idx, 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) {
+-	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;
++	/* Initial values. */
++	xresetfontsettings(mode, &font, &frcflags);
  
- 		/* Skip dummy wide-character spacing. */
+-		/* Skip dummy wide-character spacing. */
 -		if (mode == ATTR_WDUMMY)
-+		if (mode & ATTR_WDUMMY && i < (len - 1))
++	/* 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. */
_AT_@ -345,31 +394,31 @@ index 2a3bd38..0bb51ff 100644
 -			} 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;
--		}
++		/* Advance the drawing cursor if we've moved to a new cluster */
++		if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluster) {
++			xp += runewidth;
++			cluster_xp = xp;
++			cluster_yp = yp;
+ 		}
  
 -		/* Lookup character index with default font. */
 -		glyphidx = XftCharIndex(xw.dpy, font->match, rune);
 -		if (glyphidx) {
--			specs[numspecs].font = font->match;
++		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;
--			numspecs++;
++			specs[numspecs].glyph = shaped.glyphs[code_idx].codepoint;
++			specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.);
++			specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.);
++			cluster_xp += shaped.positions[code_idx].x_advance / 64.;
++			cluster_yp += shaped.positions[code_idx].y_advance / 64.;
+ 			numspecs++;
 -			continue;
 -		}
 -
_AT_@ -383,105 +432,18 @@ index 2a3bd38..0bb51ff 100644
 -			if (!glyphidx && frc[f].flags == frcflags
 -					&& frc[f].unicodep == rune) {
 -				break;
-+			/* Shape the segment. */
-+			hbtransform(&shaped, font->match, glyphs, start, length);
-+			runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE) ? 2.0f : 1.0f);
-+			cluster_xp = xp; cluster_yp = yp;
-+			for (int code_idx = 0; code_idx < shaped.count; code_idx++) {
-+				int idx = shaped.glyphs[code_idx].cluster;
-+
-+				if (glyphs[start + idx].mode & ATTR_WDUMMY)
-+					continue;
-+
-+				/* Advance the drawing cursor if we've moved to a new cluster */
-+				if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluster) {
-+					xp += runewidth;
-+					cluster_xp = xp;
-+					cluster_yp = yp;
-+					runewidth = win.cw * ((glyphs[start + idx].mode & ATTR_WIDE) ? 2.0f : 1.0f);
-+				}
-+
-+				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].codepoint;
-+					specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.);
-+					specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.);
-+					cluster_xp += shaped.positions[code_idx].x_advance / 64.;
-+					cluster_yp += shaped.positions[code_idx].y_advance / 64.;
-+					numspecs++;
-+				} else {
-+					/* If it's not found, try to fetch it through the font cache. */
-+					rune = glyphs[start + idx].u;
-+					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;
-+						}
-+					}
-+
-+					/* 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));
-+						}
-+
-+						frc[frclen].font = XftFontOpenPattern(xw.dpy,
-+								fontpattern);
-+						if (!frc[frclen].font)
-+							die("XftFontOpenPattern failed seeking fallback font: %s
",
-+								strerror(errno));
-+						frc[frclen].flags = frcflags;
-+						frc[frclen].unicodep = rune;
-+
-+						glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
-+
-+						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++;
++		} else {
++			/* If it's not found, try to fetch it through the font cache. */
++			rune = glyphs[idx].u;
++			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;
 +				}
  			}
 -		}
_AT_@ -492,10 +454,7 @@ index 2a3bd38..0bb51ff 100644
 -				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
_AT_@ -522,13 +481,58 @@ index 2a3bd38..0bb51ff 100644
 -			if (frclen >= frccap) {
 -				frccap += 16;
 -				frc = xrealloc(frc, frccap * sizeof(Fontcache));
-+			/* Determine font for glyph if different from previous glyph. */
-+			if (prevmode != mode) {
-+				prevmode = mode;
-+				xresetfontsettings(mode, &font, &frcflags);
-+				yp = winy + font->ascent;
++			/* 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));
++				}
++
++				frc[frclen].font = XftFontOpenPattern(xw.dpy,
++						fontpattern);
++				if (!frc[frclen].font)
++					die("XftFontOpenPattern failed seeking fallback font: %s
",
++						strerror(errno));
++				frc[frclen].flags = frcflags;
++				frc[frclen].unicodep = rune;
++
++				glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
++
++				f = frclen;
++				frclen++;
++
++				FcPatternDestroy(fcpattern);
++				FcCharSetDestroy(fccharset);
  			}
--
+ 
 -			frc[frclen].font = XftFontOpenPattern(xw.dpy,
 -					fontpattern);
 -			if (!frc[frclen].font)
_AT_@ -544,6 +548,11 @@ index 2a3bd38..0bb51ff 100644
 -
 -			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;
_AT_@ -554,6 +563,7 @@ index 2a3bd38..0bb51ff 100644
 -		numspecs++;
  	}
  
++	/* Cleanup and get ready for next segment. */
 +	hbcleanup(&shaped);
  	return numspecs;
  }
_AT_@ -566,7 +576,9 @@ index 2a3bd38..0bb51ff 100644
  	int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
  	    width = charlen * win.cw;
  	Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
-_AT_@ -1510,21 +1560,24 @@ void
+ 	XRenderColor colfg, colbg;
+_AT_@ -1509,23 +1525,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
+ void
  xdrawglyph(Glyph g, int x, int y)
  {
  	int numspecs;
_AT_@ -596,7 +608,9 @@ index 2a3bd38..0bb51ff 100644
  
  	if (IS_SET(MODE_HIDE))
  		return;
-_AT_@ -1652,18 +1705,16 @@ xdrawline(Line line, int x1, int y1, int x2)
+ 
+_AT_@ -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;
  
_AT_@ -619,7 +633,8 @@ index 2a3bd38..0bb51ff 100644
  			i = 0;
  		}
  		if (i == 0) {
-_AT_@ -1672,8 +1723,10 @@ xdrawline(Line line, int x1, int y1, int x2)
+ 			ox = x;
+ 			base = new;
  		}
  		i++;
  	}
_AT_@ -632,3 +647,4 @@ index 2a3bd38..0bb51ff 100644
  }
  
  void
+ xfinishdraw(void)
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-scrollback-ringbuffer-20240427-0.9.2.diff b/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-scrollback-ringbuffer-20241226-0.9.2.diff
similarity index 71%
rename from st.suckless.org/patches/ligatures/0.9.2/st-ligatures-scrollback-ringbuffer-20240427-0.9.2.diff
rename to st.suckless.org/patches/ligatures/0.9.2/st-ligatures-scrollback-ringbuffer-20241226-0.9.2.diff
index 26105db4..6f3fd265 100644
--- a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-scrollback-ringbuffer-20240427-0.9.2.diff
+++ b/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-scrollback-ringbuffer-20241226-0.9.2.diff
_AT_@ -1,8 +1,9 @@
 diff --git a/Makefile b/Makefile
-index 470ac86..38240da 100644
+index 15db421..dfcea0f 100644
 --- a/Makefile
 +++ b/Makefile
-_AT_@ -4,7 +4,7 @@
+_AT_@ -3,9 +3,9 @@
+ .POSIX:
  
  include config.mk
  
_AT_@ -11,7 +12,9 @@ index 470ac86..38240da 100644
  OBJ = $(SRC:.c=.o)
  
  all: st
-_AT_@ -22,7 +22,8 @@ config.h:
+ 
+_AT_@ -15,9 +15,10 @@ config.h:
+ .c.o:
  	$(CC) $(STCFLAGS) -c $<
  
  st.o: config.h st.h win.h
_AT_@ -21,11 +24,13 @@ index 470ac86..38240da 100644
  
  $(OBJ): config.h config.mk
  
+ st: $(OBJ)
 diff --git a/config.mk b/config.mk
-index 1e306f8..3e13e53 100644
+index fdc29a7..6833b3b 100644
 --- a/config.mk
 +++ b/config.mk
-_AT_@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config
+_AT_@ -14,12 +14,14 @@ PKG_CONFIG = pkg-config
+ 
  # includes and libs
  INCS = -I$(X11INC) \
         `$(PKG_CONFIG) --cflags fontconfig` \
_AT_@ -40,6 +45,19 @@ index 1e306f8..3e13e53 100644
  
  # flags
  STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
+ STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS)
+_AT_@ -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
_AT_@ -192,10 +210,11 @@ index 0000000..3b0ef44
 +void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
 +void hbcleanup(HbTransformData *);
 diff --git a/st.c b/st.c
-index c44797b..91f54dc 100644
+index d9b163e..fbca4ba 100644
 --- a/st.c
 +++ b/st.c
-_AT_@ -2759,7 +2759,8 @@ draw(void)
+_AT_@ -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],
_AT_@ -205,11 +224,13 @@ index c44797b..91f54dc 100644
  	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
-_AT_@ -11,7 +11,8 @@
+_AT_@ -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) : (x)
_AT_@ -219,11 +240,13 @@ index 3cea73b..709a369 100644
  				(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
-_AT_@ -25,7 +25,7 @@ enum win_mode {
+_AT_@ -24,9 +24,9 @@ enum win_mode {
+ };
  
  void xbell(void);
  void xclipcopy(void);
_AT_@ -232,11 +255,13 @@ index 6de960d..94679e4 100644
  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 9891e91..7d42790 100644
+index 25785a6..16d7a3a 100644
 --- a/x.c
 +++ b/x.c
-_AT_@ -19,6 +19,7 @@ char *argv0;
+_AT_@ -18,8 +18,9 @@
+ char *argv0;
  #include "arg.h"
  #include "st.h"
  #include "win.h"
_AT_@ -244,7 +269,9 @@ index 9891e91..7d42790 100644
  
  /* types used in config.h */
  typedef struct {
-_AT_@ -143,8 +144,9 @@ typedef struct {
+ 	uint mod;
+_AT_@ -142,10 +143,11 @@ typedef struct {
+ 	GC gc;
  } DC;
  
  static inline ushort sixd_to_16bit(int);
_AT_@ -255,7 +282,9 @@ index 9891e91..7d42790 100644
  static void xdrawglyph(Glyph, int, int);
  static void xclear(int, int, int, int);
  static int xgeommasktogravity(int);
-_AT_@ -759,7 +761,7 @@ xresize(int col, int row)
+ static int ximopen(Display *);
+_AT_@ -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 */
_AT_@ -264,7 +293,9 @@ index 9891e91..7d42790 100644
  }
  
  ushort
-_AT_@ -1064,6 +1066,9 @@ xunloadfont(Font *f)
+ sixd_to_16bit(int x)
+_AT_@ -1063,8 +1065,11 @@ xunloadfont(Font *f)
+ 
  void
  xunloadfonts(void)
  {
_AT_@ -274,7 +305,9 @@ index 9891e91..7d42790 100644
  	/* Free the loaded fonts in the font cache.  */
  	while (frclen > 0)
  		XftFontClose(xw.dpy, frc[--frclen].font);
-_AT_@ -1187,7 +1192,7 @@ xinit(int cols, int rows)
+ 
+_AT_@ -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 */
_AT_@ -283,7 +316,9 @@ index 9891e91..7d42790 100644
  
  	/* Xft rendering context */
  	xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
-_AT_@ -1241,6 +1246,22 @@ xinit(int cols, int rows)
+ 
+_AT_@ -1240,144 +1245,155 @@ xinit(int cols, int rows)
+ 	if (xsel.xtarget == None)
  		xsel.xtarget = XA_STRING;
  }
  
_AT_@ -306,28 +341,42 @@ index 9891e91..7d42790 100644
  int
  xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
  {
-_AT_@ -1255,128 +1276,156 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
+ 	float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp;
+-	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.0f);
+ 	Rune rune;
+ 	FT_UInt glyphidx;
+ 	FcResult fcres;
  	FcPattern *fcpattern, *fontpattern;
  	FcFontSet *fcsets[] = { NULL };
  	FcCharSet *fccharset;
 -	int i, f, numspecs = 0;
-+	int i, f, length = 0, start = 0, numspecs = 0;
++	int f, code_idx, 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) {
+-	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;
++	/* Initial values. */
++	xresetfontsettings(mode, &font, &frcflags);
  
- 		/* Skip dummy wide-character spacing. */
+-		/* Skip dummy wide-character spacing. */
 -		if (mode == ATTR_WDUMMY)
-+		if (mode & ATTR_WDUMMY && i < (len - 1))
++	/* 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. */
_AT_@ -345,31 +394,31 @@ index 9891e91..7d42790 100644
 -			} 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;
--		}
++		/* Advance the drawing cursor if we've moved to a new cluster */
++		if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluster) {
++			xp += runewidth;
++			cluster_xp = xp;
++			cluster_yp = yp;
+ 		}
  
 -		/* Lookup character index with default font. */
 -		glyphidx = XftCharIndex(xw.dpy, font->match, rune);
 -		if (glyphidx) {
--			specs[numspecs].font = font->match;
++		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;
--			numspecs++;
++			specs[numspecs].glyph = shaped.glyphs[code_idx].codepoint;
++			specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.);
++			specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.);
++			cluster_xp += shaped.positions[code_idx].x_advance / 64.;
++			cluster_yp += shaped.positions[code_idx].y_advance / 64.;
+ 			numspecs++;
 -			continue;
 -		}
 -
_AT_@ -383,105 +432,18 @@ index 9891e91..7d42790 100644
 -			if (!glyphidx && frc[f].flags == frcflags
 -					&& frc[f].unicodep == rune) {
 -				break;
-+			/* Shape the segment. */
-+			hbtransform(&shaped, font->match, glyphs, start, length);
-+			runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE) ? 2.0f : 1.0f);
-+			cluster_xp = xp; cluster_yp = yp;
-+			for (int code_idx = 0; code_idx < shaped.count; code_idx++) {
-+				int idx = shaped.glyphs[code_idx].cluster;
-+
-+				if (glyphs[start + idx].mode & ATTR_WDUMMY)
-+					continue;
-+
-+				/* Advance the drawing cursor if we've moved to a new cluster */
-+				if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluster) {
-+					xp += runewidth;
-+					cluster_xp = xp;
-+					cluster_yp = yp;
-+					runewidth = win.cw * ((glyphs[start + idx].mode & ATTR_WIDE) ? 2.0f : 1.0f);
-+				}
-+
-+				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].codepoint;
-+					specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.);
-+					specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.);
-+					cluster_xp += shaped.positions[code_idx].x_advance / 64.;
-+					cluster_yp += shaped.positions[code_idx].y_advance / 64.;
-+					numspecs++;
-+				} else {
-+					/* If it's not found, try to fetch it through the font cache. */
-+					rune = glyphs[start + idx].u;
-+					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;
-+						}
-+					}
-+
-+					/* 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));
-+						}
-+
-+						frc[frclen].font = XftFontOpenPattern(xw.dpy,
-+								fontpattern);
-+						if (!frc[frclen].font)
-+							die("XftFontOpenPattern failed seeking fallback font: %s
",
-+								strerror(errno));
-+						frc[frclen].flags = frcflags;
-+						frc[frclen].unicodep = rune;
-+
-+						glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
-+
-+						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++;
++		} else {
++			/* If it's not found, try to fetch it through the font cache. */
++			rune = glyphs[idx].u;
++			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;
 +				}
  			}
 -		}
_AT_@ -492,10 +454,7 @@ index 9891e91..7d42790 100644
 -				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
_AT_@ -522,13 +481,58 @@ index 9891e91..7d42790 100644
 -			if (frclen >= frccap) {
 -				frccap += 16;
 -				frc = xrealloc(frc, frccap * sizeof(Fontcache));
-+			/* Determine font for glyph if different from previous glyph. */
-+			if (prevmode != mode) {
-+				prevmode = mode;
-+				xresetfontsettings(mode, &font, &frcflags);
-+				yp = winy + font->ascent;
++			/* 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));
++				}
++
++				frc[frclen].font = XftFontOpenPattern(xw.dpy,
++						fontpattern);
++				if (!frc[frclen].font)
++					die("XftFontOpenPattern failed seeking fallback font: %s
",
++						strerror(errno));
++				frc[frclen].flags = frcflags;
++				frc[frclen].unicodep = rune;
++
++				glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
++
++				f = frclen;
++				frclen++;
++
++				FcPatternDestroy(fcpattern);
++				FcCharSetDestroy(fccharset);
  			}
--
+ 
 -			frc[frclen].font = XftFontOpenPattern(xw.dpy,
 -					fontpattern);
 -			if (!frc[frclen].font)
_AT_@ -544,6 +548,11 @@ index 9891e91..7d42790 100644
 -
 -			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;
_AT_@ -554,6 +563,8 @@ index 9891e91..7d42790 100644
 -		numspecs++;
  	}
  
++	/* Cleanup and get ready for next segment. */
++	hbcleanup(&shaped);
  	return numspecs;
  }
  
_AT_@ -565,7 +576,9 @@ index 9891e91..7d42790 100644
  	int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
  	    width = charlen * win.cw;
  	Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
-_AT_@ -1512,21 +1561,24 @@ void
+ 	XRenderColor colfg, colbg;
+_AT_@ -1511,23 +1527,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
+ void
  xdrawglyph(Glyph g, int x, int y)
  {
  	int numspecs;
_AT_@ -595,7 +608,9 @@ index 9891e91..7d42790 100644
  
  	if (IS_SET(MODE_HIDE))
  		return;
-_AT_@ -1654,18 +1706,16 @@ xdrawline(Line line, int x1, int y1, int x2)
+ 
+_AT_@ -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;
  
_AT_@ -618,7 +633,8 @@ index 9891e91..7d42790 100644
  			i = 0;
  		}
  		if (i == 0) {
-_AT_@ -1674,8 +1724,10 @@ xdrawline(Line line, int x1, int y1, int x2)
+ 			ox = x;
+ 			base = new;
  		}
  		i++;
  	}
_AT_@ -631,3 +647,4 @@ index 9891e91..7d42790 100644
  }
  
  void
+ xfinishdraw(void)
diff --git a/st.suckless.org/patches/ligatures/index.md b/st.suckless.org/patches/ligatures/index.md
index 3734a433..14f506cb 100644
--- a/st.suckless.org/patches/ligatures/index.md
+++ b/st.suckless.org/patches/ligatures/index.md
_AT_@ -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.diff)
-* [st-ligatures-scrollback-ringbuffer-0.9.2](0.9.2/st-ligatures-scrollback-ringbuffer-20240427-0.9.2.diff)
-* [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-20240427-0.9.2.diff)
-* [st-ligatures-alpha-scrollback-ringbuffer-0.9.2](0.9.2/st-ligatures-alpha-scrollback-ringbuffer-20240427-0.9.2.diff)
-* [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.diff)
+* [st-ligatures-scrollback-ringbuffer-0.9.2](0.9.2/st-ligatures-scrollback-ringbuffer-20241226-0.9.2.diff)
+* [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-20241226-0.9.2.diff)
+* [st-ligatures-alpha-scrollback-ringbuffer-0.9.2](0.9.2/st-ligatures-alpha-scrollback-ringbuffer-20241226-0.9.2.diff)
+* [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)
Received on Thu Jan 09 2025 - 22:55:03 CET

This archive was generated by hypermail 2.3.0 : Thu Jan 09 2025 - 23:00:56 CET