commit 6b3ede4c97b1c506b6738d5919f62431b1011035
parent 427d6b1820dd093ec55dfe8427dc01b6c83d0759
Author: Alexander Rogachev <sorryforbadname@gmail.com>
Date: Wed, 16 Sep 2026 21:38:51 +0400
[st][patches][ligatures] Fix vanilla 0.9.3 patch: add hb.* files.
Diffstat:
1 file changed, 151 insertions(+), 0 deletions(-)
diff --git a/st.suckless.org/patches/ligatures/0.9.3/st-ligatures-20251007-0.9.3.diff b/st.suckless.org/patches/ligatures/0.9.3/st-ligatures-20251007-0.9.3.diff
@@ -50,6 +50,157 @@ index 2fc854e..ec69d1a 100644
#MANPREFIX = ${PREFIX}/man
# compiler and linker
+diff --git a/hb.c b/hb.c
+new file mode 100644
+index 0000000..99412c8
+--- /dev/null
++++ b/hb.c
+@@ -0,0 +1,125 @@
++#include <stdlib.h>
++#include <stdio.h>
++#include <math.h>
++#include <X11/Xft/Xft.h>
++#include <X11/cursorfont.h>
++#include <hb.h>
++#include <hb-ft.h>
++
++#include "st.h"
++#include "hb.h"
++
++#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start = HB_FEATURE_GLOBAL_START, .end = HB_FEATURE_GLOBAL_END }
++#define BUFFER_STEP 256
++
++hb_font_t *hbfindfont(XftFont *match);
++
++typedef struct {
++ XftFont *match;
++ hb_font_t *font;
++} HbFontMatch;
++
++typedef struct {
++ size_t capacity;
++ HbFontMatch *fonts;
++} HbFontCache;
++
++static HbFontCache hbfontcache = { 0, NULL };
++
++typedef struct {
++ size_t capacity;
++ Rune *runes;
++} RuneBuffer;
++
++static RuneBuffer hbrunebuffer = { 0, NULL };
++
++/*
++ * Poplulate the array with a list of font features, wrapped in FEATURE macro,
++ * e. g.
++ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g')
++ */
++hb_feature_t features[] = { };
++
++void
++hbunloadfonts()
++{
++ for (int i = 0; i < hbfontcache.capacity; i++) {
++ hb_font_destroy(hbfontcache.fonts[i].font);
++ XftUnlockFace(hbfontcache.fonts[i].match);
++ }
++
++ if (hbfontcache.fonts != NULL) {
++ free(hbfontcache.fonts);
++ hbfontcache.fonts = NULL;
++ }
++ hbfontcache.capacity = 0;
++}
++
++hb_font_t *
++hbfindfont(XftFont *match)
++{
++ for (int i = 0; i < hbfontcache.capacity; i++) {
++ if (hbfontcache.fonts[i].match == match)
++ return hbfontcache.fonts[i].font;
++ }
++
++ /* Font not found in cache, caching it now. */
++ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * (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.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) {
++ ushort mode = USHRT_MAX;
++ unsigned int glyph_count;
++ int rune_idx, glyph_idx, end = start + length;
++
++ hb_font_t *font = hbfindfont(xfont);
++ if (font == NULL)
++ return;
++
++ hb_buffer_t *buffer = hb_buffer_create();
++ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
++ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS);
++
++ /* 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 * sizeof(Rune));
++ }
++
++ /* Fill buffer with codepoints. */
++ 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)
++ 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));
++
++ /* Get new glyph info. */
++ 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. */
++ data->buffer = buffer;
++ data->glyphs = info;
++ data->positions = pos;
++ data->count = glyph_count;
++}
++
++void hbcleanup(HbTransformData *data) {
++ hb_buffer_destroy(data->buffer);
++ memset(data, 0, sizeof(HbTransformData));
++}
+diff --git a/hb.h b/hb.h
+new file mode 100644
+index 0000000..3b0ef44
+--- /dev/null
++++ b/hb.h
+@@ -0,0 +1,14 @@
++#include <X11/Xft/Xft.h>
++#include <hb.h>
++#include <hb-ft.h>
++
++typedef struct {
++ hb_buffer_t *buffer;
++ hb_glyph_info_t *glyphs;
++ hb_glyph_position_t *positions;
++ unsigned int count;
++} HbTransformData;
++
++void hbunloadfonts();
++void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int);
++void hbcleanup(HbTransformData *);
diff --git a/st.c b/st.c
index 8e57991..9641c85 100644
--- a/st.c