commit ccef7d379d9245ba5a19d70bad7db288f9814d35
Author: Alexander Rogachev <[email protected]>
Date:   Sun Mar 5 00:51:21 2023 +0400

    [st][patch][ligatures] Slight refactoring plus fix for a rare memory 
corruption bug.

diff --git 
a/st.suckless.org/patches/ligatures/0.9/st-ligatures-20221120-0.9.diff 
b/st.suckless.org/patches/ligatures/0.9/st-ligatures-20230105-0.9.diff
similarity index 75%
rename from st.suckless.org/patches/ligatures/0.9/st-ligatures-20221120-0.9.diff
rename to st.suckless.org/patches/ligatures/0.9/st-ligatures-20230105-0.9.diff
index ab1e0fa3..4b6c8cb4 100644
--- a/st.suckless.org/patches/ligatures/0.9/st-ligatures-20221120-0.9.diff
+++ b/st.suckless.org/patches/ligatures/0.9/st-ligatures-20230105-0.9.diff
@@ -42,10 +42,10 @@ index 1e306f8..3e13e53 100644
  STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
 diff --git a/hb.c b/hb.c
 new file mode 100644
-index 0000000..bacec05
+index 0000000..528c040
 --- /dev/null
 +++ b/hb.c
-@@ -0,0 +1,107 @@
+@@ -0,0 +1,124 @@
 +#include <stdlib.h>
 +#include <stdio.h>
 +#include <math.h>
@@ -58,6 +58,7 @@ index 0000000..bacec05
 +#include "hb.h"
 +
 +#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start 
= HB_FEATURE_GLOBAL_START, .end = HB_FEATURE_GLOBAL_END }
++#define BUFFER_STEP 256
 +
 +hb_font_t *hbfindfont(XftFont *match);
 +
@@ -66,8 +67,19 @@ index 0000000..bacec05
 +      hb_font_t *font;
 +} HbFontMatch;
 +
-+static int hbfontslen = 0;
-+static HbFontMatch *hbfontcache = NULL;
++typedef struct {
++      size_t capacity;
++      HbFontMatch *fonts;
++} HbFontCache;
++
++static HbFontCache hbfontcache = { 0, NULL };
++
++typedef struct {
++      size_t capacity;
++      Rune *runes;
++} RuneBuffer;
++
++static RuneBuffer hbrunebuffer = { 0, NULL };
 +
 +/*
 + * Poplulate the array with a list of font features, wrapped in FEATURE macro,
@@ -79,45 +91,44 @@ index 0000000..bacec05
 +void
 +hbunloadfonts()
 +{
-+      for (int i = 0; i < hbfontslen; i++) {
-+              hb_font_destroy(hbfontcache[i].font);
-+              XftUnlockFace(hbfontcache[i].match);
++      for (int i = 0; i < hbfontcache.capacity; i++) {
++              hb_font_destroy(hbfontcache.fonts[i].font);
++              XftUnlockFace(hbfontcache.fonts[i].match);
 +      }
 +
-+      if (hbfontcache != NULL) {
-+              free(hbfontcache);
-+              hbfontcache = NULL;
++      if (hbfontcache.fonts != NULL) {
++              free(hbfontcache.fonts);
++              hbfontcache.fonts = NULL;
 +      }
-+      hbfontslen = 0;
++      hbfontcache.capacity = 0;
 +}
 +
 +hb_font_t *
 +hbfindfont(XftFont *match)
 +{
-+      for (int i = 0; i < hbfontslen; i++) {
-+              if (hbfontcache[i].match == match)
-+                      return hbfontcache[i].font;
++      for (int i = 0; i < hbfontcache.capacity; i++) {
++              if (hbfontcache.fonts[i].match == match)
++                      return hbfontcache.fonts[i].font;
 +      }
 +
 +      /* Font not found in cache, caching it now. */
-+      hbfontcache = realloc(hbfontcache, sizeof(HbFontMatch) * (hbfontslen + 
1));
++      hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * 
(hbfontcache.capacity + 1));
 +      FT_Face face = XftLockFace(match);
 +      hb_font_t *font = hb_ft_font_create(face, NULL);
 +      if (font == NULL)
 +              die("Failed to load Harfbuzz font.");
 +
-+      hbfontcache[hbfontslen].match = match;
-+      hbfontcache[hbfontslen].font = font;
-+      hbfontslen += 1;
++      hbfontcache.fonts[hbfontcache.capacity].match = match;
++      hbfontcache.fonts[hbfontcache.capacity].font = font;
++      hbfontcache.capacity += 1;
 +
 +      return font;
 +}
 +
 +void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, 
int start, int length) {
-+      Rune rune;
 +      ushort mode = USHRT_MAX;
 +      unsigned int glyph_count;
-+      int i, end = start + length;
++      int rune_idx, glyph_idx, end = start + length;
 +
 +      hb_font_t *font = hbfindfont(xfont);
 +      if (font == NULL)
@@ -126,14 +137,20 @@ index 0000000..bacec05
 +      hb_buffer_t *buffer = hb_buffer_create();
 +      hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
 +
++      /* Resize the buffer if required length is larger. */
++      if (hbrunebuffer.capacity < length) {
++              hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * 
BUFFER_STEP;
++              hbrunebuffer.runes = realloc(hbrunebuffer.runes, 
hbrunebuffer.capacity);
++      }
++
 +      /* Fill buffer with codepoints. */
-+      for (i = start; i < end; i++) {
-+              rune = glyphs[i].u;
-+              mode = glyphs[i].mode;
++      for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, 
rune_idx++) {
++              hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u;
++              mode = glyphs[glyph_idx].mode;
 +              if (mode & ATTR_WDUMMY)
-+                      rune = 0x0020;
-+              hb_buffer_add_codepoints(buffer, &rune, 1, 0, 1);
++                      hbrunebuffer.runes[rune_idx] = 0x0020;
 +      }
++      hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, length);
 +
 +      /* Shape the segment. */
 +      hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t));
@@ -142,7 +159,7 @@ index 0000000..bacec05
 +      hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_count);
 +      hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, 
&glyph_count);
 +
-+      /** Fill the output. */
++      /* Fill the output. */
 +      data->buffer = buffer;
 +      data->glyphs = info;
 +      data->positions = pos;
@@ -215,7 +232,7 @@ index 6de960d..94679e4 100644
  void xfinishdraw(void);
  void xloadcols(void);
 diff --git a/x.c b/x.c
-index 2a3bd38..5feac09 100644
+index 2a3bd38..9b97075 100644
 --- a/x.c
 +++ b/x.c
 @@ -19,6 +19,7 @@ char *argv0;
@@ -234,6 +251,15 @@ index 2a3bd38..5feac09 100644
  static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, 
int);
  static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, 
int);
  static void xdrawglyph(Glyph, int, int);
+@@ -757,7 +759,7 @@ xresize(int col, int row)
+       xclear(0, 0, win.w, win.h);
+ 
+       /* resize to new width */
+-      xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
++      xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
+ }
+ 
+ ushort
 @@ -1062,6 +1064,9 @@ xunloadfont(Font *f)
  void
  xunloadfonts(void)
@@ -244,6 +270,15 @@ index 2a3bd38..5feac09 100644
        /* Free the loaded fonts in the font cache.  */
        while (frclen > 0)
                XftFontClose(xw.dpy, frc[--frclen].font);
+@@ -1185,7 +1190,7 @@ xinit(int cols, int rows)
+       XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
+ 
+       /* font spec buffer */
+-      xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
++      xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
+ 
+       /* Xft rendering context */
+       xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
 @@ -1239,6 +1244,22 @@ xinit(int cols, int rows)
                xsel.xtarget = XA_STRING;
  }
@@ -251,7 +286,7 @@ index 2a3bd38..5feac09 100644
 +void
 +xresetfontsettings(ushort mode, Font **font, int *frcflags)
 +{
-+  *font = &dc.font;
++      *font = &dc.font;
 +      if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
 +              *font = &dc.ibfont;
 +              *frcflags = FRC_ITALICBOLD;
@@ -267,12 +302,13 @@ index 2a3bd38..5feac09 100644
  int
  xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, 
int x, int y)
  {
-@@ -1253,119 +1274,137 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const 
Glyph *glyphs, int len, int x
+@@ -1253,119 +1274,148 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const 
Glyph *glyphs, int len, int x
        FcPattern *fcpattern, *fontpattern;
        FcFontSet *fcsets[] = { NULL };
        FcCharSet *fccharset;
 -      int i, f, numspecs = 0;
 +      int i, f, length = 0, start = 0, numspecs = 0;
++  float cluster_xp = xp, cluster_yp = yp;
 +      HbTransformData shaped = { 0 };
 +
 +      /* Initial values. */
@@ -286,7 +322,7 @@ index 2a3bd38..5feac09 100644
  
                /* Skip dummy wide-character spacing. */
 -              if (mode == ATTR_WDUMMY)
-+              if (mode & ATTR_WDUMMY)
++              if (mode & ATTR_WDUMMY && i < (len - 1))
                        continue;
  
 -              /* Determine font for glyph if different from previous glyph. */
@@ -344,23 +380,34 @@ index 2a3bd38..5feac09 100644
 -                              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++) {
-+                              rune = glyphs[start + code_idx].u;
-+                              runewidth = win.cw * ((glyphs[start + 
code_idx].mode & ATTR_WIDE) ? 2.0f : 1.0f);
++                              int idx = shaped.glyphs[code_idx].cluster;
 +
-+                              if (glyphs[start + code_idx].mode & ATTR_WDUMMY)
++                              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 = xp + 
(short)shaped.positions[code_idx].x_offset;
-+                                      specs[numspecs].y = yp + 
(short)shaped.positions[code_idx].y_offset;
-+                                      xp += runewidth;
++                                      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. */
@@ -429,21 +476,17 @@ index 2a3bd38..5feac09 100644
 +                                      specs[numspecs].glyph = glyphidx;
 +                                      specs[numspecs].x = (short)xp;
 +                                      specs[numspecs].y = (short)yp;
-+                                      xp += runewidth;
 +                                      numspecs++;
 +                              }
                        }
 -              }
- 
+-
 -              /* Nothing was found. Use fontconfig to find matching font. */
 -              if (f >= frclen) {
 -                      if (!font->set)
 -                              font->set = FcFontSort(0, font->pattern,
 -                                                     1, 0, &fcres);
 -                      fcsets[0] = font->set;
-+                      /* Cleanup and get ready for next segment. */
-+                      hbcleanup(&shaped);
-+                      start = i;
  
 -                      /*
 -                       * Nothing was found in the cache. Now use
@@ -459,7 +502,10 @@ index 2a3bd38..5feac09 100644
 -                      FcPatternAddCharSet(fcpattern, FC_CHARSET,
 -                                      fccharset);
 -                      FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
--
++                      /* Cleanup and get ready for next segment. */
++                      hbcleanup(&shaped);
++                      start = i;
+ 
 -                      FcConfigSubstitute(0, fcpattern,
 -                                      FcMatchPattern);
 -                      FcDefaultSubstitute(fcpattern);
@@ -504,7 +550,7 @@ index 2a3bd38..5feac09 100644
        }
  
        return numspecs;
-@@ -1517,14 +1556,17 @@ xdrawglyph(Glyph g, int x, int y)
+@@ -1517,14 +1567,17 @@ xdrawglyph(Glyph g, int x, int y)
  }
  
  void
@@ -524,3 +570,39 @@ index 2a3bd38..5feac09 100644
  
        if (IS_SET(MODE_HIDE))
                return;
+@@ -1652,18 +1705,16 @@ xdrawline(Line line, int x1, int y1, int x2)
+       Glyph base, new;
+       XftGlyphFontSpec *specs = xw.specbuf;
+ 
+-      numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
+       i = ox = 0;
+-      for (x = x1; x < x2 && i < numspecs; x++) {
++      for (x = x1; x < x2; x++) {
+               new = line[x];
+               if (new.mode == ATTR_WDUMMY)
+                       continue;
+               if (selected(x, y1))
+                       new.mode ^= ATTR_REVERSE;
+-              if (i > 0 && ATTRCMP(base, new)) {
+-                      xdrawglyphfontspecs(specs, base, i, ox, y1);
+-                      specs += i;
+-                      numspecs -= i;
++              if ((i > 0) && ATTRCMP(base, new)) {
++                      numspecs = xmakeglyphfontspecs(specs, &line[ox], x - 
ox, ox, y1);
++                      xdrawglyphfontspecs(specs, base, numspecs, ox, y1);
+                       i = 0;
+               }
+               if (i == 0) {
+@@ -1672,8 +1723,10 @@ xdrawline(Line line, int x1, int y1, int x2)
+               }
+               i++;
+       }
+-      if (i > 0)
+-              xdrawglyphfontspecs(specs, base, i, ox, y1);
++      if (i > 0) {
++              numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox, 
y1);
++              xdrawglyphfontspecs(specs, base, numspecs, ox, y1);
++      }
+ }
+ 
+ void
diff --git 
a/st.suckless.org/patches/ligatures/0.9/st-ligatures-alpha-20221120-0.9.diff 
b/st.suckless.org/patches/ligatures/0.9/st-ligatures-alpha-20230105-0.9.diff
similarity index 74%
rename from 
st.suckless.org/patches/ligatures/0.9/st-ligatures-alpha-20221120-0.9.diff
rename to 
st.suckless.org/patches/ligatures/0.9/st-ligatures-alpha-20230105-0.9.diff
index 83c8f9fe..ddee323b 100644
--- a/st.suckless.org/patches/ligatures/0.9/st-ligatures-alpha-20221120-0.9.diff
+++ b/st.suckless.org/patches/ligatures/0.9/st-ligatures-alpha-20230105-0.9.diff
@@ -22,7 +22,7 @@ index 470ac86..38240da 100644
  $(OBJ): config.h config.mk
  
 diff --git a/config.mk b/config.mk
-index 47c615e..1ce493a 100644
+index 47c615e..d7439a3 100644
 --- a/config.mk
 +++ b/config.mk
 @@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config
@@ -30,9 +30,10 @@ index 47c615e..1ce493a 100644
  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` \
@@ -42,10 +43,10 @@ index 47c615e..1ce493a 100644
  STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
 diff --git a/hb.c b/hb.c
 new file mode 100644
-index 0000000..59b9200
+index 0000000..528c040
 --- /dev/null
 +++ b/hb.c
-@@ -0,0 +1,107 @@
+@@ -0,0 +1,124 @@
 +#include <stdlib.h>
 +#include <stdio.h>
 +#include <math.h>
@@ -58,6 +59,7 @@ index 0000000..59b9200
 +#include "hb.h"
 +
 +#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start 
= HB_FEATURE_GLOBAL_START, .end = HB_FEATURE_GLOBAL_END }
++#define BUFFER_STEP 256
 +
 +hb_font_t *hbfindfont(XftFont *match);
 +
@@ -66,8 +68,19 @@ index 0000000..59b9200
 +      hb_font_t *font;
 +} HbFontMatch;
 +
-+static int hbfontslen = 0;
-+static HbFontMatch *hbfontcache = NULL;
++typedef struct {
++      size_t capacity;
++      HbFontMatch *fonts;
++} HbFontCache;
++
++static HbFontCache hbfontcache = { 0, NULL };
++
++typedef struct {
++      size_t capacity;
++      Rune *runes;
++} RuneBuffer;
++
++static RuneBuffer hbrunebuffer = { 0, NULL };
 +
 +/*
 + * Poplulate the array with a list of font features, wrapped in FEATURE macro,
@@ -79,45 +92,44 @@ index 0000000..59b9200
 +void
 +hbunloadfonts()
 +{
-+      for (int i = 0; i < hbfontslen; i++) {
-+              hb_font_destroy(hbfontcache[i].font);
-+              XftUnlockFace(hbfontcache[i].match);
++      for (int i = 0; i < hbfontcache.capacity; i++) {
++              hb_font_destroy(hbfontcache.fonts[i].font);
++              XftUnlockFace(hbfontcache.fonts[i].match);
 +      }
 +
-+      if (hbfontcache != NULL) {
-+              free(hbfontcache);
-+              hbfontcache = NULL;
++      if (hbfontcache.fonts != NULL) {
++              free(hbfontcache.fonts);
++              hbfontcache.fonts = NULL;
 +      }
-+      hbfontslen = 0;
++      hbfontcache.capacity = 0;
 +}
 +
 +hb_font_t *
 +hbfindfont(XftFont *match)
 +{
-+      for (int i = 0; i < hbfontslen; i++) {
-+              if (hbfontcache[i].match == match)
-+                      return hbfontcache[i].font;
++      for (int i = 0; i < hbfontcache.capacity; i++) {
++              if (hbfontcache.fonts[i].match == match)
++                      return hbfontcache.fonts[i].font;
 +      }
 +
 +      /* Font not found in cache, caching it now. */
-+      hbfontcache = realloc(hbfontcache, sizeof(HbFontMatch) * (hbfontslen + 
1));
++      hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * 
(hbfontcache.capacity + 1));
 +      FT_Face face = XftLockFace(match);
 +      hb_font_t *font = hb_ft_font_create(face, NULL);
 +      if (font == NULL)
 +              die("Failed to load Harfbuzz font.");
 +
-+      hbfontcache[hbfontslen].match = match;
-+      hbfontcache[hbfontslen].font = font;
-+      hbfontslen += 1;
++      hbfontcache.fonts[hbfontcache.capacity].match = match;
++      hbfontcache.fonts[hbfontcache.capacity].font = font;
++      hbfontcache.capacity += 1;
 +
 +      return font;
 +}
 +
 +void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, 
int start, int length) {
-+      Rune rune;
 +      ushort mode = USHRT_MAX;
 +      unsigned int glyph_count;
-+      int i, end = start + length;
++      int rune_idx, glyph_idx, end = start + length;
 +
 +      hb_font_t *font = hbfindfont(xfont);
 +      if (font == NULL)
@@ -126,14 +138,20 @@ index 0000000..59b9200
 +      hb_buffer_t *buffer = hb_buffer_create();
 +      hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
 +
++      /* Resize the buffer if required length is larger. */
++      if (hbrunebuffer.capacity < length) {
++              hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * 
BUFFER_STEP;
++              hbrunebuffer.runes = realloc(hbrunebuffer.runes, 
hbrunebuffer.capacity);
++      }
++
 +      /* Fill buffer with codepoints. */
-+      for (i = start; i < end; i++) {
-+              rune = glyphs[i].u;
-+              mode = glyphs[i].mode;
++      for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, 
rune_idx++) {
++              hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u;
++              mode = glyphs[glyph_idx].mode;
 +              if (mode & ATTR_WDUMMY)
-+                      rune = 0x0020;
-+              hb_buffer_add_codepoints(buffer, &rune, 1, 0, 1);
++                      hbrunebuffer.runes[rune_idx] = 0x0020;
 +      }
++      hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, length);
 +
 +      /* Shape the segment. */
 +      hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t));
@@ -142,7 +160,7 @@ index 0000000..59b9200
 +      hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_count);
 +      hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, 
&glyph_count);
 +
-+      /** Fill the output. */
++      /* Fill the output. */
 +      data->buffer = buffer;
 +      data->glyphs = info;
 +      data->positions = pos;
@@ -215,7 +233,7 @@ index 6de960d..94679e4 100644
  void xfinishdraw(void);
  void xloadcols(void);
 diff --git a/x.c b/x.c
-index 27e81d1..34cb768 100644
+index 27e81d1..9d84793 100644
 --- a/x.c
 +++ b/x.c
 @@ -19,6 +19,7 @@ char *argv0;
@@ -234,6 +252,15 @@ index 27e81d1..34cb768 100644
  static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, 
int);
  static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, 
int);
  static void xdrawglyph(Glyph, int, int);
+@@ -759,7 +761,7 @@ xresize(int col, int row)
+       xclear(0, 0, win.w, win.h);
+ 
+       /* resize to new width */
+-      xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
++      xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
+ }
+ 
+ ushort
 @@ -1071,6 +1073,9 @@ xunloadfont(Font *f)
  void
  xunloadfonts(void)
@@ -244,6 +271,15 @@ index 27e81d1..34cb768 100644
        /* Free the loaded fonts in the font cache.  */
        while (frclen > 0)
                XftFontClose(xw.dpy, frc[--frclen].font);
+@@ -1202,7 +1207,7 @@ xinit(int cols, int rows)
+       XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
+ 
+       /* font spec buffer */
+-      xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
++      xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
+ 
+       /* Xft rendering context */
+       xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
 @@ -1256,6 +1261,22 @@ xinit(int cols, int rows)
                xsel.xtarget = XA_STRING;
  }
@@ -251,7 +287,7 @@ index 27e81d1..34cb768 100644
 +void
 +xresetfontsettings(ushort mode, Font **font, int *frcflags)
 +{
-+  *font = &dc.font;
++      *font = &dc.font;
 +      if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
 +              *font = &dc.ibfont;
 +              *frcflags = FRC_ITALICBOLD;
@@ -267,12 +303,13 @@ index 27e81d1..34cb768 100644
  int
  xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, 
int x, int y)
  {
-@@ -1270,119 +1291,137 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const 
Glyph *glyphs, int len, int x
+@@ -1270,121 +1291,151 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const 
Glyph *glyphs, int len, int x
        FcPattern *fcpattern, *fontpattern;
        FcFontSet *fcsets[] = { NULL };
        FcCharSet *fccharset;
 -      int i, f, numspecs = 0;
 +      int i, f, length = 0, start = 0, numspecs = 0;
++  float cluster_xp = xp, cluster_yp = yp;
 +      HbTransformData shaped = { 0 };
 +
 +      /* Initial values. */
@@ -286,7 +323,7 @@ index 27e81d1..34cb768 100644
  
                /* Skip dummy wide-character spacing. */
 -              if (mode == ATTR_WDUMMY)
-+              if (mode & ATTR_WDUMMY)
++              if (mode & ATTR_WDUMMY && i < (len - 1))
                        continue;
  
 -              /* Determine font for glyph if different from previous glyph. */
@@ -344,23 +381,34 @@ index 27e81d1..34cb768 100644
 -                              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++) {
-+                              rune = glyphs[start + code_idx].u;
-+                              runewidth = win.cw * ((glyphs[start + 
code_idx].mode & ATTR_WIDE) ? 2.0f : 1.0f);
++                              int idx = shaped.glyphs[code_idx].cluster;
 +
-+                              if (glyphs[start + code_idx].mode & ATTR_WDUMMY)
++                              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 = xp + 
(short)shaped.positions[code_idx].x_offset;
-+                                      specs[numspecs].y = yp + 
(short)shaped.positions[code_idx].y_offset;
-+                                      xp += runewidth;
++                                      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. */
@@ -429,21 +477,17 @@ index 27e81d1..34cb768 100644
 +                                      specs[numspecs].glyph = glyphidx;
 +                                      specs[numspecs].x = (short)xp;
 +                                      specs[numspecs].y = (short)yp;
-+                                      xp += runewidth;
 +                                      numspecs++;
 +                              }
                        }
 -              }
- 
+-
 -              /* Nothing was found. Use fontconfig to find matching font. */
 -              if (f >= frclen) {
 -                      if (!font->set)
 -                              font->set = FcFontSort(0, font->pattern,
 -                                                     1, 0, &fcres);
 -                      fcsets[0] = font->set;
-+                      /* Cleanup and get ready for next segment. */
-+                      hbcleanup(&shaped);
-+                      start = i;
  
 -                      /*
 -                       * Nothing was found in the cache. Now use
@@ -459,7 +503,10 @@ index 27e81d1..34cb768 100644
 -                      FcPatternAddCharSet(fcpattern, FC_CHARSET,
 -                                      fccharset);
 -                      FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
--
++                      /* Cleanup and get ready for next segment. */
++                      hbcleanup(&shaped);
++                      start = i;
+ 
 -                      FcConfigSubstitute(0, fcpattern,
 -                                      FcMatchPattern);
 -                      FcDefaultSubstitute(fcpattern);
@@ -503,8 +550,11 @@ index 27e81d1..34cb768 100644
 -              numspecs++;
        }
  
++      hbcleanup(&shaped);
        return numspecs;
-@@ -1534,14 +1573,17 @@ xdrawglyph(Glyph g, int x, int y)
+ }
+ 
+@@ -1534,14 +1585,17 @@ xdrawglyph(Glyph g, int x, int y)
  }
  
  void
@@ -524,3 +574,39 @@ index 27e81d1..34cb768 100644
  
        if (IS_SET(MODE_HIDE))
                return;
+@@ -1669,18 +1723,16 @@ xdrawline(Line line, int x1, int y1, int x2)
+       Glyph base, new;
+       XftGlyphFontSpec *specs = xw.specbuf;
+ 
+-      numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
+       i = ox = 0;
+-      for (x = x1; x < x2 && i < numspecs; x++) {
++      for (x = x1; x < x2; x++) {
+               new = line[x];
+               if (new.mode == ATTR_WDUMMY)
+                       continue;
+               if (selected(x, y1))
+                       new.mode ^= ATTR_REVERSE;
+-              if (i > 0 && ATTRCMP(base, new)) {
+-                      xdrawglyphfontspecs(specs, base, i, ox, y1);
+-                      specs += i;
+-                      numspecs -= i;
++              if ((i > 0) && ATTRCMP(base, new)) {
++                      numspecs = xmakeglyphfontspecs(specs, &line[ox], x - 
ox, ox, y1);
++                      xdrawglyphfontspecs(specs, base, numspecs, ox, y1);
+                       i = 0;
+               }
+               if (i == 0) {
+@@ -1689,8 +1741,10 @@ xdrawline(Line line, int x1, int y1, int x2)
+               }
+               i++;
+       }
+-      if (i > 0)
+-              xdrawglyphfontspecs(specs, base, i, ox, y1);
++      if (i > 0) {
++              numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox, 
y1);
++              xdrawglyphfontspecs(specs, base, numspecs, ox, y1);
++      }
+ }
+ 
+ void
diff --git 
a/st.suckless.org/patches/ligatures/0.9/st-ligatures-alpha-scrollback-20221120-0.9.diff
 
b/st.suckless.org/patches/ligatures/0.9/st-ligatures-alpha-scrollback-20230105-0.9.diff
similarity index 74%
rename from 
st.suckless.org/patches/ligatures/0.9/st-ligatures-alpha-scrollback-20221120-0.9.diff
rename to 
st.suckless.org/patches/ligatures/0.9/st-ligatures-alpha-scrollback-20230105-0.9.diff
index 230e177f..e49bc9d3 100644
--- 
a/st.suckless.org/patches/ligatures/0.9/st-ligatures-alpha-scrollback-20221120-0.9.diff
+++ 
b/st.suckless.org/patches/ligatures/0.9/st-ligatures-alpha-scrollback-20230105-0.9.diff
@@ -22,7 +22,7 @@ index 470ac86..38240da 100644
  $(OBJ): config.h config.mk
  
 diff --git a/config.mk b/config.mk
-index 47c615e..1ce493a 100644
+index 47c615e..d7439a3 100644
 --- a/config.mk
 +++ b/config.mk
 @@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config
@@ -30,9 +30,10 @@ index 47c615e..1ce493a 100644
  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` \
@@ -42,10 +43,10 @@ index 47c615e..1ce493a 100644
  STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
 diff --git a/hb.c b/hb.c
 new file mode 100644
-index 0000000..59b9200
+index 0000000..528c040
 --- /dev/null
 +++ b/hb.c
-@@ -0,0 +1,107 @@
+@@ -0,0 +1,124 @@
 +#include <stdlib.h>
 +#include <stdio.h>
 +#include <math.h>
@@ -58,6 +59,7 @@ index 0000000..59b9200
 +#include "hb.h"
 +
 +#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start 
= HB_FEATURE_GLOBAL_START, .end = HB_FEATURE_GLOBAL_END }
++#define BUFFER_STEP 256
 +
 +hb_font_t *hbfindfont(XftFont *match);
 +
@@ -66,8 +68,19 @@ index 0000000..59b9200
 +      hb_font_t *font;
 +} HbFontMatch;
 +
-+static int hbfontslen = 0;
-+static HbFontMatch *hbfontcache = NULL;
++typedef struct {
++      size_t capacity;
++      HbFontMatch *fonts;
++} HbFontCache;
++
++static HbFontCache hbfontcache = { 0, NULL };
++
++typedef struct {
++      size_t capacity;
++      Rune *runes;
++} RuneBuffer;
++
++static RuneBuffer hbrunebuffer = { 0, NULL };
 +
 +/*
 + * Poplulate the array with a list of font features, wrapped in FEATURE macro,
@@ -79,45 +92,44 @@ index 0000000..59b9200
 +void
 +hbunloadfonts()
 +{
-+      for (int i = 0; i < hbfontslen; i++) {
-+              hb_font_destroy(hbfontcache[i].font);
-+              XftUnlockFace(hbfontcache[i].match);
++      for (int i = 0; i < hbfontcache.capacity; i++) {
++              hb_font_destroy(hbfontcache.fonts[i].font);
++              XftUnlockFace(hbfontcache.fonts[i].match);
 +      }
 +
-+      if (hbfontcache != NULL) {
-+              free(hbfontcache);
-+              hbfontcache = NULL;
++      if (hbfontcache.fonts != NULL) {
++              free(hbfontcache.fonts);
++              hbfontcache.fonts = NULL;
 +      }
-+      hbfontslen = 0;
++      hbfontcache.capacity = 0;
 +}
 +
 +hb_font_t *
 +hbfindfont(XftFont *match)
 +{
-+      for (int i = 0; i < hbfontslen; i++) {
-+              if (hbfontcache[i].match == match)
-+                      return hbfontcache[i].font;
++      for (int i = 0; i < hbfontcache.capacity; i++) {
++              if (hbfontcache.fonts[i].match == match)
++                      return hbfontcache.fonts[i].font;
 +      }
 +
 +      /* Font not found in cache, caching it now. */
-+      hbfontcache = realloc(hbfontcache, sizeof(HbFontMatch) * (hbfontslen + 
1));
++      hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * 
(hbfontcache.capacity + 1));
 +      FT_Face face = XftLockFace(match);
 +      hb_font_t *font = hb_ft_font_create(face, NULL);
 +      if (font == NULL)
 +              die("Failed to load Harfbuzz font.");
 +
-+      hbfontcache[hbfontslen].match = match;
-+      hbfontcache[hbfontslen].font = font;
-+      hbfontslen += 1;
++      hbfontcache.fonts[hbfontcache.capacity].match = match;
++      hbfontcache.fonts[hbfontcache.capacity].font = font;
++      hbfontcache.capacity += 1;
 +
 +      return font;
 +}
 +
 +void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, 
int start, int length) {
-+      Rune rune;
 +      ushort mode = USHRT_MAX;
 +      unsigned int glyph_count;
-+      int i, end = start + length;
++      int rune_idx, glyph_idx, end = start + length;
 +
 +      hb_font_t *font = hbfindfont(xfont);
 +      if (font == NULL)
@@ -126,14 +138,20 @@ index 0000000..59b9200
 +      hb_buffer_t *buffer = hb_buffer_create();
 +      hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
 +
++      /* Resize the buffer if required length is larger. */
++      if (hbrunebuffer.capacity < length) {
++              hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * 
BUFFER_STEP;
++              hbrunebuffer.runes = realloc(hbrunebuffer.runes, 
hbrunebuffer.capacity);
++      }
++
 +      /* Fill buffer with codepoints. */
-+      for (i = start; i < end; i++) {
-+              rune = glyphs[i].u;
-+              mode = glyphs[i].mode;
++      for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, 
rune_idx++) {
++              hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u;
++              mode = glyphs[glyph_idx].mode;
 +              if (mode & ATTR_WDUMMY)
-+                      rune = 0x0020;
-+              hb_buffer_add_codepoints(buffer, &rune, 1, 0, 1);
++                      hbrunebuffer.runes[rune_idx] = 0x0020;
 +      }
++      hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, length);
 +
 +      /* Shape the segment. */
 +      hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t));
@@ -142,7 +160,7 @@ index 0000000..59b9200
 +      hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_count);
 +      hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, 
&glyph_count);
 +
-+      /** Fill the output. */
++      /* Fill the output. */
 +      data->buffer = buffer;
 +      data->glyphs = info;
 +      data->positions = pos;
@@ -174,16 +192,17 @@ index 0000000..88de9bd
 +void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
 +void hbcleanup(HbTransformData *);
 diff --git a/st.c b/st.c
-index 79ee9ba..7675db6 100644
+index 79ee9ba..454771d 100644
 --- a/st.c
 +++ b/st.c
-@@ -2711,7 +2711,8 @@ draw(void)
+@@ -2711,7 +2711,9 @@ draw(void)
        drawregion(0, 0, term.col, term.row);
        if (term.scr == 0)
                xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
 -                              term.ocx, term.ocy, 
term.line[term.ocy][term.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();
@@ -215,7 +234,7 @@ index 6de960d..94679e4 100644
  void xfinishdraw(void);
  void xloadcols(void);
 diff --git a/x.c b/x.c
-index 27e81d1..34cb768 100644
+index 27e81d1..5d19ed7 100644
 --- a/x.c
 +++ b/x.c
 @@ -19,6 +19,7 @@ char *argv0;
@@ -234,6 +253,15 @@ index 27e81d1..34cb768 100644
  static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, 
int);
  static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, 
int);
  static void xdrawglyph(Glyph, int, int);
+@@ -759,7 +761,7 @@ xresize(int col, int row)
+       xclear(0, 0, win.w, win.h);
+ 
+       /* resize to new width */
+-      xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
++      xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
+ }
+ 
+ ushort
 @@ -1071,6 +1073,9 @@ xunloadfont(Font *f)
  void
  xunloadfonts(void)
@@ -244,6 +272,15 @@ index 27e81d1..34cb768 100644
        /* Free the loaded fonts in the font cache.  */
        while (frclen > 0)
                XftFontClose(xw.dpy, frc[--frclen].font);
+@@ -1202,7 +1207,7 @@ xinit(int cols, int rows)
+       XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
+ 
+       /* font spec buffer */
+-      xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
++      xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
+ 
+       /* Xft rendering context */
+       xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
 @@ -1256,6 +1261,22 @@ xinit(int cols, int rows)
                xsel.xtarget = XA_STRING;
  }
@@ -251,7 +288,7 @@ index 27e81d1..34cb768 100644
 +void
 +xresetfontsettings(ushort mode, Font **font, int *frcflags)
 +{
-+  *font = &dc.font;
++      *font = &dc.font;
 +      if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
 +              *font = &dc.ibfont;
 +              *frcflags = FRC_ITALICBOLD;
@@ -267,12 +304,13 @@ index 27e81d1..34cb768 100644
  int
  xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, 
int x, int y)
  {
-@@ -1270,119 +1291,137 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const 
Glyph *glyphs, int len, int x
+@@ -1270,119 +1291,148 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const 
Glyph *glyphs, int len, int x
        FcPattern *fcpattern, *fontpattern;
        FcFontSet *fcsets[] = { NULL };
        FcCharSet *fccharset;
 -      int i, f, numspecs = 0;
 +      int i, f, length = 0, start = 0, numspecs = 0;
++  float cluster_xp = xp, cluster_yp = yp;
 +      HbTransformData shaped = { 0 };
 +
 +      /* Initial values. */
@@ -286,7 +324,7 @@ index 27e81d1..34cb768 100644
  
                /* Skip dummy wide-character spacing. */
 -              if (mode == ATTR_WDUMMY)
-+              if (mode & ATTR_WDUMMY)
++              if (mode & ATTR_WDUMMY && i < (len - 1))
                        continue;
  
 -              /* Determine font for glyph if different from previous glyph. */
@@ -344,23 +382,34 @@ index 27e81d1..34cb768 100644
 -                              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++) {
-+                              rune = glyphs[start + code_idx].u;
-+                              runewidth = win.cw * ((glyphs[start + 
code_idx].mode & ATTR_WIDE) ? 2.0f : 1.0f);
++                              int idx = shaped.glyphs[code_idx].cluster;
 +
-+                              if (glyphs[start + code_idx].mode & ATTR_WDUMMY)
++                              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 = xp + 
(short)shaped.positions[code_idx].x_offset;
-+                                      specs[numspecs].y = yp + 
(short)shaped.positions[code_idx].y_offset;
-+                                      xp += runewidth;
++                                      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. */
@@ -429,21 +478,17 @@ index 27e81d1..34cb768 100644
 +                                      specs[numspecs].glyph = glyphidx;
 +                                      specs[numspecs].x = (short)xp;
 +                                      specs[numspecs].y = (short)yp;
-+                                      xp += runewidth;
 +                                      numspecs++;
 +                              }
                        }
 -              }
- 
+-
 -              /* Nothing was found. Use fontconfig to find matching font. */
 -              if (f >= frclen) {
 -                      if (!font->set)
 -                              font->set = FcFontSort(0, font->pattern,
 -                                                     1, 0, &fcres);
 -                      fcsets[0] = font->set;
-+                      /* Cleanup and get ready for next segment. */
-+                      hbcleanup(&shaped);
-+                      start = i;
  
 -                      /*
 -                       * Nothing was found in the cache. Now use
@@ -459,7 +504,10 @@ index 27e81d1..34cb768 100644
 -                      FcPatternAddCharSet(fcpattern, FC_CHARSET,
 -                                      fccharset);
 -                      FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
--
++                      /* Cleanup and get ready for next segment. */
++                      hbcleanup(&shaped);
++                      start = i;
+ 
 -                      FcConfigSubstitute(0, fcpattern,
 -                                      FcMatchPattern);
 -                      FcDefaultSubstitute(fcpattern);
@@ -504,7 +552,7 @@ index 27e81d1..34cb768 100644
        }
  
        return numspecs;
-@@ -1534,14 +1573,17 @@ xdrawglyph(Glyph g, int x, int y)
+@@ -1534,14 +1584,17 @@ xdrawglyph(Glyph g, int x, int y)
  }
  
  void
@@ -524,3 +572,39 @@ index 27e81d1..34cb768 100644
  
        if (IS_SET(MODE_HIDE))
                return;
+@@ -1669,18 +1722,16 @@ xdrawline(Line line, int x1, int y1, int x2)
+       Glyph base, new;
+       XftGlyphFontSpec *specs = xw.specbuf;
+ 
+-      numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
+       i = ox = 0;
+-      for (x = x1; x < x2 && i < numspecs; x++) {
++      for (x = x1; x < x2; x++) {
+               new = line[x];
+               if (new.mode == ATTR_WDUMMY)
+                       continue;
+               if (selected(x, y1))
+                       new.mode ^= ATTR_REVERSE;
+-              if (i > 0 && ATTRCMP(base, new)) {
+-                      xdrawglyphfontspecs(specs, base, i, ox, y1);
+-                      specs += i;
+-                      numspecs -= i;
++              if ((i > 0) && ATTRCMP(base, new)) {
++                      numspecs = xmakeglyphfontspecs(specs, &line[ox], x - 
ox, ox, y1);
++                      xdrawglyphfontspecs(specs, base, numspecs, ox, y1);
+                       i = 0;
+               }
+               if (i == 0) {
+@@ -1689,8 +1740,10 @@ xdrawline(Line line, int x1, int y1, int x2)
+               }
+               i++;
+       }
+-      if (i > 0)
+-              xdrawglyphfontspecs(specs, base, i, ox, y1);
++      if (i > 0) {
++              numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox, 
y1);
++              xdrawglyphfontspecs(specs, base, numspecs, ox, y1);
++      }
+ }
+ 
+ void
diff --git 
a/st.suckless.org/patches/ligatures/0.9/st-ligatures-alpha-scrollback-ringbuffer-20221120-0.9.diff
 
b/st.suckless.org/patches/ligatures/0.9/st-ligatures-alpha-scrollback-ringbuffer-20230105-0.9.diff
similarity index 74%
rename from 
st.suckless.org/patches/ligatures/0.9/st-ligatures-alpha-scrollback-ringbuffer-20221120-0.9.diff
rename to 
st.suckless.org/patches/ligatures/0.9/st-ligatures-alpha-scrollback-ringbuffer-20230105-0.9.diff
index 586cbbce..b4a23522 100644
--- 
a/st.suckless.org/patches/ligatures/0.9/st-ligatures-alpha-scrollback-ringbuffer-20221120-0.9.diff
+++ 
b/st.suckless.org/patches/ligatures/0.9/st-ligatures-alpha-scrollback-ringbuffer-20230105-0.9.diff
@@ -22,7 +22,7 @@ index 470ac86..38240da 100644
  $(OBJ): config.h config.mk
  
 diff --git a/config.mk b/config.mk
-index 47c615e..1ce493a 100644
+index 47c615e..d7439a3 100644
 --- a/config.mk
 +++ b/config.mk
 @@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config
@@ -30,9 +30,10 @@ index 47c615e..1ce493a 100644
  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` \
@@ -42,10 +43,10 @@ index 47c615e..1ce493a 100644
  STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
 diff --git a/hb.c b/hb.c
 new file mode 100644
-index 0000000..59b9200
+index 0000000..528c040
 --- /dev/null
 +++ b/hb.c
-@@ -0,0 +1,107 @@
+@@ -0,0 +1,124 @@
 +#include <stdlib.h>
 +#include <stdio.h>
 +#include <math.h>
@@ -58,6 +59,7 @@ index 0000000..59b9200
 +#include "hb.h"
 +
 +#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start 
= HB_FEATURE_GLOBAL_START, .end = HB_FEATURE_GLOBAL_END }
++#define BUFFER_STEP 256
 +
 +hb_font_t *hbfindfont(XftFont *match);
 +
@@ -66,8 +68,19 @@ index 0000000..59b9200
 +      hb_font_t *font;
 +} HbFontMatch;
 +
-+static int hbfontslen = 0;
-+static HbFontMatch *hbfontcache = NULL;
++typedef struct {
++      size_t capacity;
++      HbFontMatch *fonts;
++} HbFontCache;
++
++static HbFontCache hbfontcache = { 0, NULL };
++
++typedef struct {
++      size_t capacity;
++      Rune *runes;
++} RuneBuffer;
++
++static RuneBuffer hbrunebuffer = { 0, NULL };
 +
 +/*
 + * Poplulate the array with a list of font features, wrapped in FEATURE macro,
@@ -79,45 +92,44 @@ index 0000000..59b9200
 +void
 +hbunloadfonts()
 +{
-+      for (int i = 0; i < hbfontslen; i++) {
-+              hb_font_destroy(hbfontcache[i].font);
-+              XftUnlockFace(hbfontcache[i].match);
++      for (int i = 0; i < hbfontcache.capacity; i++) {
++              hb_font_destroy(hbfontcache.fonts[i].font);
++              XftUnlockFace(hbfontcache.fonts[i].match);
 +      }
 +
-+      if (hbfontcache != NULL) {
-+              free(hbfontcache);
-+              hbfontcache = NULL;
++      if (hbfontcache.fonts != NULL) {
++              free(hbfontcache.fonts);
++              hbfontcache.fonts = NULL;
 +      }
-+      hbfontslen = 0;
++      hbfontcache.capacity = 0;
 +}
 +
 +hb_font_t *
 +hbfindfont(XftFont *match)
 +{
-+      for (int i = 0; i < hbfontslen; i++) {
-+              if (hbfontcache[i].match == match)
-+                      return hbfontcache[i].font;
++      for (int i = 0; i < hbfontcache.capacity; i++) {
++              if (hbfontcache.fonts[i].match == match)
++                      return hbfontcache.fonts[i].font;
 +      }
 +
 +      /* Font not found in cache, caching it now. */
-+      hbfontcache = realloc(hbfontcache, sizeof(HbFontMatch) * (hbfontslen + 
1));
++      hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * 
(hbfontcache.capacity + 1));
 +      FT_Face face = XftLockFace(match);
 +      hb_font_t *font = hb_ft_font_create(face, NULL);
 +      if (font == NULL)
 +              die("Failed to load Harfbuzz font.");
 +
-+      hbfontcache[hbfontslen].match = match;
-+      hbfontcache[hbfontslen].font = font;
-+      hbfontslen += 1;
++      hbfontcache.fonts[hbfontcache.capacity].match = match;
++      hbfontcache.fonts[hbfontcache.capacity].font = font;
++      hbfontcache.capacity += 1;
 +
 +      return font;
 +}
 +
 +void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, 
int start, int length) {
-+      Rune rune;
 +      ushort mode = USHRT_MAX;
 +      unsigned int glyph_count;
-+      int i, end = start + length;
++      int rune_idx, glyph_idx, end = start + length;
 +
 +      hb_font_t *font = hbfindfont(xfont);
 +      if (font == NULL)
@@ -126,14 +138,20 @@ index 0000000..59b9200
 +      hb_buffer_t *buffer = hb_buffer_create();
 +      hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
 +
++      /* Resize the buffer if required length is larger. */
++      if (hbrunebuffer.capacity < length) {
++              hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * 
BUFFER_STEP;
++              hbrunebuffer.runes = realloc(hbrunebuffer.runes, 
hbrunebuffer.capacity);
++      }
++
 +      /* Fill buffer with codepoints. */
-+      for (i = start; i < end; i++) {
-+              rune = glyphs[i].u;
-+              mode = glyphs[i].mode;
++      for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, 
rune_idx++) {
++              hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u;
++              mode = glyphs[glyph_idx].mode;
 +              if (mode & ATTR_WDUMMY)
-+                      rune = 0x0020;
-+              hb_buffer_add_codepoints(buffer, &rune, 1, 0, 1);
++                      hbrunebuffer.runes[rune_idx] = 0x0020;
 +      }
++      hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, length);
 +
 +      /* Shape the segment. */
 +      hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t));
@@ -142,7 +160,7 @@ index 0000000..59b9200
 +      hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_count);
 +      hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, 
&glyph_count);
 +
-+      /** Fill the output. */
++      /* Fill the output. */
 +      data->buffer = buffer;
 +      data->glyphs = info;
 +      data->positions = pos;
@@ -174,16 +192,17 @@ index 0000000..88de9bd
 +void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
 +void hbcleanup(HbTransformData *);
 diff --git a/st.c b/st.c
-index 79ee9ba..7675db6 100644
+index 79ee9ba..454771d 100644
 --- a/st.c
 +++ b/st.c
-@@ -2762,7 +2762,8 @@ draw(void)
+@@ -2711,7 +2711,9 @@ draw(void)
        drawregion(0, 0, term.col, term.row);
        if (TSCREEN.off == 0)
                xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx],
 -                              term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]);
 +                              term.ocx, term.ocy, TLINE(term.ocy)[term.ocx],
 +                              TLINE(term.ocy), term.col);
++
        term.ocx = cx;
        term.ocy = term.c.y;
        xfinishdraw();
@@ -215,7 +234,7 @@ index 6de960d..94679e4 100644
  void xfinishdraw(void);
  void xloadcols(void);
 diff --git a/x.c b/x.c
-index 27e81d1..34cb768 100644
+index 27e81d1..5d19ed7 100644
 --- a/x.c
 +++ b/x.c
 @@ -19,6 +19,7 @@ char *argv0;
@@ -234,6 +253,15 @@ index 27e81d1..34cb768 100644
  static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, 
int);
  static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, 
int);
  static void xdrawglyph(Glyph, int, int);
+@@ -759,7 +761,7 @@ xresize(int col, int row)
+       xclear(0, 0, win.w, win.h);
+ 
+       /* resize to new width */
+-      xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
++      xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
+ }
+ 
+ ushort
 @@ -1071,6 +1073,9 @@ xunloadfont(Font *f)
  void
  xunloadfonts(void)
@@ -244,6 +272,15 @@ index 27e81d1..34cb768 100644
        /* Free the loaded fonts in the font cache.  */
        while (frclen > 0)
                XftFontClose(xw.dpy, frc[--frclen].font);
+@@ -1202,7 +1207,7 @@ xinit(int cols, int rows)
+       XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
+ 
+       /* font spec buffer */
+-      xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
++      xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
+ 
+       /* Xft rendering context */
+       xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
 @@ -1256,6 +1261,22 @@ xinit(int cols, int rows)
                xsel.xtarget = XA_STRING;
  }
@@ -251,7 +288,7 @@ index 27e81d1..34cb768 100644
 +void
 +xresetfontsettings(ushort mode, Font **font, int *frcflags)
 +{
-+  *font = &dc.font;
++      *font = &dc.font;
 +      if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
 +              *font = &dc.ibfont;
 +              *frcflags = FRC_ITALICBOLD;
@@ -267,12 +304,13 @@ index 27e81d1..34cb768 100644
  int
  xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, 
int x, int y)
  {
-@@ -1270,119 +1291,137 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const 
Glyph *glyphs, int len, int x
+@@ -1270,119 +1291,148 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const 
Glyph *glyphs, int len, int x
        FcPattern *fcpattern, *fontpattern;
        FcFontSet *fcsets[] = { NULL };
        FcCharSet *fccharset;
 -      int i, f, numspecs = 0;
 +      int i, f, length = 0, start = 0, numspecs = 0;
++  float cluster_xp = xp, cluster_yp = yp;
 +      HbTransformData shaped = { 0 };
 +
 +      /* Initial values. */
@@ -286,7 +324,7 @@ index 27e81d1..34cb768 100644
  
                /* Skip dummy wide-character spacing. */
 -              if (mode == ATTR_WDUMMY)
-+              if (mode & ATTR_WDUMMY)
++              if (mode & ATTR_WDUMMY && i < (len - 1))
                        continue;
  
 -              /* Determine font for glyph if different from previous glyph. */
@@ -344,23 +382,34 @@ index 27e81d1..34cb768 100644
 -                              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++) {
-+                              rune = glyphs[start + code_idx].u;
-+                              runewidth = win.cw * ((glyphs[start + 
code_idx].mode & ATTR_WIDE) ? 2.0f : 1.0f);
++                              int idx = shaped.glyphs[code_idx].cluster;
 +
-+                              if (glyphs[start + code_idx].mode & ATTR_WDUMMY)
++                              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 = xp + 
(short)shaped.positions[code_idx].x_offset;
-+                                      specs[numspecs].y = yp + 
(short)shaped.positions[code_idx].y_offset;
-+                                      xp += runewidth;
++                                      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. */
@@ -429,21 +478,17 @@ index 27e81d1..34cb768 100644
 +                                      specs[numspecs].glyph = glyphidx;
 +                                      specs[numspecs].x = (short)xp;
 +                                      specs[numspecs].y = (short)yp;
-+                                      xp += runewidth;
 +                                      numspecs++;
 +                              }
                        }
 -              }
- 
+-
 -              /* Nothing was found. Use fontconfig to find matching font. */
 -              if (f >= frclen) {
 -                      if (!font->set)
 -                              font->set = FcFontSort(0, font->pattern,
 -                                                     1, 0, &fcres);
 -                      fcsets[0] = font->set;
-+                      /* Cleanup and get ready for next segment. */
-+                      hbcleanup(&shaped);
-+                      start = i;
  
 -                      /*
 -                       * Nothing was found in the cache. Now use
@@ -459,7 +504,10 @@ index 27e81d1..34cb768 100644
 -                      FcPatternAddCharSet(fcpattern, FC_CHARSET,
 -                                      fccharset);
 -                      FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
--
++                      /* Cleanup and get ready for next segment. */
++                      hbcleanup(&shaped);
++                      start = i;
+ 
 -                      FcConfigSubstitute(0, fcpattern,
 -                                      FcMatchPattern);
 -                      FcDefaultSubstitute(fcpattern);
@@ -504,7 +552,7 @@ index 27e81d1..34cb768 100644
        }
  
        return numspecs;
-@@ -1534,14 +1573,17 @@ xdrawglyph(Glyph g, int x, int y)
+@@ -1534,14 +1584,17 @@ xdrawglyph(Glyph g, int x, int y)
  }
  
  void
@@ -524,3 +572,39 @@ index 27e81d1..34cb768 100644
  
        if (IS_SET(MODE_HIDE))
                return;
+@@ -1669,18 +1722,16 @@ xdrawline(Line line, int x1, int y1, int x2)
+       Glyph base, new;
+       XftGlyphFontSpec *specs = xw.specbuf;
+ 
+-      numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
+       i = ox = 0;
+-      for (x = x1; x < x2 && i < numspecs; x++) {
++      for (x = x1; x < x2; x++) {
+               new = line[x];
+               if (new.mode == ATTR_WDUMMY)
+                       continue;
+               if (selected(x, y1))
+                       new.mode ^= ATTR_REVERSE;
+-              if (i > 0 && ATTRCMP(base, new)) {
+-                      xdrawglyphfontspecs(specs, base, i, ox, y1);
+-                      specs += i;
+-                      numspecs -= i;
++              if ((i > 0) && ATTRCMP(base, new)) {
++                      numspecs = xmakeglyphfontspecs(specs, &line[ox], x - 
ox, ox, y1);
++                      xdrawglyphfontspecs(specs, base, numspecs, ox, y1);
+                       i = 0;
+               }
+               if (i == 0) {
+@@ -1689,8 +1740,10 @@ xdrawline(Line line, int x1, int y1, int x2)
+               }
+               i++;
+       }
+-      if (i > 0)
+-              xdrawglyphfontspecs(specs, base, i, ox, y1);
++      if (i > 0) {
++              numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox, 
y1);
++              xdrawglyphfontspecs(specs, base, numspecs, ox, y1);
++      }
+ }
+ 
+ void
diff --git 
a/st.suckless.org/patches/ligatures/0.9/st-ligatures-boxdraw-20221120-0.9.diff 
b/st.suckless.org/patches/ligatures/0.9/st-ligatures-boxdraw-20230105-0.9.diff
similarity index 76%
rename from 
st.suckless.org/patches/ligatures/0.9/st-ligatures-boxdraw-20221120-0.9.diff
rename to 
st.suckless.org/patches/ligatures/0.9/st-ligatures-boxdraw-20230105-0.9.diff
index fa11b28b..6191e7fd 100644
--- 
a/st.suckless.org/patches/ligatures/0.9/st-ligatures-boxdraw-20221120-0.9.diff
+++ 
b/st.suckless.org/patches/ligatures/0.9/st-ligatures-boxdraw-20230105-0.9.diff
@@ -43,10 +43,10 @@ index 1e306f8..3e13e53 100644
  STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
 diff --git a/hb.c b/hb.c
 new file mode 100644
-index 0000000..59b9200
+index 0000000..528c040
 --- /dev/null
 +++ b/hb.c
-@@ -0,0 +1,107 @@
+@@ -0,0 +1,124 @@
 +#include <stdlib.h>
 +#include <stdio.h>
 +#include <math.h>
@@ -59,6 +59,7 @@ index 0000000..59b9200
 +#include "hb.h"
 +
 +#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start 
= HB_FEATURE_GLOBAL_START, .end = HB_FEATURE_GLOBAL_END }
++#define BUFFER_STEP 256
 +
 +hb_font_t *hbfindfont(XftFont *match);
 +
@@ -67,8 +68,19 @@ index 0000000..59b9200
 +      hb_font_t *font;
 +} HbFontMatch;
 +
-+static int hbfontslen = 0;
-+static HbFontMatch *hbfontcache = NULL;
++typedef struct {
++      size_t capacity;
++      HbFontMatch *fonts;
++} HbFontCache;
++
++static HbFontCache hbfontcache = { 0, NULL };
++
++typedef struct {
++      size_t capacity;
++      Rune *runes;
++} RuneBuffer;
++
++static RuneBuffer hbrunebuffer = { 0, NULL };
 +
 +/*
 + * Poplulate the array with a list of font features, wrapped in FEATURE macro,
@@ -80,45 +92,44 @@ index 0000000..59b9200
 +void
 +hbunloadfonts()
 +{
-+      for (int i = 0; i < hbfontslen; i++) {
-+              hb_font_destroy(hbfontcache[i].font);
-+              XftUnlockFace(hbfontcache[i].match);
++      for (int i = 0; i < hbfontcache.capacity; i++) {
++              hb_font_destroy(hbfontcache.fonts[i].font);
++              XftUnlockFace(hbfontcache.fonts[i].match);
 +      }
 +
-+      if (hbfontcache != NULL) {
-+              free(hbfontcache);
-+              hbfontcache = NULL;
++      if (hbfontcache.fonts != NULL) {
++              free(hbfontcache.fonts);
++              hbfontcache.fonts = NULL;
 +      }
-+      hbfontslen = 0;
++      hbfontcache.capacity = 0;
 +}
 +
 +hb_font_t *
 +hbfindfont(XftFont *match)
 +{
-+      for (int i = 0; i < hbfontslen; i++) {
-+              if (hbfontcache[i].match == match)
-+                      return hbfontcache[i].font;
++      for (int i = 0; i < hbfontcache.capacity; i++) {
++              if (hbfontcache.fonts[i].match == match)
++                      return hbfontcache.fonts[i].font;
 +      }
 +
 +      /* Font not found in cache, caching it now. */
-+      hbfontcache = realloc(hbfontcache, sizeof(HbFontMatch) * (hbfontslen + 
1));
++      hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * 
(hbfontcache.capacity + 1));
 +      FT_Face face = XftLockFace(match);
 +      hb_font_t *font = hb_ft_font_create(face, NULL);
 +      if (font == NULL)
 +              die("Failed to load Harfbuzz font.");
 +
-+      hbfontcache[hbfontslen].match = match;
-+      hbfontcache[hbfontslen].font = font;
-+      hbfontslen += 1;
++      hbfontcache.fonts[hbfontcache.capacity].match = match;
++      hbfontcache.fonts[hbfontcache.capacity].font = font;
++      hbfontcache.capacity += 1;
 +
 +      return font;
 +}
 +
 +void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, 
int start, int length) {
-+      Rune rune;
 +      ushort mode = USHRT_MAX;
 +      unsigned int glyph_count;
-+      int i, end = start + length;
++      int rune_idx, glyph_idx, end = start + length;
 +
 +      hb_font_t *font = hbfindfont(xfont);
 +      if (font == NULL)
@@ -127,14 +138,20 @@ index 0000000..59b9200
 +      hb_buffer_t *buffer = hb_buffer_create();
 +      hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
 +
++      /* Resize the buffer if required length is larger. */
++      if (hbrunebuffer.capacity < length) {
++              hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * 
BUFFER_STEP;
++              hbrunebuffer.runes = realloc(hbrunebuffer.runes, 
hbrunebuffer.capacity);
++      }
++
 +      /* Fill buffer with codepoints. */
-+      for (i = start; i < end; i++) {
-+              rune = glyphs[i].u;
-+              mode = glyphs[i].mode;
++      for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, 
rune_idx++) {
++              hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u;
++              mode = glyphs[glyph_idx].mode;
 +              if (mode & ATTR_WDUMMY)
-+                      rune = 0x0020;
-+              hb_buffer_add_codepoints(buffer, &rune, 1, 0, 1);
++                      hbrunebuffer.runes[rune_idx] = 0x0020;
 +      }
++      hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, length);
 +
 +      /* Shape the segment. */
 +      hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t));
@@ -143,7 +160,7 @@ index 0000000..59b9200
 +      hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_count);
 +      hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, 
&glyph_count);
 +
-+      /** Fill the output. */
++      /* Fill the output. */
 +      data->buffer = buffer;
 +      data->glyphs = info;
 +      data->positions = pos;
@@ -216,7 +233,7 @@ index 6de960d..94679e4 100644
  void xfinishdraw(void);
  void xloadcols(void);
 diff --git a/x.c b/x.c
-index bf6bbf9..440bd2a 100644
+index bf6bbf9..929a59a 100644
 --- a/x.c
 +++ b/x.c
 @@ -19,6 +19,7 @@ char *argv0;
@@ -235,6 +252,15 @@ index bf6bbf9..440bd2a 100644
  static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, 
int);
  static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, 
int);
  static void xdrawglyph(Glyph, int, int);
+@@ -757,7 +759,7 @@ xresize(int col, int row)
+       xclear(0, 0, win.w, win.h);
+ 
+       /* resize to new width */
+-      xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
++      xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
+ }
+ 
+ ushort
 @@ -1062,6 +1064,9 @@ xunloadfont(Font *f)
  void
  xunloadfonts(void)
@@ -245,6 +271,15 @@ index bf6bbf9..440bd2a 100644
        /* Free the loaded fonts in the font cache.  */
        while (frclen > 0)
                XftFontClose(xw.dpy, frc[--frclen].font);
+@@ -1185,7 +1190,7 @@ xinit(int cols, int rows)
+       XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
+ 
+       /* font spec buffer */
+-      xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
++      xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
+ 
+       /* Xft rendering context */
+       xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
 @@ -1241,6 +1246,22 @@ xinit(int cols, int rows)
        boxdraw_xinit(xw.dpy, xw.cmap, xw.draw, xw.vis);
  }
@@ -252,7 +287,7 @@ index bf6bbf9..440bd2a 100644
 +void
 +xresetfontsettings(ushort mode, Font **font, int *frcflags)
 +{
-+  *font = &dc.font;
++      *font = &dc.font;
 +      if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
 +              *font = &dc.ibfont;
 +              *frcflags = FRC_ITALICBOLD;
@@ -268,12 +303,13 @@ index bf6bbf9..440bd2a 100644
  int
  xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, 
int x, int y)
  {
-@@ -1255,124 +1276,145 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const 
Glyph *glyphs, int len, int x
+@@ -1255,126 +1276,158 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const 
Glyph *glyphs, int len, int x
        FcPattern *fcpattern, *fontpattern;
        FcFontSet *fcsets[] = { NULL };
        FcCharSet *fccharset;
 -      int i, f, numspecs = 0;
 +      int i, f, length = 0, start = 0, numspecs = 0;
++      float cluster_xp = xp, cluster_yp = yp;
 +      HbTransformData shaped = { 0 };
 +
 +      /* Initial values. */
@@ -287,7 +323,7 @@ index bf6bbf9..440bd2a 100644
  
                /* Skip dummy wide-character spacing. */
 -              if (mode == ATTR_WDUMMY)
-+              if (mode & ATTR_WDUMMY)
++              if (mode & ATTR_WDUMMY && i < (len - 1))
                        continue;
  
 -              /* Determine font for glyph if different from previous glyph. */
@@ -350,31 +386,41 @@ index bf6bbf9..440bd2a 100644
 -                              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++) {
-+                              rune = glyphs[start + code_idx].u;
-+                              runewidth = win.cw * ((glyphs[start + 
code_idx].mode & ATTR_WIDE) ? 2.0f : 1.0f);
++                              int idx = shaped.glyphs[code_idx].cluster;
 +
-+                              if (glyphs[start + code_idx].mode & ATTR_WDUMMY)
++                              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 + code_idx].mode & 
ATTR_BOXDRAW) {
 +                                      /* minor shoehorning: boxdraw uses only 
this ushort */
 +                                      specs[numspecs].font = font->match;
 +                                      specs[numspecs].glyph = 
boxdrawindex(&glyphs[start + code_idx]);
 +                                      specs[numspecs].x = xp;
 +                                      specs[numspecs].y = yp;
-+                                      xp += runewidth;
 +                                      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 = xp + 
(short)shaped.positions[code_idx].x_offset;
-+                                      specs[numspecs].y = yp + 
(short)shaped.positions[code_idx].y_offset;
-+                                      xp += runewidth;
++                                      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. */
@@ -443,7 +489,6 @@ index bf6bbf9..440bd2a 100644
 +                                      specs[numspecs].glyph = glyphidx;
 +                                      specs[numspecs].x = (short)xp;
 +                                      specs[numspecs].y = (short)yp;
-+                                      xp += runewidth;
 +                                      numspecs++;
 +                              }
                        }
@@ -517,8 +562,11 @@ index bf6bbf9..440bd2a 100644
 -              numspecs++;
        }
  
++      hbcleanup(&shaped);
        return numspecs;
-@@ -1528,14 +1570,17 @@ xdrawglyph(Glyph g, int x, int y)
+ }
+ 
+@@ -1528,14 +1581,17 @@ xdrawglyph(Glyph g, int x, int y)
  }
  
  void
@@ -538,3 +586,39 @@ index bf6bbf9..440bd2a 100644
  
        if (IS_SET(MODE_HIDE))
                return;
+@@ -1663,18 +1719,16 @@ xdrawline(Line line, int x1, int y1, int x2)
+       Glyph base, new;
+       XftGlyphFontSpec *specs = xw.specbuf;
+ 
+-      numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
+       i = ox = 0;
+-      for (x = x1; x < x2 && i < numspecs; x++) {
++      for (x = x1; x < x2; x++) {
+               new = line[x];
+               if (new.mode == ATTR_WDUMMY)
+                       continue;
+               if (selected(x, y1))
+                       new.mode ^= ATTR_REVERSE;
+-              if (i > 0 && ATTRCMP(base, new)) {
+-                      xdrawglyphfontspecs(specs, base, i, ox, y1);
+-                      specs += i;
+-                      numspecs -= i;
++              if ((i > 0) && ATTRCMP(base, new)) {
++                      numspecs = xmakeglyphfontspecs(specs, &line[ox], x - 
ox, ox, y1);
++                      xdrawglyphfontspecs(specs, base, numspecs, ox, y1);
+                       i = 0;
+               }
+               if (i == 0) {
+@@ -1683,8 +1737,10 @@ xdrawline(Line line, int x1, int y1, int x2)
+               }
+               i++;
+       }
+-      if (i > 0)
+-              xdrawglyphfontspecs(specs, base, i, ox, y1);
++      if (i > 0) {
++              numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox, 
y1);
++              xdrawglyphfontspecs(specs, base, numspecs, ox, y1);
++      }
+ }
+ 
+ void
diff --git 
a/st.suckless.org/patches/ligatures/0.9/st-ligatures-scrollback-20221120-0.9.diff
 
b/st.suckless.org/patches/ligatures/0.9/st-ligatures-scrollback-20230105-0.9.diff
similarity index 75%
rename from 
st.suckless.org/patches/ligatures/0.9/st-ligatures-scrollback-20221120-0.9.diff
rename to 
st.suckless.org/patches/ligatures/0.9/st-ligatures-scrollback-20230105-0.9.diff
index 9a5686f8..9255c55a 100644
--- 
a/st.suckless.org/patches/ligatures/0.9/st-ligatures-scrollback-20221120-0.9.diff
+++ 
b/st.suckless.org/patches/ligatures/0.9/st-ligatures-scrollback-20230105-0.9.diff
@@ -42,10 +42,10 @@ index 1e306f8..3e13e53 100644
  STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
 diff --git a/hb.c b/hb.c
 new file mode 100644
-index 0000000..59b9200
+index 0000000..528c040
 --- /dev/null
 +++ b/hb.c
-@@ -0,0 +1,107 @@
+@@ -0,0 +1,124 @@
 +#include <stdlib.h>
 +#include <stdio.h>
 +#include <math.h>
@@ -58,6 +58,7 @@ index 0000000..59b9200
 +#include "hb.h"
 +
 +#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start 
= HB_FEATURE_GLOBAL_START, .end = HB_FEATURE_GLOBAL_END }
++#define BUFFER_STEP 256
 +
 +hb_font_t *hbfindfont(XftFont *match);
 +
@@ -66,8 +67,19 @@ index 0000000..59b9200
 +      hb_font_t *font;
 +} HbFontMatch;
 +
-+static int hbfontslen = 0;
-+static HbFontMatch *hbfontcache = NULL;
++typedef struct {
++      size_t capacity;
++      HbFontMatch *fonts;
++} HbFontCache;
++
++static HbFontCache hbfontcache = { 0, NULL };
++
++typedef struct {
++      size_t capacity;
++      Rune *runes;
++} RuneBuffer;
++
++static RuneBuffer hbrunebuffer = { 0, NULL };
 +
 +/*
 + * Poplulate the array with a list of font features, wrapped in FEATURE macro,
@@ -79,45 +91,44 @@ index 0000000..59b9200
 +void
 +hbunloadfonts()
 +{
-+      for (int i = 0; i < hbfontslen; i++) {
-+              hb_font_destroy(hbfontcache[i].font);
-+              XftUnlockFace(hbfontcache[i].match);
++      for (int i = 0; i < hbfontcache.capacity; i++) {
++              hb_font_destroy(hbfontcache.fonts[i].font);
++              XftUnlockFace(hbfontcache.fonts[i].match);
 +      }
 +
-+      if (hbfontcache != NULL) {
-+              free(hbfontcache);
-+              hbfontcache = NULL;
++      if (hbfontcache.fonts != NULL) {
++              free(hbfontcache.fonts);
++              hbfontcache.fonts = NULL;
 +      }
-+      hbfontslen = 0;
++      hbfontcache.capacity = 0;
 +}
 +
 +hb_font_t *
 +hbfindfont(XftFont *match)
 +{
-+      for (int i = 0; i < hbfontslen; i++) {
-+              if (hbfontcache[i].match == match)
-+                      return hbfontcache[i].font;
++      for (int i = 0; i < hbfontcache.capacity; i++) {
++              if (hbfontcache.fonts[i].match == match)
++                      return hbfontcache.fonts[i].font;
 +      }
 +
 +      /* Font not found in cache, caching it now. */
-+      hbfontcache = realloc(hbfontcache, sizeof(HbFontMatch) * (hbfontslen + 
1));
++      hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * 
(hbfontcache.capacity + 1));
 +      FT_Face face = XftLockFace(match);
 +      hb_font_t *font = hb_ft_font_create(face, NULL);
 +      if (font == NULL)
 +              die("Failed to load Harfbuzz font.");
 +
-+      hbfontcache[hbfontslen].match = match;
-+      hbfontcache[hbfontslen].font = font;
-+      hbfontslen += 1;
++      hbfontcache.fonts[hbfontcache.capacity].match = match;
++      hbfontcache.fonts[hbfontcache.capacity].font = font;
++      hbfontcache.capacity += 1;
 +
 +      return font;
 +}
 +
 +void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, 
int start, int length) {
-+      Rune rune;
 +      ushort mode = USHRT_MAX;
 +      unsigned int glyph_count;
-+      int i, end = start + length;
++      int rune_idx, glyph_idx, end = start + length;
 +
 +      hb_font_t *font = hbfindfont(xfont);
 +      if (font == NULL)
@@ -126,14 +137,20 @@ index 0000000..59b9200
 +      hb_buffer_t *buffer = hb_buffer_create();
 +      hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
 +
++      /* Resize the buffer if required length is larger. */
++      if (hbrunebuffer.capacity < length) {
++              hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * 
BUFFER_STEP;
++              hbrunebuffer.runes = realloc(hbrunebuffer.runes, 
hbrunebuffer.capacity);
++      }
++
 +      /* Fill buffer with codepoints. */
-+      for (i = start; i < end; i++) {
-+              rune = glyphs[i].u;
-+              mode = glyphs[i].mode;
++      for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, 
rune_idx++) {
++              hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u;
++              mode = glyphs[glyph_idx].mode;
 +              if (mode & ATTR_WDUMMY)
-+                      rune = 0x0020;
-+              hb_buffer_add_codepoints(buffer, &rune, 1, 0, 1);
++                      hbrunebuffer.runes[rune_idx] = 0x0020;
 +      }
++      hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, length);
 +
 +      /* Shape the segment. */
 +      hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t));
@@ -142,7 +159,7 @@ index 0000000..59b9200
 +      hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_count);
 +      hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, 
&glyph_count);
 +
-+      /** Fill the output. */
++      /* Fill the output. */
 +      data->buffer = buffer;
 +      data->glyphs = info;
 +      data->positions = pos;
@@ -215,7 +232,7 @@ index 6de960d..94679e4 100644
  void xfinishdraw(void);
  void xloadcols(void);
 diff --git a/x.c b/x.c
-index 2a3bd38..5feac09 100644
+index 2a3bd38..e66cf0c 100644
 --- a/x.c
 +++ b/x.c
 @@ -19,6 +19,7 @@ char *argv0;
@@ -234,6 +251,15 @@ index 2a3bd38..5feac09 100644
  static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, 
int);
  static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, 
int);
  static void xdrawglyph(Glyph, int, int);
+@@ -757,7 +759,7 @@ xresize(int col, int row)
+       xclear(0, 0, win.w, win.h);
+ 
+       /* resize to new width */
+-      xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
++      xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
+ }
+ 
+ ushort
 @@ -1062,6 +1064,9 @@ xunloadfont(Font *f)
  void
  xunloadfonts(void)
@@ -244,6 +270,15 @@ index 2a3bd38..5feac09 100644
        /* Free the loaded fonts in the font cache.  */
        while (frclen > 0)
                XftFontClose(xw.dpy, frc[--frclen].font);
+@@ -1185,7 +1190,7 @@ xinit(int cols, int rows)
+       XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
+ 
+       /* font spec buffer */
+-      xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
++      xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
+ 
+       /* Xft rendering context */
+       xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
 @@ -1239,6 +1244,22 @@ xinit(int cols, int rows)
                xsel.xtarget = XA_STRING;
  }
@@ -251,7 +286,7 @@ index 2a3bd38..5feac09 100644
 +void
 +xresetfontsettings(ushort mode, Font **font, int *frcflags)
 +{
-+  *font = &dc.font;
++      *font = &dc.font;
 +      if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
 +              *font = &dc.ibfont;
 +              *frcflags = FRC_ITALICBOLD;
@@ -267,12 +302,13 @@ index 2a3bd38..5feac09 100644
  int
  xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, 
int x, int y)
  {
-@@ -1253,119 +1274,137 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const 
Glyph *glyphs, int len, int x
+@@ -1253,121 +1274,151 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const 
Glyph *glyphs, int len, int x
        FcPattern *fcpattern, *fontpattern;
        FcFontSet *fcsets[] = { NULL };
        FcCharSet *fccharset;
 -      int i, f, numspecs = 0;
 +      int i, f, length = 0, start = 0, numspecs = 0;
++      float cluster_xp = xp, cluster_yp = yp;
 +      HbTransformData shaped = { 0 };
 +
 +      /* Initial values. */
@@ -286,7 +322,7 @@ index 2a3bd38..5feac09 100644
  
                /* Skip dummy wide-character spacing. */
 -              if (mode == ATTR_WDUMMY)
-+              if (mode & ATTR_WDUMMY)
++              if (mode & ATTR_WDUMMY && i < (len - 1))
                        continue;
  
 -              /* Determine font for glyph if different from previous glyph. */
@@ -344,23 +380,34 @@ index 2a3bd38..5feac09 100644
 -                              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++) {
-+                              rune = glyphs[start + code_idx].u;
-+                              runewidth = win.cw * ((glyphs[start + 
code_idx].mode & ATTR_WIDE) ? 2.0f : 1.0f);
++                              int idx = shaped.glyphs[code_idx].cluster;
 +
-+                              if (glyphs[start + code_idx].mode & ATTR_WDUMMY)
++                              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 = xp + 
(short)shaped.positions[code_idx].x_offset;
-+                                      specs[numspecs].y = yp + 
(short)shaped.positions[code_idx].y_offset;
-+                                      xp += runewidth;
++                                      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. */
@@ -429,21 +476,17 @@ index 2a3bd38..5feac09 100644
 +                                      specs[numspecs].glyph = glyphidx;
 +                                      specs[numspecs].x = (short)xp;
 +                                      specs[numspecs].y = (short)yp;
-+                                      xp += runewidth;
 +                                      numspecs++;
 +                              }
                        }
 -              }
- 
+-
 -              /* Nothing was found. Use fontconfig to find matching font. */
 -              if (f >= frclen) {
 -                      if (!font->set)
 -                              font->set = FcFontSort(0, font->pattern,
 -                                                     1, 0, &fcres);
 -                      fcsets[0] = font->set;
-+                      /* Cleanup and get ready for next segment. */
-+                      hbcleanup(&shaped);
-+                      start = i;
  
 -                      /*
 -                       * Nothing was found in the cache. Now use
@@ -459,7 +502,10 @@ index 2a3bd38..5feac09 100644
 -                      FcPatternAddCharSet(fcpattern, FC_CHARSET,
 -                                      fccharset);
 -                      FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
--
++                      /* Cleanup and get ready for next segment. */
++                      hbcleanup(&shaped);
++                      start = i;
+ 
 -                      FcConfigSubstitute(0, fcpattern,
 -                                      FcMatchPattern);
 -                      FcDefaultSubstitute(fcpattern);
@@ -503,8 +549,11 @@ index 2a3bd38..5feac09 100644
 -              numspecs++;
        }
  
++      hbcleanup(&shaped);
        return numspecs;
-@@ -1517,14 +1556,17 @@ xdrawglyph(Glyph g, int x, int y)
+ }
+ 
+@@ -1517,14 +1568,17 @@ xdrawglyph(Glyph g, int x, int y)
  }
  
  void
@@ -524,3 +573,39 @@ index 2a3bd38..5feac09 100644
  
        if (IS_SET(MODE_HIDE))
                return;
+@@ -1652,18 +1706,16 @@ xdrawline(Line line, int x1, int y1, int x2)
+       Glyph base, new;
+       XftGlyphFontSpec *specs = xw.specbuf;
+ 
+-      numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
+       i = ox = 0;
+-      for (x = x1; x < x2 && i < numspecs; x++) {
++      for (x = x1; x < x2; x++) {
+               new = line[x];
+               if (new.mode == ATTR_WDUMMY)
+                       continue;
+               if (selected(x, y1))
+                       new.mode ^= ATTR_REVERSE;
+-              if (i > 0 && ATTRCMP(base, new)) {
+-                      xdrawglyphfontspecs(specs, base, i, ox, y1);
+-                      specs += i;
+-                      numspecs -= i;
++              if ((i > 0) && ATTRCMP(base, new)) {
++                      numspecs = xmakeglyphfontspecs(specs, &line[ox], x - 
ox, ox, y1);
++                      xdrawglyphfontspecs(specs, base, numspecs, ox, y1);
+                       i = 0;
+               }
+               if (i == 0) {
+@@ -1672,8 +1724,10 @@ xdrawline(Line line, int x1, int y1, int x2)
+               }
+               i++;
+       }
+-      if (i > 0)
+-              xdrawglyphfontspecs(specs, base, i, ox, y1);
++      if (i > 0) {
++              numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox, 
y1);
++              xdrawglyphfontspecs(specs, base, numspecs, ox, y1);
++      }
+ }
+ 
+ void
diff --git 
a/st.suckless.org/patches/ligatures/0.9/st-ligatures-scrollback-ringbuffer-20221120-0.9.diff
 
b/st.suckless.org/patches/ligatures/0.9/st-ligatures-scrollback-ringbuffer-20230105-0.9.diff
similarity index 74%
rename from 
st.suckless.org/patches/ligatures/0.9/st-ligatures-scrollback-ringbuffer-20221120-0.9.diff
rename to 
st.suckless.org/patches/ligatures/0.9/st-ligatures-scrollback-ringbuffer-20230105-0.9.diff
index 294f511d..69652198 100644
--- 
a/st.suckless.org/patches/ligatures/0.9/st-ligatures-scrollback-ringbuffer-20221120-0.9.diff
+++ 
b/st.suckless.org/patches/ligatures/0.9/st-ligatures-scrollback-ringbuffer-20230105-0.9.diff
@@ -42,10 +42,10 @@ index 1e306f8..3e13e53 100644
  STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600
 diff --git a/hb.c b/hb.c
 new file mode 100644
-index 0000000..59b9200
+index 0000000..528c040
 --- /dev/null
 +++ b/hb.c
-@@ -0,0 +1,107 @@
+@@ -0,0 +1,124 @@
 +#include <stdlib.h>
 +#include <stdio.h>
 +#include <math.h>
@@ -58,6 +58,7 @@ index 0000000..59b9200
 +#include "hb.h"
 +
 +#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start 
= HB_FEATURE_GLOBAL_START, .end = HB_FEATURE_GLOBAL_END }
++#define BUFFER_STEP 256
 +
 +hb_font_t *hbfindfont(XftFont *match);
 +
@@ -66,8 +67,19 @@ index 0000000..59b9200
 +      hb_font_t *font;
 +} HbFontMatch;
 +
-+static int hbfontslen = 0;
-+static HbFontMatch *hbfontcache = NULL;
++typedef struct {
++      size_t capacity;
++      HbFontMatch *fonts;
++} HbFontCache;
++
++static HbFontCache hbfontcache = { 0, NULL };
++
++typedef struct {
++      size_t capacity;
++      Rune *runes;
++} RuneBuffer;
++
++static RuneBuffer hbrunebuffer = { 0, NULL };
 +
 +/*
 + * Poplulate the array with a list of font features, wrapped in FEATURE macro,
@@ -79,45 +91,44 @@ index 0000000..59b9200
 +void
 +hbunloadfonts()
 +{
-+      for (int i = 0; i < hbfontslen; i++) {
-+              hb_font_destroy(hbfontcache[i].font);
-+              XftUnlockFace(hbfontcache[i].match);
++      for (int i = 0; i < hbfontcache.capacity; i++) {
++              hb_font_destroy(hbfontcache.fonts[i].font);
++              XftUnlockFace(hbfontcache.fonts[i].match);
 +      }
 +
-+      if (hbfontcache != NULL) {
-+              free(hbfontcache);
-+              hbfontcache = NULL;
++      if (hbfontcache.fonts != NULL) {
++              free(hbfontcache.fonts);
++              hbfontcache.fonts = NULL;
 +      }
-+      hbfontslen = 0;
++      hbfontcache.capacity = 0;
 +}
 +
 +hb_font_t *
 +hbfindfont(XftFont *match)
 +{
-+      for (int i = 0; i < hbfontslen; i++) {
-+              if (hbfontcache[i].match == match)
-+                      return hbfontcache[i].font;
++      for (int i = 0; i < hbfontcache.capacity; i++) {
++              if (hbfontcache.fonts[i].match == match)
++                      return hbfontcache.fonts[i].font;
 +      }
 +
 +      /* Font not found in cache, caching it now. */
-+      hbfontcache = realloc(hbfontcache, sizeof(HbFontMatch) * (hbfontslen + 
1));
++      hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * 
(hbfontcache.capacity + 1));
 +      FT_Face face = XftLockFace(match);
 +      hb_font_t *font = hb_ft_font_create(face, NULL);
 +      if (font == NULL)
 +              die("Failed to load Harfbuzz font.");
 +
-+      hbfontcache[hbfontslen].match = match;
-+      hbfontcache[hbfontslen].font = font;
-+      hbfontslen += 1;
++      hbfontcache.fonts[hbfontcache.capacity].match = match;
++      hbfontcache.fonts[hbfontcache.capacity].font = font;
++      hbfontcache.capacity += 1;
 +
 +      return font;
 +}
 +
 +void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, 
int start, int length) {
-+      Rune rune;
 +      ushort mode = USHRT_MAX;
 +      unsigned int glyph_count;
-+      int i, end = start + length;
++      int rune_idx, glyph_idx, end = start + length;
 +
 +      hb_font_t *font = hbfindfont(xfont);
 +      if (font == NULL)
@@ -126,14 +137,20 @@ index 0000000..59b9200
 +      hb_buffer_t *buffer = hb_buffer_create();
 +      hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
 +
++      /* Resize the buffer if required length is larger. */
++      if (hbrunebuffer.capacity < length) {
++              hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * 
BUFFER_STEP;
++              hbrunebuffer.runes = realloc(hbrunebuffer.runes, 
hbrunebuffer.capacity);
++      }
++
 +      /* Fill buffer with codepoints. */
-+      for (i = start; i < end; i++) {
-+              rune = glyphs[i].u;
-+              mode = glyphs[i].mode;
++      for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, 
rune_idx++) {
++              hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u;
++              mode = glyphs[glyph_idx].mode;
 +              if (mode & ATTR_WDUMMY)
-+                      rune = 0x0020;
-+              hb_buffer_add_codepoints(buffer, &rune, 1, 0, 1);
++                      hbrunebuffer.runes[rune_idx] = 0x0020;
 +      }
++      hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, length);
 +
 +      /* Shape the segment. */
 +      hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t));
@@ -142,7 +159,7 @@ index 0000000..59b9200
 +      hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_count);
 +      hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, 
&glyph_count);
 +
-+      /** Fill the output. */
++      /* Fill the output. */
 +      data->buffer = buffer;
 +      data->glyphs = info;
 +      data->positions = pos;
@@ -174,10 +191,10 @@ index 0000000..88de9bd
 +void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
 +void hbcleanup(HbTransformData *);
 diff --git a/st.c b/st.c
-index 79ee9ba..7675db6 100644
+index c44797b..91f54dc 100644
 --- a/st.c
 +++ b/st.c
-@@ -2762,7 +2762,8 @@ draw(void)
+@@ -2759,7 +2759,8 @@ draw(void)
        drawregion(0, 0, term.col, term.row);
        if (TSCREEN.off == 0)
                xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx],
@@ -188,7 +205,7 @@ index 79ee9ba..7675db6 100644
        term.ocy = term.c.y;
        xfinishdraw();
 diff --git a/st.h b/st.h
-index 818a6f8..4e584b6 100644
+index 3cea73b..709a369 100644
 --- a/st.h
 +++ b/st.h
 @@ -11,7 +11,8 @@
@@ -215,7 +232,7 @@ index 6de960d..94679e4 100644
  void xfinishdraw(void);
  void xloadcols(void);
 diff --git a/x.c b/x.c
-index 2a3bd38..5feac09 100644
+index 9891e91..ec3567a 100644
 --- a/x.c
 +++ b/x.c
 @@ -19,6 +19,7 @@ char *argv0;
@@ -226,7 +243,7 @@ index 2a3bd38..5feac09 100644
  
  /* types used in config.h */
  typedef struct {
-@@ -141,6 +142,7 @@ typedef struct {
+@@ -143,6 +144,7 @@ typedef struct {
  } DC;
  
  static inline ushort sixd_to_16bit(int);
@@ -234,7 +251,16 @@ index 2a3bd38..5feac09 100644
  static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, 
int);
  static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, 
int);
  static void xdrawglyph(Glyph, int, int);
-@@ -1062,6 +1064,9 @@ xunloadfont(Font *f)
+@@ -759,7 +761,7 @@ xresize(int col, int row)
+       xclear(0, 0, win.w, win.h);
+ 
+       /* resize to new width */
+-      xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec));
++      xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4);
+ }
+ 
+ ushort
+@@ -1064,6 +1066,9 @@ xunloadfont(Font *f)
  void
  xunloadfonts(void)
  {
@@ -244,14 +270,23 @@ index 2a3bd38..5feac09 100644
        /* Free the loaded fonts in the font cache.  */
        while (frclen > 0)
                XftFontClose(xw.dpy, frc[--frclen].font);
-@@ -1239,6 +1244,22 @@ xinit(int cols, int rows)
+@@ -1187,7 +1192,7 @@ xinit(int cols, int rows)
+       XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h);
+ 
+       /* font spec buffer */
+-      xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec));
++      xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4);
+ 
+       /* Xft rendering context */
+       xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap);
+@@ -1241,6 +1246,22 @@ xinit(int cols, int rows)
                xsel.xtarget = XA_STRING;
  }
  
 +void
 +xresetfontsettings(ushort mode, Font **font, int *frcflags)
 +{
-+  *font = &dc.font;
++      *font = &dc.font;
 +      if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
 +              *font = &dc.ibfont;
 +              *frcflags = FRC_ITALICBOLD;
@@ -267,12 +302,13 @@ index 2a3bd38..5feac09 100644
  int
  xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, 
int x, int y)
  {
-@@ -1253,119 +1274,137 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const 
Glyph *glyphs, int len, int x
+@@ -1255,119 +1276,148 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const 
Glyph *glyphs, int len, int x
        FcPattern *fcpattern, *fontpattern;
        FcFontSet *fcsets[] = { NULL };
        FcCharSet *fccharset;
 -      int i, f, numspecs = 0;
 +      int i, f, length = 0, start = 0, numspecs = 0;
++  float cluster_xp = xp, cluster_yp = yp;
 +      HbTransformData shaped = { 0 };
 +
 +      /* Initial values. */
@@ -286,7 +322,7 @@ index 2a3bd38..5feac09 100644
  
                /* Skip dummy wide-character spacing. */
 -              if (mode == ATTR_WDUMMY)
-+              if (mode & ATTR_WDUMMY)
++              if (mode & ATTR_WDUMMY && i < (len - 1))
                        continue;
  
 -              /* Determine font for glyph if different from previous glyph. */
@@ -344,23 +380,34 @@ index 2a3bd38..5feac09 100644
 -                              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++) {
-+                              rune = glyphs[start + code_idx].u;
-+                              runewidth = win.cw * ((glyphs[start + 
code_idx].mode & ATTR_WIDE) ? 2.0f : 1.0f);
++                              int idx = shaped.glyphs[code_idx].cluster;
 +
-+                              if (glyphs[start + code_idx].mode & ATTR_WDUMMY)
++                              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 = xp + 
(short)shaped.positions[code_idx].x_offset;
-+                                      specs[numspecs].y = yp + 
(short)shaped.positions[code_idx].y_offset;
-+                                      xp += runewidth;
++                                      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. */
@@ -429,21 +476,17 @@ index 2a3bd38..5feac09 100644
 +                                      specs[numspecs].glyph = glyphidx;
 +                                      specs[numspecs].x = (short)xp;
 +                                      specs[numspecs].y = (short)yp;
-+                                      xp += runewidth;
 +                                      numspecs++;
 +                              }
                        }
 -              }
- 
+-
 -              /* Nothing was found. Use fontconfig to find matching font. */
 -              if (f >= frclen) {
 -                      if (!font->set)
 -                              font->set = FcFontSort(0, font->pattern,
 -                                                     1, 0, &fcres);
 -                      fcsets[0] = font->set;
-+                      /* Cleanup and get ready for next segment. */
-+                      hbcleanup(&shaped);
-+                      start = i;
  
 -                      /*
 -                       * Nothing was found in the cache. Now use
@@ -459,7 +502,10 @@ index 2a3bd38..5feac09 100644
 -                      FcPatternAddCharSet(fcpattern, FC_CHARSET,
 -                                      fccharset);
 -                      FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
--
++                      /* Cleanup and get ready for next segment. */
++                      hbcleanup(&shaped);
++                      start = i;
+ 
 -                      FcConfigSubstitute(0, fcpattern,
 -                                      FcMatchPattern);
 -                      FcDefaultSubstitute(fcpattern);
@@ -504,7 +550,7 @@ index 2a3bd38..5feac09 100644
        }
  
        return numspecs;
-@@ -1517,14 +1556,17 @@ xdrawglyph(Glyph g, int x, int y)
+@@ -1519,14 +1569,17 @@ xdrawglyph(Glyph g, int x, int y)
  }
  
  void
@@ -524,3 +570,39 @@ index 2a3bd38..5feac09 100644
  
        if (IS_SET(MODE_HIDE))
                return;
+@@ -1654,18 +1707,16 @@ xdrawline(Line line, int x1, int y1, int x2)
+       Glyph base, new;
+       XftGlyphFontSpec *specs = xw.specbuf;
+ 
+-      numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
+       i = ox = 0;
+-      for (x = x1; x < x2 && i < numspecs; x++) {
++      for (x = x1; x < x2; x++) {
+               new = line[x];
+               if (new.mode == ATTR_WDUMMY)
+                       continue;
+               if (selected(x, y1))
+                       new.mode ^= ATTR_REVERSE;
+-              if (i > 0 && ATTRCMP(base, new)) {
+-                      xdrawglyphfontspecs(specs, base, i, ox, y1);
+-                      specs += i;
+-                      numspecs -= i;
++              if ((i > 0) && ATTRCMP(base, new)) {
++                      numspecs = xmakeglyphfontspecs(specs, &line[ox], x - 
ox, ox, y1);
++                      xdrawglyphfontspecs(specs, base, numspecs, ox, y1);
+                       i = 0;
+               }
+               if (i == 0) {
+@@ -1674,8 +1725,10 @@ xdrawline(Line line, int x1, int y1, int x2)
+               }
+               i++;
+       }
+-      if (i > 0)
+-              xdrawglyphfontspecs(specs, base, i, ox, y1);
++      if (i > 0) {
++              numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox, 
y1);
++              xdrawglyphfontspecs(specs, base, numspecs, ox, y1);
++      }
+ }
+ 
+ void


Reply via email to