[st][patches][ligatures] - sites - public wiki contents of suckless.org | |
git clone git://git.suckless.org/sites | |
Log | |
Files | |
Refs | |
--- | |
commit 77456c4698f33511b0652c9dfd6f2904b4797584 | |
parent b034a2ce4801188b3af190036eb66a13e24cbffc | |
Author: Alexander Rogachev <[email protected]> | |
Date: Fri, 10 Jan 2025 01:55:48 +0400 | |
[st][patches][ligatures] | |
Some code cleanup and refactoring. | |
Diffstat: | |
D st.suckless.org/patches/ligatures/… | 633 ---------------------------… | |
A st.suckless.org/patches/ligatures/… | 650 +++++++++++++++++++++++++++… | |
D st.suckless.org/patches/ligatures/… | 635 ---------------------------… | |
A st.suckless.org/patches/ligatures/… | 650 +++++++++++++++++++++++++++… | |
D st.suckless.org/patches/ligatures/… | 635 ---------------------------… | |
A st.suckless.org/patches/ligatures/… | 650 +++++++++++++++++++++++++++… | |
D st.suckless.org/patches/ligatures/… | 635 ---------------------------… | |
A st.suckless.org/patches/ligatures/… | 650 +++++++++++++++++++++++++++… | |
D st.suckless.org/patches/ligatures/… | 647 ---------------------------… | |
A st.suckless.org/patches/ligatures/… | 662 +++++++++++++++++++++++++++… | |
D st.suckless.org/patches/ligatures/… | 634 ---------------------------… | |
A st.suckless.org/patches/ligatures/… | 650 +++++++++++++++++++++++++++… | |
D st.suckless.org/patches/ligatures/… | 633 ---------------------------… | |
A st.suckless.org/patches/ligatures/… | 650 +++++++++++++++++++++++++++… | |
M st.suckless.org/patches/ligatures/… | 14 +++++++------- | |
15 files changed, 4569 insertions(+), 4459 deletions(-) | |
--- | |
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-20240427-0.9.… | |
@@ -1,633 +0,0 @@ | |
-diff --git a/Makefile b/Makefile | |
-index 470ac86..38240da 100644 | |
---- a/Makefile | |
-+++ b/Makefile | |
-@@ -4,7 +4,7 @@ | |
- | |
- include config.mk | |
- | |
--SRC = st.c x.c | |
-+SRC = st.c x.c hb.c | |
- OBJ = $(SRC:.c=.o) | |
- | |
- all: st | |
-@@ -22,7 +22,8 @@ config.h: | |
- $(CC) $(STCFLAGS) -c $< | |
- | |
- st.o: config.h st.h win.h | |
--x.o: arg.h config.h st.h win.h | |
-+x.o: arg.h config.h st.h win.h hb.h | |
-+hb.o: st.h | |
- | |
- $(OBJ): config.h config.mk | |
- | |
-diff --git a/config.mk b/config.mk | |
-index 1e306f8..3e13e53 100644 | |
---- a/config.mk | |
-+++ b/config.mk | |
-@@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config | |
- # includes and libs | |
- INCS = -I$(X11INC) \ | |
- `$(PKG_CONFIG) --cflags fontconfig` \ | |
-- `$(PKG_CONFIG) --cflags freetype2` | |
-+ `$(PKG_CONFIG) --cflags freetype2` \ | |
-+ `$(PKG_CONFIG) --cflags harfbuzz` | |
- LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ | |
- `$(PKG_CONFIG) --libs fontconfig` \ | |
-- `$(PKG_CONFIG) --libs freetype2` | |
-+ `$(PKG_CONFIG) --libs freetype2` \ | |
-+ `$(PKG_CONFIG) --libs harfbuzz` | |
- | |
- # flags | |
- STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 | |
-diff --git a/hb.c b/hb.c | |
-new file mode 100644 | |
-index 0000000..99412c8 | |
---- /dev/null | |
-+++ b/hb.c | |
-@@ -0,0 +1,125 @@ | |
-+#include <stdlib.h> | |
-+#include <stdio.h> | |
-+#include <math.h> | |
-+#include <X11/Xft/Xft.h> | |
-+#include <X11/cursorfont.h> | |
-+#include <hb.h> | |
-+#include <hb-ft.h> | |
-+ | |
-+#include "st.h" | |
-+#include "hb.h" | |
-+ | |
-+#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start… | |
-+#define BUFFER_STEP 256 | |
-+ | |
-+hb_font_t *hbfindfont(XftFont *match); | |
-+ | |
-+typedef struct { | |
-+ XftFont *match; | |
-+ hb_font_t *font; | |
-+} HbFontMatch; | |
-+ | |
-+typedef struct { | |
-+ size_t capacity; | |
-+ HbFontMatch *fonts; | |
-+} HbFontCache; | |
-+ | |
-+static HbFontCache hbfontcache = { 0, NULL }; | |
-+ | |
-+typedef struct { | |
-+ size_t capacity; | |
-+ Rune *runes; | |
-+} RuneBuffer; | |
-+ | |
-+static RuneBuffer hbrunebuffer = { 0, NULL }; | |
-+ | |
-+/* | |
-+ * Poplulate the array with a list of font features, wrapped in FEATURE macro, | |
-+ * e. g. | |
-+ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') | |
-+ */ | |
-+hb_feature_t features[] = { }; | |
-+ | |
-+void | |
-+hbunloadfonts() | |
-+{ | |
-+ for (int i = 0; i < hbfontcache.capacity; i++) { | |
-+ hb_font_destroy(hbfontcache.fonts[i].font); | |
-+ XftUnlockFace(hbfontcache.fonts[i].match); | |
-+ } | |
-+ | |
-+ if (hbfontcache.fonts != NULL) { | |
-+ free(hbfontcache.fonts); | |
-+ hbfontcache.fonts = NULL; | |
-+ } | |
-+ hbfontcache.capacity = 0; | |
-+} | |
-+ | |
-+hb_font_t * | |
-+hbfindfont(XftFont *match) | |
-+{ | |
-+ for (int i = 0; i < hbfontcache.capacity; i++) { | |
-+ if (hbfontcache.fonts[i].match == match) | |
-+ return hbfontcache.fonts[i].font; | |
-+ } | |
-+ | |
-+ /* Font not found in cache, caching it now. */ | |
-+ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * … | |
-+ FT_Face face = XftLockFace(match); | |
-+ hb_font_t *font = hb_ft_font_create(face, NULL); | |
-+ if (font == NULL) | |
-+ die("Failed to load Harfbuzz font."); | |
-+ | |
-+ hbfontcache.fonts[hbfontcache.capacity].match = match; | |
-+ hbfontcache.fonts[hbfontcache.capacity].font = font; | |
-+ hbfontcache.capacity += 1; | |
-+ | |
-+ return font; | |
-+} | |
-+ | |
-+void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, … | |
-+ ushort mode = USHRT_MAX; | |
-+ unsigned int glyph_count; | |
-+ int rune_idx, glyph_idx, end = start + length; | |
-+ | |
-+ hb_font_t *font = hbfindfont(xfont); | |
-+ if (font == NULL) | |
-+ return; | |
-+ | |
-+ hb_buffer_t *buffer = hb_buffer_create(); | |
-+ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); | |
-+ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_… | |
-+ | |
-+ /* Resize the buffer if required length is larger. */ | |
-+ if (hbrunebuffer.capacity < length) { | |
-+ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S… | |
-+ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer… | |
-+ } | |
-+ | |
-+ /* Fill buffer with codepoints. */ | |
-+ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r… | |
-+ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u; | |
-+ mode = glyphs[glyph_idx].mode; | |
-+ if (mode & ATTR_WDUMMY) | |
-+ hbrunebuffer.runes[rune_idx] = 0x0020; | |
-+ } | |
-+ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt… | |
-+ | |
-+ /* Shape the segment. */ | |
-+ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t… | |
-+ | |
-+ /* Get new glyph info. */ | |
-+ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun… | |
-+ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly… | |
-+ | |
-+ /* Fill the output. */ | |
-+ data->buffer = buffer; | |
-+ data->glyphs = info; | |
-+ data->positions = pos; | |
-+ data->count = glyph_count; | |
-+} | |
-+ | |
-+void hbcleanup(HbTransformData *data) { | |
-+ hb_buffer_destroy(data->buffer); | |
-+ memset(data, 0, sizeof(HbTransformData)); | |
-+} | |
-diff --git a/hb.h b/hb.h | |
-new file mode 100644 | |
-index 0000000..3b0ef44 | |
---- /dev/null | |
-+++ b/hb.h | |
-@@ -0,0 +1,14 @@ | |
-+#include <X11/Xft/Xft.h> | |
-+#include <hb.h> | |
-+#include <hb-ft.h> | |
-+ | |
-+typedef struct { | |
-+ hb_buffer_t *buffer; | |
-+ hb_glyph_info_t *glyphs; | |
-+ hb_glyph_position_t *positions; | |
-+ unsigned int count; | |
-+} HbTransformData; | |
-+ | |
-+void hbunloadfonts(); | |
-+void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int); | |
-+void hbcleanup(HbTransformData *); | |
-diff --git a/st.c b/st.c | |
-index 62def59..041c6d8 100644 | |
---- a/st.c | |
-+++ b/st.c | |
-@@ -2640,7 +2640,8 @@ draw(void) | |
- | |
- drawregion(0, 0, term.col, term.row); | |
- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], | |
-- term.ocx, term.ocy, term.line[term.ocy][term.ocx]); | |
-+ term.ocx, term.ocy, term.line[term.ocy][term.ocx], | |
-+ term.line[term.ocy], term.col); | |
- term.ocx = cx; | |
- term.ocy = term.c.y; | |
- xfinishdraw(); | |
-diff --git a/st.h b/st.h | |
-index fd3b0d8..142fdfe 100644 | |
---- a/st.h | |
-+++ b/st.h | |
-@@ -11,7 +11,8 @@ | |
- #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) | |
- #define DEFAULT(a, b) (a) = (a) ? (a) : (b) | |
- #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)… | |
--#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f… | |
-+#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode … | |
-+ (a).fg != (b).fg || \ | |
- (a).bg != (b).bg) | |
- #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ | |
- (t1.tv_nsec-t2.tv_nsec)/1E6) | |
-diff --git a/win.h b/win.h | |
-index 6de960d..94679e4 100644 | |
---- a/win.h | |
-+++ b/win.h | |
-@@ -25,7 +25,7 @@ enum win_mode { | |
- | |
- void xbell(void); | |
- void xclipcopy(void); | |
--void xdrawcursor(int, int, Glyph, int, int, Glyph); | |
-+void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int); | |
- void xdrawline(Line, int, int, int); | |
- void xfinishdraw(void); | |
- void xloadcols(void); | |
-diff --git a/x.c b/x.c | |
-index 2a3bd38..66605ae 100644 | |
---- a/x.c | |
-+++ b/x.c | |
-@@ -19,6 +19,7 @@ char *argv0; | |
- #include "arg.h" | |
- #include "st.h" | |
- #include "win.h" | |
-+#include "hb.h" | |
- | |
- /* types used in config.h */ | |
- typedef struct { | |
-@@ -141,8 +142,9 @@ typedef struct { | |
- } DC; | |
- | |
- static inline ushort sixd_to_16bit(int); | |
-+static void xresetfontsettings(ushort mode, Font **font, int *frcflags); | |
- static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i… | |
--static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
-+static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
- static void xdrawglyph(Glyph, int, int); | |
- static void xclear(int, int, int, int); | |
- static int xgeommasktogravity(int); | |
-@@ -757,7 +759,7 @@ xresize(int col, int row) | |
- xclear(0, 0, win.w, win.h); | |
- | |
- /* resize to new width */ | |
-- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); | |
-+ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4); | |
- } | |
- | |
- ushort | |
-@@ -1062,6 +1064,9 @@ xunloadfont(Font *f) | |
- void | |
- xunloadfonts(void) | |
- { | |
-+ /* Clear Harfbuzz font cache. */ | |
-+ hbunloadfonts(); | |
-+ | |
- /* Free the loaded fonts in the font cache. */ | |
- while (frclen > 0) | |
- XftFontClose(xw.dpy, frc[--frclen].font); | |
-@@ -1185,7 +1190,7 @@ xinit(int cols, int rows) | |
- XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); | |
- | |
- /* font spec buffer */ | |
-- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); | |
-+ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4); | |
- | |
- /* Xft rendering context */ | |
- xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); | |
-@@ -1239,6 +1244,22 @@ xinit(int cols, int rows) | |
- xsel.xtarget = XA_STRING; | |
- } | |
- | |
-+void | |
-+xresetfontsettings(ushort mode, Font **font, int *frcflags) | |
-+{ | |
-+ *font = &dc.font; | |
-+ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
-+ *font = &dc.ibfont; | |
-+ *frcflags = FRC_ITALICBOLD; | |
-+ } else if (mode & ATTR_ITALIC) { | |
-+ *font = &dc.ifont; | |
-+ *frcflags = FRC_ITALIC; | |
-+ } else if (mode & ATTR_BOLD) { | |
-+ *font = &dc.bfont; | |
-+ *frcflags = FRC_BOLD; | |
-+ } | |
-+} | |
-+ | |
- int | |
- xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in… | |
- { | |
-@@ -1253,128 +1274,156 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const G… | |
- FcPattern *fcpattern, *fontpattern; | |
- FcFontSet *fcsets[] = { NULL }; | |
- FcCharSet *fccharset; | |
-- int i, f, numspecs = 0; | |
-+ int i, f, length = 0, start = 0, numspecs = 0; | |
-+ float cluster_xp = xp, cluster_yp = yp; | |
-+ HbTransformData shaped = { 0 }; | |
-+ | |
-+ /* Initial values. */ | |
-+ mode = prevmode = glyphs[0].mode & ~ATTR_WRAP; | |
-+ xresetfontsettings(mode, &font, &frcflags); | |
- | |
- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { | |
-- /* Fetch rune and mode for current glyph. */ | |
-- rune = glyphs[i].u; | |
-- mode = glyphs[i].mode; | |
-+ mode = glyphs[i].mode & ~ATTR_WRAP; | |
- | |
- /* Skip dummy wide-character spacing. */ | |
-- if (mode == ATTR_WDUMMY) | |
-+ if (mode & ATTR_WDUMMY && i < (len - 1)) | |
- continue; | |
- | |
-- /* Determine font for glyph if different from previous glyph.… | |
-- if (prevmode != mode) { | |
-- prevmode = mode; | |
-- font = &dc.font; | |
-- frcflags = FRC_NORMAL; | |
-- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0… | |
-- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
-- font = &dc.ibfont; | |
-- frcflags = FRC_ITALICBOLD; | |
-- } else if (mode & ATTR_ITALIC) { | |
-- font = &dc.ifont; | |
-- frcflags = FRC_ITALIC; | |
-- } else if (mode & ATTR_BOLD) { | |
-- font = &dc.bfont; | |
-- frcflags = FRC_BOLD; | |
-+ if ( | |
-+ prevmode != mode | |
-+ || ATTRCMP(glyphs[start], glyphs[i]) | |
-+ || selected(x + i, y) != selected(x + start, y) | |
-+ || i == (len - 1) | |
-+ ) { | |
-+ /* Handle 1-character wide segments and end of line */ | |
-+ length = i - start; | |
-+ if (i == start) { | |
-+ length = 1; | |
-+ } else if (i == (len - 1)) { | |
-+ length = (i - start + 1); | |
- } | |
-- yp = winy + font->ascent; | |
-- } | |
- | |
-- /* Lookup character index with default font. */ | |
-- glyphidx = XftCharIndex(xw.dpy, font->match, rune); | |
-- if (glyphidx) { | |
-- specs[numspecs].font = font->match; | |
-- specs[numspecs].glyph = glyphidx; | |
-- specs[numspecs].x = (short)xp; | |
-- specs[numspecs].y = (short)yp; | |
-- xp += runewidth; | |
-- numspecs++; | |
-- continue; | |
-- } | |
-- | |
-- /* Fallback on font cache, search the font cache for match. */ | |
-- for (f = 0; f < frclen; f++) { | |
-- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); | |
-- /* Everything correct. */ | |
-- if (glyphidx && frc[f].flags == frcflags) | |
-- break; | |
-- /* We got a default font for a not found glyph. */ | |
-- if (!glyphidx && frc[f].flags == frcflags | |
-- && frc[f].unicodep == rune) { | |
-- break; | |
-+ /* Shape the segment. */ | |
-+ hbtransform(&shaped, font->match, glyphs, start, leng… | |
-+ runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE… | |
-+ cluster_xp = xp; cluster_yp = yp; | |
-+ for (int code_idx = 0; code_idx < shaped.count; code_… | |
-+ int idx = shaped.glyphs[code_idx].cluster; | |
-+ | |
-+ if (glyphs[start + idx].mode & ATTR_WDUMMY) | |
-+ continue; | |
-+ | |
-+ /* Advance the drawing cursor if we've moved … | |
-+ if (code_idx > 0 && idx != shaped.glyphs[code… | |
-+ xp += runewidth; | |
-+ cluster_xp = xp; | |
-+ cluster_yp = yp; | |
-+ runewidth = win.cw * ((glyphs[start +… | |
-+ } | |
-+ | |
-+ if (shaped.glyphs[code_idx].codepoint != 0) { | |
-+ /* If symbol is found, put it into th… | |
-+ specs[numspecs].font = font->match; | |
-+ specs[numspecs].glyph = shaped.glyphs… | |
-+ specs[numspecs].x = cluster_xp + (sho… | |
-+ specs[numspecs].y = cluster_yp - (sho… | |
-+ cluster_xp += shaped.positions[code_i… | |
-+ cluster_yp += shaped.positions[code_i… | |
-+ numspecs++; | |
-+ } else { | |
-+ /* If it's not found, try to fetch it… | |
-+ rune = glyphs[start + idx].u; | |
-+ for (f = 0; f < frclen; f++) { | |
-+ glyphidx = XftCharIndex(xw.dp… | |
-+ /* Everything correct. */ | |
-+ if (glyphidx && frc[f].flags … | |
-+ break; | |
-+ /* We got a default font for … | |
-+ if (!glyphidx && frc[f].flags… | |
-+ && frc[f].uni… | |
-+ break; | |
-+ } | |
-+ } | |
-+ | |
-+ /* Nothing was found. Use fontconfig … | |
-+ if (f >= frclen) { | |
-+ if (!font->set) | |
-+ font->set = FcFontSor… | |
-+ … | |
-+ fcsets[0] = font->set; | |
-+ | |
-+ /* | |
-+ * Nothing was found in the c… | |
-+ * some dozen of Fontconfig c… | |
-+ * font for one single charac… | |
-+ * | |
-+ * Xft and fontconfig are des… | |
-+ */ | |
-+ fcpattern = FcPatternDuplicat… | |
-+ fccharset = FcCharSetCreate(); | |
-+ | |
-+ FcCharSetAddChar(fccharset, r… | |
-+ FcPatternAddCharSet(fcpattern… | |
-+ fccharset); | |
-+ FcPatternAddBool(fcpattern, F… | |
-+ | |
-+ FcConfigSubstitute(0, fcpatte… | |
-+ FcMatchPatter… | |
-+ FcDefaultSubstitute(fcpattern… | |
-+ | |
-+ fontpattern = FcFontSetMatch(… | |
-+ fcpattern, &f… | |
-+ | |
-+ /* Allocate memory for the ne… | |
-+ if (frclen >= frccap) { | |
-+ frccap += 16; | |
-+ frc = xrealloc(frc, f… | |
-+ } | |
-+ | |
-+ frc[frclen].font = XftFontOpe… | |
-+ fontpattern); | |
-+ if (!frc[frclen].font) | |
-+ die("XftFontOpenPatte… | |
-+ strerror(errn… | |
-+ frc[frclen].flags = frcflags; | |
-+ frc[frclen].unicodep = rune; | |
-+ | |
-+ glyphidx = XftCharIndex(xw.dp… | |
-+ | |
-+ f = frclen; | |
-+ frclen++; | |
-+ | |
-+ FcPatternDestroy(fcpattern); | |
-+ FcCharSetDestroy(fccharset); | |
-+ } | |
-+ | |
-+ specs[numspecs].font = frc[f].font; | |
-+ specs[numspecs].glyph = glyphidx; | |
-+ specs[numspecs].x = (short)xp; | |
-+ specs[numspecs].y = (short)yp; | |
-+ numspecs++; | |
-+ } | |
- } | |
-- } | |
- | |
-- /* Nothing was found. Use fontconfig to find matching font. */ | |
-- if (f >= frclen) { | |
-- if (!font->set) | |
-- font->set = FcFontSort(0, font->pattern, | |
-- 1, 0, &fcres); | |
-- fcsets[0] = font->set; | |
-+ /* Cleanup and get ready for next segment. */ | |
-+ hbcleanup(&shaped); | |
-+ start = i; | |
- | |
-- /* | |
-- * Nothing was found in the cache. Now use | |
-- * some dozen of Fontconfig calls to get the | |
-- * font for one single character. | |
-- * | |
-- * Xft and fontconfig are design failures. | |
-- */ | |
-- fcpattern = FcPatternDuplicate(font->pattern); | |
-- fccharset = FcCharSetCreate(); | |
-- | |
-- FcCharSetAddChar(fccharset, rune); | |
-- FcPatternAddCharSet(fcpattern, FC_CHARSET, | |
-- fccharset); | |
-- FcPatternAddBool(fcpattern, FC_SCALABLE, 1); | |
-- | |
-- FcConfigSubstitute(0, fcpattern, | |
-- FcMatchPattern); | |
-- FcDefaultSubstitute(fcpattern); | |
-- | |
-- fontpattern = FcFontSetMatch(0, fcsets, 1, | |
-- fcpattern, &fcres); | |
-- | |
-- /* Allocate memory for the new cache entry. */ | |
-- if (frclen >= frccap) { | |
-- frccap += 16; | |
-- frc = xrealloc(frc, frccap * sizeof(Fontcache… | |
-+ /* Determine font for glyph if different from previou… | |
-+ if (prevmode != mode) { | |
-+ prevmode = mode; | |
-+ xresetfontsettings(mode, &font, &frcflags); | |
-+ yp = winy + font->ascent; | |
- } | |
-- | |
-- frc[frclen].font = XftFontOpenPattern(xw.dpy, | |
-- fontpattern); | |
-- if (!frc[frclen].font) | |
-- die("XftFontOpenPattern failed seeking fallba… | |
-- strerror(errno)); | |
-- frc[frclen].flags = frcflags; | |
-- frc[frclen].unicodep = rune; | |
-- | |
-- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run… | |
-- | |
-- f = frclen; | |
-- frclen++; | |
-- | |
-- FcPatternDestroy(fcpattern); | |
-- FcCharSetDestroy(fccharset); | |
- } | |
-- | |
-- specs[numspecs].font = frc[f].font; | |
-- specs[numspecs].glyph = glyphidx; | |
-- specs[numspecs].x = (short)xp; | |
-- specs[numspecs].y = (short)yp; | |
-- xp += runewidth; | |
-- numspecs++; | |
- } | |
- | |
- return numspecs; | |
- } | |
- | |
- void | |
--xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
-+xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
- { | |
-- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); | |
- int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, | |
- width = charlen * win.cw; | |
- Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; | |
-@@ -1510,21 +1559,24 @@ void | |
- xdrawglyph(Glyph g, int x, int y) | |
- { | |
- int numspecs; | |
-- XftGlyphFontSpec spec; | |
-+ XftGlyphFontSpec *specs = xw.specbuf; | |
- | |
-- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); | |
-- xdrawglyphfontspecs(&spec, g, numspecs, x, y); | |
-+ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y); | |
-+ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? … | |
- } | |
- | |
- void | |
--xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) | |
-+xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int… | |
- { | |
- Color drawcol; | |
- | |
- /* remove the old cursor */ | |
- if (selected(ox, oy)) | |
- og.mode ^= ATTR_REVERSE; | |
-- xdrawglyph(og, ox, oy); | |
-+ | |
-+ /* Redraw the line where cursor was previously. | |
-+ * It will restore the ligatures broken by the cursor. */ | |
-+ xdrawline(line, 0, oy, len); | |
- | |
- if (IS_SET(MODE_HIDE)) | |
- return; | |
-@@ -1652,18 +1704,16 @@ xdrawline(Line line, int x1, int y1, int x2) | |
- Glyph base, new; | |
- XftGlyphFontSpec *specs = xw.specbuf; | |
- | |
-- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); | |
- i = ox = 0; | |
-- for (x = x1; x < x2 && i < numspecs; x++) { | |
-+ for (x = x1; x < x2; x++) { | |
- new = line[x]; | |
- if (new.mode == ATTR_WDUMMY) | |
- continue; | |
- if (selected(x, y1)) | |
- new.mode ^= ATTR_REVERSE; | |
-- if (i > 0 && ATTRCMP(base, new)) { | |
-- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
-- specs += i; | |
-- numspecs -= i; | |
-+ if ((i > 0) && ATTRCMP(base, new)) { | |
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - … | |
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x … | |
- i = 0; | |
- } | |
- if (i == 0) { | |
-@@ -1672,8 +1722,10 @@ xdrawline(Line line, int x1, int y1, int x2) | |
- } | |
- i++; | |
- } | |
-- if (i > 0) | |
-- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
-+ if (i > 0) { | |
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,… | |
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox); | |
-+ } | |
- } | |
- | |
- void | |
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-20241226-0.9.… | |
@@ -0,0 +1,650 @@ | |
+diff --git a/Makefile b/Makefile | |
+index 15db421..dfcea0f 100644 | |
+--- a/Makefile | |
++++ b/Makefile | |
+@@ -3,9 +3,9 @@ | |
+ .POSIX: | |
+ | |
+ include config.mk | |
+ | |
+-SRC = st.c x.c | |
++SRC = st.c x.c hb.c | |
+ OBJ = $(SRC:.c=.o) | |
+ | |
+ all: st | |
+ | |
+@@ -15,9 +15,10 @@ config.h: | |
+ .c.o: | |
+ $(CC) $(STCFLAGS) -c $< | |
+ | |
+ st.o: config.h st.h win.h | |
+-x.o: arg.h config.h st.h win.h | |
++x.o: arg.h config.h st.h win.h hb.h | |
++hb.o: st.h | |
+ | |
+ $(OBJ): config.h config.mk | |
+ | |
+ st: $(OBJ) | |
+diff --git a/config.mk b/config.mk | |
+index fdc29a7..6833b3b 100644 | |
+--- a/config.mk | |
++++ b/config.mk | |
+@@ -14,12 +14,14 @@ PKG_CONFIG = pkg-config | |
+ | |
+ # includes and libs | |
+ INCS = -I$(X11INC) \ | |
+ `$(PKG_CONFIG) --cflags fontconfig` \ | |
+- `$(PKG_CONFIG) --cflags freetype2` | |
++ `$(PKG_CONFIG) --cflags freetype2` \ | |
++ `$(PKG_CONFIG) --cflags harfbuzz` | |
+ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ | |
+ `$(PKG_CONFIG) --libs fontconfig` \ | |
+- `$(PKG_CONFIG) --libs freetype2` | |
++ `$(PKG_CONFIG) --libs freetype2` \ | |
++ `$(PKG_CONFIG) --libs harfbuzz` | |
+ | |
+ # flags | |
+ STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 | |
+ STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS) | |
+@@ -28,9 +30,10 @@ STLDFLAGS = $(LIBS) $(LDFLAGS) | |
+ # OpenBSD: | |
+ #CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE | |
+ #LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \ | |
+ # `$(PKG_CONFIG) --libs fontconfig` \ | |
+-# `$(PKG_CONFIG) --libs freetype2` | |
++# `$(PKG_CONFIG) --libs freetype2` \ | |
++# `$(PKG_CONFIG) --libs harfbuzz` | |
+ #MANPREFIX = ${PREFIX}/man | |
+ | |
+ # compiler and linker | |
+ # CC = c99 | |
+diff --git a/hb.c b/hb.c | |
+new file mode 100644 | |
+index 0000000..99412c8 | |
+--- /dev/null | |
++++ b/hb.c | |
+@@ -0,0 +1,125 @@ | |
++#include <stdlib.h> | |
++#include <stdio.h> | |
++#include <math.h> | |
++#include <X11/Xft/Xft.h> | |
++#include <X11/cursorfont.h> | |
++#include <hb.h> | |
++#include <hb-ft.h> | |
++ | |
++#include "st.h" | |
++#include "hb.h" | |
++ | |
++#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start… | |
++#define BUFFER_STEP 256 | |
++ | |
++hb_font_t *hbfindfont(XftFont *match); | |
++ | |
++typedef struct { | |
++ XftFont *match; | |
++ hb_font_t *font; | |
++} HbFontMatch; | |
++ | |
++typedef struct { | |
++ size_t capacity; | |
++ HbFontMatch *fonts; | |
++} HbFontCache; | |
++ | |
++static HbFontCache hbfontcache = { 0, NULL }; | |
++ | |
++typedef struct { | |
++ size_t capacity; | |
++ Rune *runes; | |
++} RuneBuffer; | |
++ | |
++static RuneBuffer hbrunebuffer = { 0, NULL }; | |
++ | |
++/* | |
++ * Poplulate the array with a list of font features, wrapped in FEATURE macro, | |
++ * e. g. | |
++ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') | |
++ */ | |
++hb_feature_t features[] = { }; | |
++ | |
++void | |
++hbunloadfonts() | |
++{ | |
++ for (int i = 0; i < hbfontcache.capacity; i++) { | |
++ hb_font_destroy(hbfontcache.fonts[i].font); | |
++ XftUnlockFace(hbfontcache.fonts[i].match); | |
++ } | |
++ | |
++ if (hbfontcache.fonts != NULL) { | |
++ free(hbfontcache.fonts); | |
++ hbfontcache.fonts = NULL; | |
++ } | |
++ hbfontcache.capacity = 0; | |
++} | |
++ | |
++hb_font_t * | |
++hbfindfont(XftFont *match) | |
++{ | |
++ for (int i = 0; i < hbfontcache.capacity; i++) { | |
++ if (hbfontcache.fonts[i].match == match) | |
++ return hbfontcache.fonts[i].font; | |
++ } | |
++ | |
++ /* Font not found in cache, caching it now. */ | |
++ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * … | |
++ FT_Face face = XftLockFace(match); | |
++ hb_font_t *font = hb_ft_font_create(face, NULL); | |
++ if (font == NULL) | |
++ die("Failed to load Harfbuzz font."); | |
++ | |
++ hbfontcache.fonts[hbfontcache.capacity].match = match; | |
++ hbfontcache.fonts[hbfontcache.capacity].font = font; | |
++ hbfontcache.capacity += 1; | |
++ | |
++ return font; | |
++} | |
++ | |
++void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, … | |
++ ushort mode = USHRT_MAX; | |
++ unsigned int glyph_count; | |
++ int rune_idx, glyph_idx, end = start + length; | |
++ | |
++ hb_font_t *font = hbfindfont(xfont); | |
++ if (font == NULL) | |
++ return; | |
++ | |
++ hb_buffer_t *buffer = hb_buffer_create(); | |
++ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); | |
++ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_… | |
++ | |
++ /* Resize the buffer if required length is larger. */ | |
++ if (hbrunebuffer.capacity < length) { | |
++ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S… | |
++ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer… | |
++ } | |
++ | |
++ /* Fill buffer with codepoints. */ | |
++ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r… | |
++ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u; | |
++ mode = glyphs[glyph_idx].mode; | |
++ if (mode & ATTR_WDUMMY) | |
++ hbrunebuffer.runes[rune_idx] = 0x0020; | |
++ } | |
++ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt… | |
++ | |
++ /* Shape the segment. */ | |
++ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t… | |
++ | |
++ /* Get new glyph info. */ | |
++ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun… | |
++ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly… | |
++ | |
++ /* Fill the output. */ | |
++ data->buffer = buffer; | |
++ data->glyphs = info; | |
++ data->positions = pos; | |
++ data->count = glyph_count; | |
++} | |
++ | |
++void hbcleanup(HbTransformData *data) { | |
++ hb_buffer_destroy(data->buffer); | |
++ memset(data, 0, sizeof(HbTransformData)); | |
++} | |
+diff --git a/hb.h b/hb.h | |
+new file mode 100644 | |
+index 0000000..3b0ef44 | |
+--- /dev/null | |
++++ b/hb.h | |
+@@ -0,0 +1,14 @@ | |
++#include <X11/Xft/Xft.h> | |
++#include <hb.h> | |
++#include <hb-ft.h> | |
++ | |
++typedef struct { | |
++ hb_buffer_t *buffer; | |
++ hb_glyph_info_t *glyphs; | |
++ hb_glyph_position_t *positions; | |
++ unsigned int count; | |
++} HbTransformData; | |
++ | |
++void hbunloadfonts(); | |
++void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int); | |
++void hbcleanup(HbTransformData *); | |
+diff --git a/st.c b/st.c | |
+index b9f66e7..da33a85 100644 | |
+--- a/st.c | |
++++ b/st.c | |
+@@ -2658,9 +2658,10 @@ draw(void) | |
+ cx--; | |
+ | |
+ drawregion(0, 0, term.col, term.row); | |
+ xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], | |
+- term.ocx, term.ocy, term.line[term.ocy][term.ocx]); | |
++ term.ocx, term.ocy, term.line[term.ocy][term.ocx], | |
++ term.line[term.ocy], term.col); | |
+ term.ocx = cx; | |
+ term.ocy = term.c.y; | |
+ xfinishdraw(); | |
+ if (ocx != term.ocx || ocy != term.ocy) | |
+diff --git a/st.h b/st.h | |
+index fd3b0d8..142fdfe 100644 | |
+--- a/st.h | |
++++ b/st.h | |
+@@ -10,9 +10,10 @@ | |
+ #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) | |
+ #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) | |
+ #define DEFAULT(a, b) (a) = (a) ? (a) : (b) | |
+ #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)… | |
+-#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f… | |
++#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode … | |
++ (a).fg != (b).fg || \ | |
+ (a).bg != (b).bg) | |
+ #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ | |
+ (t1.tv_nsec-t2.tv_nsec)/1E6) | |
+ #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) | |
+diff --git a/win.h b/win.h | |
+index 6de960d..94679e4 100644 | |
+--- a/win.h | |
++++ b/win.h | |
+@@ -24,9 +24,9 @@ enum win_mode { | |
+ }; | |
+ | |
+ void xbell(void); | |
+ void xclipcopy(void); | |
+-void xdrawcursor(int, int, Glyph, int, int, Glyph); | |
++void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int); | |
+ void xdrawline(Line, int, int, int); | |
+ void xfinishdraw(void); | |
+ void xloadcols(void); | |
+ int xsetcolorname(int, const char *); | |
+diff --git a/x.c b/x.c | |
+index bd23686..2bf3b72 100644 | |
+--- a/x.c | |
++++ b/x.c | |
+@@ -18,8 +18,9 @@ | |
+ char *argv0; | |
+ #include "arg.h" | |
+ #include "st.h" | |
+ #include "win.h" | |
++#include "hb.h" | |
+ | |
+ /* types used in config.h */ | |
+ typedef struct { | |
+ uint mod; | |
+@@ -140,10 +141,11 @@ typedef struct { | |
+ GC gc; | |
+ } DC; | |
+ | |
+ static inline ushort sixd_to_16bit(int); | |
++static void xresetfontsettings(ushort mode, Font **font, int *frcflags); | |
+ static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i… | |
+-static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
++static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
+ static void xdrawglyph(Glyph, int, int); | |
+ static void xclear(int, int, int, int); | |
+ static int xgeommasktogravity(int); | |
+ static int ximopen(Display *); | |
+@@ -756,9 +758,9 @@ xresize(int col, int row) | |
+ XftDrawChange(xw.draw, xw.buf); | |
+ xclear(0, 0, win.w, win.h); | |
+ | |
+ /* resize to new width */ | |
+- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); | |
++ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4); | |
+ } | |
+ | |
+ ushort | |
+ sixd_to_16bit(int x) | |
+@@ -1061,8 +1063,11 @@ xunloadfont(Font *f) | |
+ | |
+ void | |
+ xunloadfonts(void) | |
+ { | |
++ /* Clear Harfbuzz font cache. */ | |
++ hbunloadfonts(); | |
++ | |
+ /* Free the loaded fonts in the font cache. */ | |
+ while (frclen > 0) | |
+ XftFontClose(xw.dpy, frc[--frclen].font); | |
+ | |
+@@ -1184,9 +1189,9 @@ xinit(int cols, int rows) | |
+ XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); | |
+ XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); | |
+ | |
+ /* font spec buffer */ | |
+- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); | |
++ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4); | |
+ | |
+ /* Xft rendering context */ | |
+ xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); | |
+ | |
+@@ -1238,144 +1243,155 @@ xinit(int cols, int rows) | |
+ if (xsel.xtarget == None) | |
+ xsel.xtarget = XA_STRING; | |
+ } | |
+ | |
++void | |
++xresetfontsettings(ushort mode, Font **font, int *frcflags) | |
++{ | |
++ *font = &dc.font; | |
++ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
++ *font = &dc.ibfont; | |
++ *frcflags = FRC_ITALICBOLD; | |
++ } else if (mode & ATTR_ITALIC) { | |
++ *font = &dc.ifont; | |
++ *frcflags = FRC_ITALIC; | |
++ } else if (mode & ATTR_BOLD) { | |
++ *font = &dc.bfont; | |
++ *frcflags = FRC_BOLD; | |
++ } | |
++} | |
++ | |
+ int | |
+ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in… | |
+ { | |
+ float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp,… | |
+- ushort mode, prevmode = USHRT_MAX; | |
++ ushort mode = glyphs[0].mode & ~ATTR_WRAP; | |
+ Font *font = &dc.font; | |
+ int frcflags = FRC_NORMAL; | |
+- float runewidth = win.cw; | |
++ float runewidth = win.cw * ((glyphs[0].mode & ATTR_WIDE) ? 2.0f : 1.0… | |
+ Rune rune; | |
+ FT_UInt glyphidx; | |
+ FcResult fcres; | |
+ FcPattern *fcpattern, *fontpattern; | |
+ FcFontSet *fcsets[] = { NULL }; | |
+ FcCharSet *fccharset; | |
+- int i, f, numspecs = 0; | |
++ int f, code_idx, numspecs = 0; | |
++ float cluster_xp = xp, cluster_yp = yp; | |
++ HbTransformData shaped = { 0 }; | |
+ | |
+- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { | |
+- /* Fetch rune and mode for current glyph. */ | |
+- rune = glyphs[i].u; | |
+- mode = glyphs[i].mode; | |
++ /* Initial values. */ | |
++ xresetfontsettings(mode, &font, &frcflags); | |
+ | |
+- /* Skip dummy wide-character spacing. */ | |
+- if (mode == ATTR_WDUMMY) | |
++ /* Shape the segment. */ | |
++ hbtransform(&shaped, font->match, glyphs, 0, len); | |
++ xp = winx; yp = winy + font->ascent; | |
++ cluster_xp = xp; cluster_yp = yp; | |
++ | |
++ for (code_idx = 0; code_idx < shaped.count; code_idx++) { | |
++ int idx = shaped.glyphs[code_idx].cluster; | |
++ | |
++ if (glyphs[idx].mode & ATTR_WDUMMY) | |
+ continue; | |
+ | |
+- /* Determine font for glyph if different from previous glyph.… | |
+- if (prevmode != mode) { | |
+- prevmode = mode; | |
+- font = &dc.font; | |
+- frcflags = FRC_NORMAL; | |
+- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0… | |
+- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
+- font = &dc.ibfont; | |
+- frcflags = FRC_ITALICBOLD; | |
+- } else if (mode & ATTR_ITALIC) { | |
+- font = &dc.ifont; | |
+- frcflags = FRC_ITALIC; | |
+- } else if (mode & ATTR_BOLD) { | |
+- font = &dc.bfont; | |
+- frcflags = FRC_BOLD; | |
+- } | |
+- yp = winy + font->ascent; | |
++ /* Advance the drawing cursor if we've moved to a new cluster… | |
++ if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluste… | |
++ xp += runewidth; | |
++ cluster_xp = xp; | |
++ cluster_yp = yp; | |
+ } | |
+ | |
+- /* Lookup character index with default font. */ | |
+- glyphidx = XftCharIndex(xw.dpy, font->match, rune); | |
+- if (glyphidx) { | |
++ if (shaped.glyphs[code_idx].codepoint != 0) { | |
++ /* If symbol is found, put it into the specs. */ | |
+ specs[numspecs].font = font->match; | |
+- specs[numspecs].glyph = glyphidx; | |
+- specs[numspecs].x = (short)xp; | |
+- specs[numspecs].y = (short)yp; | |
+- xp += runewidth; | |
++ specs[numspecs].glyph = shaped.glyphs[code_idx].codep… | |
++ specs[numspecs].x = cluster_xp + (short)(shaped.posit… | |
++ specs[numspecs].y = cluster_yp - (short)(shaped.posit… | |
++ cluster_xp += shaped.positions[code_idx].x_advance / … | |
++ cluster_yp += shaped.positions[code_idx].y_advance / … | |
+ numspecs++; | |
+- continue; | |
+- } | |
+- | |
+- /* Fallback on font cache, search the font cache for match. */ | |
+- for (f = 0; f < frclen; f++) { | |
+- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); | |
+- /* Everything correct. */ | |
+- if (glyphidx && frc[f].flags == frcflags) | |
+- break; | |
+- /* We got a default font for a not found glyph. */ | |
+- if (!glyphidx && frc[f].flags == frcflags | |
+- && frc[f].unicodep == rune) { | |
+- break; | |
++ } else { | |
++ /* If it's not found, try to fetch it through the fon… | |
++ rune = glyphs[idx].u; | |
++ for (f = 0; f < frclen; f++) { | |
++ glyphidx = XftCharIndex(xw.dpy, frc[f].font, … | |
++ /* Everything correct. */ | |
++ if (glyphidx && frc[f].flags == frcflags) | |
++ break; | |
++ /* We got a default font for a not found glyp… | |
++ if (!glyphidx && frc[f].flags == frcflags | |
++ && frc[f].unicodep == rune) { | |
++ break; | |
++ } | |
+ } | |
+- } | |
+ | |
+- /* Nothing was found. Use fontconfig to find matching font. */ | |
+- if (f >= frclen) { | |
+- if (!font->set) | |
+- font->set = FcFontSort(0, font->pattern, | |
+- 1, 0, &fcres); | |
+- fcsets[0] = font->set; | |
+- | |
+- /* | |
+- * Nothing was found in the cache. Now use | |
+- * some dozen of Fontconfig calls to get the | |
+- * font for one single character. | |
+- * | |
+- * Xft and fontconfig are design failures. | |
+- */ | |
+- fcpattern = FcPatternDuplicate(font->pattern); | |
+- fccharset = FcCharSetCreate(); | |
+- | |
+- FcCharSetAddChar(fccharset, rune); | |
+- FcPatternAddCharSet(fcpattern, FC_CHARSET, | |
+- fccharset); | |
+- FcPatternAddBool(fcpattern, FC_SCALABLE, 1); | |
+- | |
+- FcConfigSubstitute(0, fcpattern, | |
+- FcMatchPattern); | |
+- FcDefaultSubstitute(fcpattern); | |
+- | |
+- fontpattern = FcFontSetMatch(0, fcsets, 1, | |
+- fcpattern, &fcres); | |
+- | |
+- /* Allocate memory for the new cache entry. */ | |
+- if (frclen >= frccap) { | |
+- frccap += 16; | |
+- frc = xrealloc(frc, frccap * sizeof(Fontcache… | |
++ /* Nothing was found. Use fontconfig to find matching… | |
++ if (f >= frclen) { | |
++ if (!font->set) | |
++ font->set = FcFontSort(0, font->patte… | |
++ 1, 0, &fcres); | |
++ fcsets[0] = font->set; | |
++ | |
++ /* | |
++ * Nothing was found in the cache. Now use | |
++ * some dozen of Fontconfig calls to get the | |
++ * font for one single character. | |
++ * | |
++ * Xft and fontconfig are design failures. | |
++ */ | |
++ fcpattern = FcPatternDuplicate(font->pattern); | |
++ fccharset = FcCharSetCreate(); | |
++ | |
++ FcCharSetAddChar(fccharset, rune); | |
++ FcPatternAddCharSet(fcpattern, FC_CHARSET, | |
++ fccharset); | |
++ FcPatternAddBool(fcpattern, FC_SCALABLE, 1); | |
++ | |
++ FcConfigSubstitute(0, fcpattern, | |
++ FcMatchPattern); | |
++ FcDefaultSubstitute(fcpattern); | |
++ | |
++ fontpattern = FcFontSetMatch(0, fcsets, 1, | |
++ fcpattern, &fcres); | |
++ | |
++ /* Allocate memory for the new cache entry. */ | |
++ if (frclen >= frccap) { | |
++ frccap += 16; | |
++ frc = xrealloc(frc, frccap * sizeof(F… | |
++ } | |
++ | |
++ frc[frclen].font = XftFontOpenPattern(xw.dpy, | |
++ fontpattern); | |
++ if (!frc[frclen].font) | |
++ die("XftFontOpenPattern failed seekin… | |
++ strerror(errno)); | |
++ frc[frclen].flags = frcflags; | |
++ frc[frclen].unicodep = rune; | |
++ | |
++ glyphidx = XftCharIndex(xw.dpy, frc[frclen].f… | |
++ | |
++ f = frclen; | |
++ frclen++; | |
++ | |
++ FcPatternDestroy(fcpattern); | |
++ FcCharSetDestroy(fccharset); | |
+ } | |
+ | |
+- frc[frclen].font = XftFontOpenPattern(xw.dpy, | |
+- fontpattern); | |
+- if (!frc[frclen].font) | |
+- die("XftFontOpenPattern failed seeking fallba… | |
+- strerror(errno)); | |
+- frc[frclen].flags = frcflags; | |
+- frc[frclen].unicodep = rune; | |
+- | |
+- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run… | |
+- | |
+- f = frclen; | |
+- frclen++; | |
+- | |
+- FcPatternDestroy(fcpattern); | |
+- FcCharSetDestroy(fccharset); | |
++ specs[numspecs].font = frc[f].font; | |
++ specs[numspecs].glyph = glyphidx; | |
++ specs[numspecs].x = (short)xp; | |
++ specs[numspecs].y = (short)yp; | |
++ numspecs++; | |
+ } | |
+- | |
+- specs[numspecs].font = frc[f].font; | |
+- specs[numspecs].glyph = glyphidx; | |
+- specs[numspecs].x = (short)xp; | |
+- specs[numspecs].y = (short)yp; | |
+- xp += runewidth; | |
+- numspecs++; | |
+ } | |
+ | |
++ /* Cleanup and get ready for next segment. */ | |
++ hbcleanup(&shaped); | |
+ return numspecs; | |
+ } | |
+ | |
+ void | |
+-xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
++xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
+ { | |
+- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); | |
+ int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, | |
+ width = charlen * win.cw; | |
+ Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; | |
+ XRenderColor colfg, colbg; | |
+@@ -1509,23 +1525,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Gly… | |
+ void | |
+ xdrawglyph(Glyph g, int x, int y) | |
+ { | |
+ int numspecs; | |
+- XftGlyphFontSpec spec; | |
++ XftGlyphFontSpec *specs = xw.specbuf; | |
+ | |
+- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); | |
+- xdrawglyphfontspecs(&spec, g, numspecs, x, y); | |
++ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y); | |
++ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? … | |
+ } | |
+ | |
+ void | |
+-xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) | |
++xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int… | |
+ { | |
+ Color drawcol; | |
+ | |
+ /* remove the old cursor */ | |
+ if (selected(ox, oy)) | |
+ og.mode ^= ATTR_REVERSE; | |
+- xdrawglyph(og, ox, oy); | |
++ | |
++ /* Redraw the line where cursor was previously. | |
++ * It will restore the ligatures broken by the cursor. */ | |
++ xdrawline(line, 0, oy, len); | |
+ | |
+ if (IS_SET(MODE_HIDE)) | |
+ return; | |
+ | |
+@@ -1657,30 +1676,30 @@ xdrawline(Line line, int x1, int y1, int x2) | |
+ int i, x, ox, numspecs; | |
+ Glyph base, new; | |
+ XftGlyphFontSpec *specs = xw.specbuf; | |
+ | |
+- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); | |
+ i = ox = 0; | |
+- for (x = x1; x < x2 && i < numspecs; x++) { | |
++ for (x = x1; x < x2; x++) { | |
+ new = line[x]; | |
+ if (new.mode == ATTR_WDUMMY) | |
+ continue; | |
+ if (selected(x, y1)) | |
+ new.mode ^= ATTR_REVERSE; | |
+- if (i > 0 && ATTRCMP(base, new)) { | |
+- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
+- specs += i; | |
+- numspecs -= i; | |
++ if ((i > 0) && ATTRCMP(base, new)) { | |
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - … | |
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x … | |
+ i = 0; | |
+ } | |
+ if (i == 0) { | |
+ ox = x; | |
+ base = new; | |
+ } | |
+ i++; | |
+ } | |
+- if (i > 0) | |
+- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
++ if (i > 0) { | |
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,… | |
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox); | |
++ } | |
+ } | |
+ | |
+ void | |
+ xfinishdraw(void) | |
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-2024042… | |
@@ -1,635 +0,0 @@ | |
-diff --git a/Makefile b/Makefile | |
-index 470ac86..38240da 100644 | |
---- a/Makefile | |
-+++ b/Makefile | |
-@@ -4,7 +4,7 @@ | |
- | |
- include config.mk | |
- | |
--SRC = st.c x.c | |
-+SRC = st.c x.c hb.c | |
- OBJ = $(SRC:.c=.o) | |
- | |
- all: st | |
-@@ -22,7 +22,8 @@ config.h: | |
- $(CC) $(STCFLAGS) -c $< | |
- | |
- st.o: config.h st.h win.h | |
--x.o: arg.h config.h st.h win.h | |
-+x.o: arg.h config.h st.h win.h hb.h | |
-+hb.o: st.h | |
- | |
- $(OBJ): config.h config.mk | |
- | |
-diff --git a/config.mk b/config.mk | |
-index 47c615e..d7439a3 100644 | |
---- a/config.mk | |
-+++ b/config.mk | |
-@@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config | |
- # includes and libs | |
- INCS = -I$(X11INC) \ | |
- `$(PKG_CONFIG) --cflags fontconfig` \ | |
-- `$(PKG_CONFIG) --cflags freetype2` | |
--LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\ | |
-+ `$(PKG_CONFIG) --cflags freetype2` \ | |
-+ `$(PKG_CONFIG) --cflags harfbuzz` | |
-+LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender \ | |
- `$(PKG_CONFIG) --libs fontconfig` \ | |
-- `$(PKG_CONFIG) --libs freetype2` | |
-+ `$(PKG_CONFIG) --libs freetype2` \ | |
-+ `$(PKG_CONFIG) --libs harfbuzz` | |
- | |
- # flags | |
- STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 | |
-diff --git a/hb.c b/hb.c | |
-new file mode 100644 | |
-index 0000000..58d534b | |
---- /dev/null | |
-+++ b/hb.c | |
-@@ -0,0 +1,125 @@ | |
-+#include <stdlib.h> | |
-+#include <stdio.h> | |
-+#include <math.h> | |
-+#include <X11/Xft/Xft.h> | |
-+#include <X11/cursorfont.h> | |
-+#include <hb.h> | |
-+#include <hb-ft.h> | |
-+ | |
-+#include "st.h" | |
-+#include "hb.h" | |
-+ | |
-+#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start… | |
-+#define BUFFER_STEP 256 | |
-+ | |
-+hb_font_t *hbfindfont(XftFont *match); | |
-+ | |
-+typedef struct { | |
-+ XftFont *match; | |
-+ hb_font_t *font; | |
-+} HbFontMatch; | |
-+ | |
-+typedef struct { | |
-+ size_t capacity; | |
-+ HbFontMatch *fonts; | |
-+} HbFontCache; | |
-+ | |
-+static HbFontCache hbfontcache = { 0, NULL }; | |
-+ | |
-+typedef struct { | |
-+ size_t capacity; | |
-+ Rune *runes; | |
-+} RuneBuffer; | |
-+ | |
-+static RuneBuffer hbrunebuffer = { 0, NULL }; | |
-+ | |
-+/* | |
-+ * Poplulate the array with a list of font features, wrapped in FEATURE macro, | |
-+ * e. g. | |
-+ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') | |
-+ */ | |
-+hb_feature_t features[] = { }; | |
-+ | |
-+void | |
-+hbunloadfonts() | |
-+{ | |
-+ for (int i = 0; i < hbfontcache.capacity; i++) { | |
-+ hb_font_destroy(hbfontcache.fonts[i].font); | |
-+ XftUnlockFace(hbfontcache.fonts[i].match); | |
-+ } | |
-+ | |
-+ if (hbfontcache.fonts != NULL) { | |
-+ free(hbfontcache.fonts); | |
-+ hbfontcache.fonts = NULL; | |
-+ } | |
-+ hbfontcache.capacity = 0; | |
-+} | |
-+ | |
-+hb_font_t * | |
-+hbfindfont(XftFont *match) | |
-+{ | |
-+ for (int i = 0; i < hbfontcache.capacity; i++) { | |
-+ if (hbfontcache.fonts[i].match == match) | |
-+ return hbfontcache.fonts[i].font; | |
-+ } | |
-+ | |
-+ /* Font not found in cache, caching it now. */ | |
-+ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * … | |
-+ FT_Face face = XftLockFace(match); | |
-+ hb_font_t *font = hb_ft_font_create(face, NULL); | |
-+ if (font == NULL) | |
-+ die("Failed to load Harfbuzz font."); | |
-+ | |
-+ hbfontcache.fonts[hbfontcache.capacity].match = match; | |
-+ hbfontcache.fonts[hbfontcache.capacity].font = font; | |
-+ hbfontcache.capacity += 1; | |
-+ | |
-+ return font; | |
-+} | |
-+ | |
-+void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, … | |
-+ ushort mode = USHRT_MAX; | |
-+ unsigned int glyph_count; | |
-+ int rune_idx, glyph_idx, end = start + length; | |
-+ | |
-+ hb_font_t *font = hbfindfont(xfont); | |
-+ if (font == NULL) | |
-+ return; | |
-+ | |
-+ hb_buffer_t *buffer = hb_buffer_create(); | |
-+ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); | |
-+ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_… | |
-+ | |
-+ /* Resize the buffer if required length is larger. */ | |
-+ if (hbrunebuffer.capacity < length) { | |
-+ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S… | |
-+ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer… | |
-+ } | |
-+ | |
-+ /* Fill buffer with codepoints. */ | |
-+ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r… | |
-+ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u; | |
-+ mode = glyphs[glyph_idx].mode; | |
-+ if (mode & ATTR_WDUMMY) | |
-+ hbrunebuffer.runes[rune_idx] = 0x0020; | |
-+ } | |
-+ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt… | |
-+ | |
-+ /* Shape the segment. */ | |
-+ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t… | |
-+ | |
-+ /* Get new glyph info. */ | |
-+ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun… | |
-+ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly… | |
-+ | |
-+ /* Fill the output. */ | |
-+ data->buffer = buffer; | |
-+ data->glyphs = info; | |
-+ data->positions = pos; | |
-+ data->count = glyph_count; | |
-+} | |
-+ | |
-+void hbcleanup(HbTransformData *data) { | |
-+ hb_buffer_destroy(data->buffer); | |
-+ memset(data, 0, sizeof(HbTransformData)); | |
-+} | |
-diff --git a/hb.h b/hb.h | |
-new file mode 100644 | |
-index 0000000..3b0ef44 | |
---- /dev/null | |
-+++ b/hb.h | |
-@@ -0,0 +1,14 @@ | |
-+#include <X11/Xft/Xft.h> | |
-+#include <hb.h> | |
-+#include <hb-ft.h> | |
-+ | |
-+typedef struct { | |
-+ hb_buffer_t *buffer; | |
-+ hb_glyph_info_t *glyphs; | |
-+ hb_glyph_position_t *positions; | |
-+ unsigned int count; | |
-+} HbTransformData; | |
-+ | |
-+void hbunloadfonts(); | |
-+void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int); | |
-+void hbcleanup(HbTransformData *); | |
-diff --git a/st.c b/st.c | |
-index 62def59..041c6d8 100644 | |
---- a/st.c | |
-+++ b/st.c | |
-@@ -2640,7 +2640,8 @@ draw(void) | |
- | |
- drawregion(0, 0, term.col, term.row); | |
- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], | |
-- term.ocx, term.ocy, term.line[term.ocy][term.ocx]); | |
-+ term.ocx, term.ocy, term.line[term.ocy][term.ocx], | |
-+ term.line[term.ocy], term.col); | |
- term.ocx = cx; | |
- term.ocy = term.c.y; | |
- xfinishdraw(); | |
-diff --git a/st.h b/st.h | |
-index 9f91e2a..b1a6256 100644 | |
---- a/st.h | |
-+++ b/st.h | |
-@@ -11,7 +11,8 @@ | |
- #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) | |
- #define DEFAULT(a, b) (a) = (a) ? (a) : (b) | |
- #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)… | |
--#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f… | |
-+#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode … | |
-+ (a).fg != (b).fg || \ | |
- (a).bg != (b).bg) | |
- #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ | |
- (t1.tv_nsec-t2.tv_nsec)/1E6) | |
-diff --git a/win.h b/win.h | |
-index 6de960d..94679e4 100644 | |
---- a/win.h | |
-+++ b/win.h | |
-@@ -25,7 +25,7 @@ enum win_mode { | |
- | |
- void xbell(void); | |
- void xclipcopy(void); | |
--void xdrawcursor(int, int, Glyph, int, int, Glyph); | |
-+void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int); | |
- void xdrawline(Line, int, int, int); | |
- void xfinishdraw(void); | |
- void xloadcols(void); | |
-diff --git a/x.c b/x.c | |
-index 27e81d1..0632636 100644 | |
---- a/x.c | |
-+++ b/x.c | |
-@@ -19,6 +19,7 @@ char *argv0; | |
- #include "arg.h" | |
- #include "st.h" | |
- #include "win.h" | |
-+#include "hb.h" | |
- | |
- /* types used in config.h */ | |
- typedef struct { | |
-@@ -142,8 +143,9 @@ typedef struct { | |
- } DC; | |
- | |
- static inline ushort sixd_to_16bit(int); | |
-+static void xresetfontsettings(ushort mode, Font **font, int *frcflags); | |
- static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i… | |
--static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
-+static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
- static void xdrawglyph(Glyph, int, int); | |
- static void xclear(int, int, int, int); | |
- static int xgeommasktogravity(int); | |
-@@ -759,7 +761,7 @@ xresize(int col, int row) | |
- xclear(0, 0, win.w, win.h); | |
- | |
- /* resize to new width */ | |
-- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); | |
-+ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4); | |
- } | |
- | |
- ushort | |
-@@ -1071,6 +1073,9 @@ xunloadfont(Font *f) | |
- void | |
- xunloadfonts(void) | |
- { | |
-+ /* Clear Harfbuzz font cache. */ | |
-+ hbunloadfonts(); | |
-+ | |
- /* Free the loaded fonts in the font cache. */ | |
- while (frclen > 0) | |
- XftFontClose(xw.dpy, frc[--frclen].font); | |
-@@ -1202,7 +1207,7 @@ xinit(int cols, int rows) | |
- XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); | |
- | |
- /* font spec buffer */ | |
-- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); | |
-+ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4); | |
- | |
- /* Xft rendering context */ | |
- xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); | |
-@@ -1256,6 +1261,22 @@ xinit(int cols, int rows) | |
- xsel.xtarget = XA_STRING; | |
- } | |
- | |
-+void | |
-+xresetfontsettings(ushort mode, Font **font, int *frcflags) | |
-+{ | |
-+ *font = &dc.font; | |
-+ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
-+ *font = &dc.ibfont; | |
-+ *frcflags = FRC_ITALICBOLD; | |
-+ } else if (mode & ATTR_ITALIC) { | |
-+ *font = &dc.ifont; | |
-+ *frcflags = FRC_ITALIC; | |
-+ } else if (mode & ATTR_BOLD) { | |
-+ *font = &dc.bfont; | |
-+ *frcflags = FRC_BOLD; | |
-+ } | |
-+} | |
-+ | |
- int | |
- xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in… | |
- { | |
-@@ -1270,128 +1291,157 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const G… | |
- FcPattern *fcpattern, *fontpattern; | |
- FcFontSet *fcsets[] = { NULL }; | |
- FcCharSet *fccharset; | |
-- int i, f, numspecs = 0; | |
-+ int i, f, length = 0, start = 0, numspecs = 0; | |
-+ float cluster_xp = xp, cluster_yp = yp; | |
-+ HbTransformData shaped = { 0 }; | |
-+ | |
-+ /* Initial values. */ | |
-+ mode = prevmode = glyphs[0].mode & ~ATTR_WRAP; | |
-+ xresetfontsettings(mode, &font, &frcflags); | |
- | |
- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { | |
-- /* Fetch rune and mode for current glyph. */ | |
-- rune = glyphs[i].u; | |
-- mode = glyphs[i].mode; | |
-+ mode = glyphs[i].mode & ~ATTR_WRAP; | |
- | |
- /* Skip dummy wide-character spacing. */ | |
-- if (mode == ATTR_WDUMMY) | |
-+ if (mode & ATTR_WDUMMY && i < (len - 1)) | |
- continue; | |
- | |
-- /* Determine font for glyph if different from previous glyph.… | |
-- if (prevmode != mode) { | |
-- prevmode = mode; | |
-- font = &dc.font; | |
-- frcflags = FRC_NORMAL; | |
-- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0… | |
-- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
-- font = &dc.ibfont; | |
-- frcflags = FRC_ITALICBOLD; | |
-- } else if (mode & ATTR_ITALIC) { | |
-- font = &dc.ifont; | |
-- frcflags = FRC_ITALIC; | |
-- } else if (mode & ATTR_BOLD) { | |
-- font = &dc.bfont; | |
-- frcflags = FRC_BOLD; | |
-+ if ( | |
-+ prevmode != mode | |
-+ || ATTRCMP(glyphs[start], glyphs[i]) | |
-+ || selected(x + i, y) != selected(x + start, y) | |
-+ || i == (len - 1) | |
-+ ) { | |
-+ /* Handle 1-character wide segments and end of line */ | |
-+ length = i - start; | |
-+ if (i == start) { | |
-+ length = 1; | |
-+ } else if (i == (len - 1)) { | |
-+ length = (i - start + 1); | |
- } | |
-- yp = winy + font->ascent; | |
-- } | |
- | |
-- /* Lookup character index with default font. */ | |
-- glyphidx = XftCharIndex(xw.dpy, font->match, rune); | |
-- if (glyphidx) { | |
-- specs[numspecs].font = font->match; | |
-- specs[numspecs].glyph = glyphidx; | |
-- specs[numspecs].x = (short)xp; | |
-- specs[numspecs].y = (short)yp; | |
-- xp += runewidth; | |
-- numspecs++; | |
-- continue; | |
-- } | |
-- | |
-- /* Fallback on font cache, search the font cache for match. */ | |
-- for (f = 0; f < frclen; f++) { | |
-- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); | |
-- /* Everything correct. */ | |
-- if (glyphidx && frc[f].flags == frcflags) | |
-- break; | |
-- /* We got a default font for a not found glyph. */ | |
-- if (!glyphidx && frc[f].flags == frcflags | |
-- && frc[f].unicodep == rune) { | |
-- break; | |
-+ /* Shape the segment. */ | |
-+ hbtransform(&shaped, font->match, glyphs, start, leng… | |
-+ runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE… | |
-+ cluster_xp = xp; cluster_yp = yp; | |
-+ for (int code_idx = 0; code_idx < shaped.count; code_… | |
-+ int idx = shaped.glyphs[code_idx].cluster; | |
-+ | |
-+ if (glyphs[start + idx].mode & ATTR_WDUMMY) | |
-+ continue; | |
-+ | |
-+ /* Advance the drawing cursor if we've moved … | |
-+ if (code_idx > 0 && idx != shaped.glyphs[code… | |
-+ xp += runewidth; | |
-+ cluster_xp = xp; | |
-+ cluster_yp = yp; | |
-+ runewidth = win.cw * ((glyphs[start +… | |
-+ } | |
-+ | |
-+ if (shaped.glyphs[code_idx].codepoint != 0) { | |
-+ /* If symbol is found, put it into th… | |
-+ specs[numspecs].font = font->match; | |
-+ specs[numspecs].glyph = shaped.glyphs… | |
-+ specs[numspecs].x = cluster_xp + (sho… | |
-+ specs[numspecs].y = cluster_yp - (sho… | |
-+ cluster_xp += shaped.positions[code_i… | |
-+ cluster_yp += shaped.positions[code_i… | |
-+ numspecs++; | |
-+ } else { | |
-+ /* If it's not found, try to fetch it… | |
-+ rune = glyphs[start + idx].u; | |
-+ for (f = 0; f < frclen; f++) { | |
-+ glyphidx = XftCharIndex(xw.dp… | |
-+ /* Everything correct. */ | |
-+ if (glyphidx && frc[f].flags … | |
-+ break; | |
-+ /* We got a default font for … | |
-+ if (!glyphidx && frc[f].flags… | |
-+ && frc[f].uni… | |
-+ break; | |
-+ } | |
-+ } | |
-+ | |
-+ /* Nothing was found. Use fontconfig … | |
-+ if (f >= frclen) { | |
-+ if (!font->set) | |
-+ font->set = FcFontSor… | |
-+ … | |
-+ fcsets[0] = font->set; | |
-+ | |
-+ /* | |
-+ * Nothing was found in the c… | |
-+ * some dozen of Fontconfig c… | |
-+ * font for one single charac… | |
-+ * | |
-+ * Xft and fontconfig are des… | |
-+ */ | |
-+ fcpattern = FcPatternDuplicat… | |
-+ fccharset = FcCharSetCreate(); | |
-+ | |
-+ FcCharSetAddChar(fccharset, r… | |
-+ FcPatternAddCharSet(fcpattern… | |
-+ fccharset); | |
-+ FcPatternAddBool(fcpattern, F… | |
-+ | |
-+ FcConfigSubstitute(0, fcpatte… | |
-+ FcMatchPatter… | |
-+ FcDefaultSubstitute(fcpattern… | |
-+ | |
-+ fontpattern = FcFontSetMatch(… | |
-+ fcpattern, &f… | |
-+ | |
-+ /* Allocate memory for the ne… | |
-+ if (frclen >= frccap) { | |
-+ frccap += 16; | |
-+ frc = xrealloc(frc, f… | |
-+ } | |
-+ | |
-+ frc[frclen].font = XftFontOpe… | |
-+ fontpattern); | |
-+ if (!frc[frclen].font) | |
-+ die("XftFontOpenPatte… | |
-+ strerror(errn… | |
-+ frc[frclen].flags = frcflags; | |
-+ frc[frclen].unicodep = rune; | |
-+ | |
-+ glyphidx = XftCharIndex(xw.dp… | |
-+ | |
-+ f = frclen; | |
-+ frclen++; | |
-+ | |
-+ FcPatternDestroy(fcpattern); | |
-+ FcCharSetDestroy(fccharset); | |
-+ } | |
-+ | |
-+ specs[numspecs].font = frc[f].font; | |
-+ specs[numspecs].glyph = glyphidx; | |
-+ specs[numspecs].x = (short)xp; | |
-+ specs[numspecs].y = (short)yp; | |
-+ numspecs++; | |
-+ } | |
- } | |
-- } | |
- | |
-- /* Nothing was found. Use fontconfig to find matching font. */ | |
-- if (f >= frclen) { | |
-- if (!font->set) | |
-- font->set = FcFontSort(0, font->pattern, | |
-- 1, 0, &fcres); | |
-- fcsets[0] = font->set; | |
-+ /* Cleanup and get ready for next segment. */ | |
-+ hbcleanup(&shaped); | |
-+ start = i; | |
- | |
-- /* | |
-- * Nothing was found in the cache. Now use | |
-- * some dozen of Fontconfig calls to get the | |
-- * font for one single character. | |
-- * | |
-- * Xft and fontconfig are design failures. | |
-- */ | |
-- fcpattern = FcPatternDuplicate(font->pattern); | |
-- fccharset = FcCharSetCreate(); | |
-- | |
-- FcCharSetAddChar(fccharset, rune); | |
-- FcPatternAddCharSet(fcpattern, FC_CHARSET, | |
-- fccharset); | |
-- FcPatternAddBool(fcpattern, FC_SCALABLE, 1); | |
-- | |
-- FcConfigSubstitute(0, fcpattern, | |
-- FcMatchPattern); | |
-- FcDefaultSubstitute(fcpattern); | |
-- | |
-- fontpattern = FcFontSetMatch(0, fcsets, 1, | |
-- fcpattern, &fcres); | |
-- | |
-- /* Allocate memory for the new cache entry. */ | |
-- if (frclen >= frccap) { | |
-- frccap += 16; | |
-- frc = xrealloc(frc, frccap * sizeof(Fontcache… | |
-+ /* Determine font for glyph if different from previou… | |
-+ if (prevmode != mode) { | |
-+ prevmode = mode; | |
-+ xresetfontsettings(mode, &font, &frcflags); | |
-+ yp = winy + font->ascent; | |
- } | |
-- | |
-- frc[frclen].font = XftFontOpenPattern(xw.dpy, | |
-- fontpattern); | |
-- if (!frc[frclen].font) | |
-- die("XftFontOpenPattern failed seeking fallba… | |
-- strerror(errno)); | |
-- frc[frclen].flags = frcflags; | |
-- frc[frclen].unicodep = rune; | |
-- | |
-- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run… | |
-- | |
-- f = frclen; | |
-- frclen++; | |
-- | |
-- FcPatternDestroy(fcpattern); | |
-- FcCharSetDestroy(fccharset); | |
- } | |
-- | |
-- specs[numspecs].font = frc[f].font; | |
-- specs[numspecs].glyph = glyphidx; | |
-- specs[numspecs].x = (short)xp; | |
-- specs[numspecs].y = (short)yp; | |
-- xp += runewidth; | |
-- numspecs++; | |
- } | |
- | |
-+ hbcleanup(&shaped); | |
- return numspecs; | |
- } | |
- | |
- void | |
--xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
-+xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
- { | |
-- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); | |
- int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, | |
- width = charlen * win.cw; | |
- Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; | |
-@@ -1527,21 +1577,24 @@ void | |
- xdrawglyph(Glyph g, int x, int y) | |
- { | |
- int numspecs; | |
-- XftGlyphFontSpec spec; | |
-+ XftGlyphFontSpec *specs = xw.specbuf; | |
- | |
-- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); | |
-- xdrawglyphfontspecs(&spec, g, numspecs, x, y); | |
-+ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y); | |
-+ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? … | |
- } | |
- | |
- void | |
--xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) | |
-+xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int… | |
- { | |
- Color drawcol; | |
- | |
- /* remove the old cursor */ | |
- if (selected(ox, oy)) | |
- og.mode ^= ATTR_REVERSE; | |
-- xdrawglyph(og, ox, oy); | |
-+ | |
-+ /* Redraw the line where cursor was previously. | |
-+ * It will restore the ligatures broken by the cursor. */ | |
-+ xdrawline(line, 0, oy, len); | |
- | |
- if (IS_SET(MODE_HIDE)) | |
- return; | |
-@@ -1669,18 +1722,16 @@ xdrawline(Line line, int x1, int y1, int x2) | |
- Glyph base, new; | |
- XftGlyphFontSpec *specs = xw.specbuf; | |
- | |
-- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); | |
- i = ox = 0; | |
-- for (x = x1; x < x2 && i < numspecs; x++) { | |
-+ for (x = x1; x < x2; x++) { | |
- new = line[x]; | |
- if (new.mode == ATTR_WDUMMY) | |
- continue; | |
- if (selected(x, y1)) | |
- new.mode ^= ATTR_REVERSE; | |
-- if (i > 0 && ATTRCMP(base, new)) { | |
-- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
-- specs += i; | |
-- numspecs -= i; | |
-+ if ((i > 0) && ATTRCMP(base, new)) { | |
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - … | |
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x … | |
- i = 0; | |
- } | |
- if (i == 0) { | |
-@@ -1689,8 +1740,10 @@ xdrawline(Line line, int x1, int y1, int x2) | |
- } | |
- i++; | |
- } | |
-- if (i > 0) | |
-- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
-+ if (i > 0) { | |
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,… | |
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox); | |
-+ } | |
- } | |
- | |
- void | |
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-2024122… | |
@@ -0,0 +1,650 @@ | |
+diff --git a/Makefile b/Makefile | |
+index 15db421..dfcea0f 100644 | |
+--- a/Makefile | |
++++ b/Makefile | |
+@@ -3,9 +3,9 @@ | |
+ .POSIX: | |
+ | |
+ include config.mk | |
+ | |
+-SRC = st.c x.c | |
++SRC = st.c x.c hb.c | |
+ OBJ = $(SRC:.c=.o) | |
+ | |
+ all: st | |
+ | |
+@@ -15,9 +15,10 @@ config.h: | |
+ .c.o: | |
+ $(CC) $(STCFLAGS) -c $< | |
+ | |
+ st.o: config.h st.h win.h | |
+-x.o: arg.h config.h st.h win.h | |
++x.o: arg.h config.h st.h win.h hb.h | |
++hb.o: st.h | |
+ | |
+ $(OBJ): config.h config.mk | |
+ | |
+ st: $(OBJ) | |
+diff --git a/config.mk b/config.mk | |
+index 069a6c2..977b7c7 100644 | |
+--- a/config.mk | |
++++ b/config.mk | |
+@@ -14,12 +14,14 @@ PKG_CONFIG = pkg-config | |
+ | |
+ # includes and libs | |
+ INCS = -I$(X11INC) \ | |
+ `$(PKG_CONFIG) --cflags fontconfig` \ | |
+- `$(PKG_CONFIG) --cflags freetype2` | |
++ `$(PKG_CONFIG) --cflags freetype2` \ | |
++ `$(PKG_CONFIG) --cflags harfbuzz` | |
+ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\ | |
+ `$(PKG_CONFIG) --libs fontconfig` \ | |
+- `$(PKG_CONFIG) --libs freetype2` | |
++ `$(PKG_CONFIG) --libs freetype2` \ | |
++ `$(PKG_CONFIG) --libs harfbuzz` | |
+ | |
+ # flags | |
+ STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 | |
+ STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS) | |
+@@ -28,9 +30,10 @@ STLDFLAGS = $(LIBS) $(LDFLAGS) | |
+ # OpenBSD: | |
+ #CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE | |
+ #LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \ | |
+ # `$(PKG_CONFIG) --libs fontconfig` \ | |
+-# `$(PKG_CONFIG) --libs freetype2` | |
++# `$(PKG_CONFIG) --libs freetype2` \ | |
++# `$(PKG_CONFIG) --libs harfbuzz` | |
+ #MANPREFIX = ${PREFIX}/man | |
+ | |
+ # compiler and linker | |
+ # CC = c99 | |
+diff --git a/hb.c b/hb.c | |
+new file mode 100644 | |
+index 0000000..99412c8 | |
+--- /dev/null | |
++++ b/hb.c | |
+@@ -0,0 +1,125 @@ | |
++#include <stdlib.h> | |
++#include <stdio.h> | |
++#include <math.h> | |
++#include <X11/Xft/Xft.h> | |
++#include <X11/cursorfont.h> | |
++#include <hb.h> | |
++#include <hb-ft.h> | |
++ | |
++#include "st.h" | |
++#include "hb.h" | |
++ | |
++#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start… | |
++#define BUFFER_STEP 256 | |
++ | |
++hb_font_t *hbfindfont(XftFont *match); | |
++ | |
++typedef struct { | |
++ XftFont *match; | |
++ hb_font_t *font; | |
++} HbFontMatch; | |
++ | |
++typedef struct { | |
++ size_t capacity; | |
++ HbFontMatch *fonts; | |
++} HbFontCache; | |
++ | |
++static HbFontCache hbfontcache = { 0, NULL }; | |
++ | |
++typedef struct { | |
++ size_t capacity; | |
++ Rune *runes; | |
++} RuneBuffer; | |
++ | |
++static RuneBuffer hbrunebuffer = { 0, NULL }; | |
++ | |
++/* | |
++ * Poplulate the array with a list of font features, wrapped in FEATURE macro, | |
++ * e. g. | |
++ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') | |
++ */ | |
++hb_feature_t features[] = { }; | |
++ | |
++void | |
++hbunloadfonts() | |
++{ | |
++ for (int i = 0; i < hbfontcache.capacity; i++) { | |
++ hb_font_destroy(hbfontcache.fonts[i].font); | |
++ XftUnlockFace(hbfontcache.fonts[i].match); | |
++ } | |
++ | |
++ if (hbfontcache.fonts != NULL) { | |
++ free(hbfontcache.fonts); | |
++ hbfontcache.fonts = NULL; | |
++ } | |
++ hbfontcache.capacity = 0; | |
++} | |
++ | |
++hb_font_t * | |
++hbfindfont(XftFont *match) | |
++{ | |
++ for (int i = 0; i < hbfontcache.capacity; i++) { | |
++ if (hbfontcache.fonts[i].match == match) | |
++ return hbfontcache.fonts[i].font; | |
++ } | |
++ | |
++ /* Font not found in cache, caching it now. */ | |
++ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * … | |
++ FT_Face face = XftLockFace(match); | |
++ hb_font_t *font = hb_ft_font_create(face, NULL); | |
++ if (font == NULL) | |
++ die("Failed to load Harfbuzz font."); | |
++ | |
++ hbfontcache.fonts[hbfontcache.capacity].match = match; | |
++ hbfontcache.fonts[hbfontcache.capacity].font = font; | |
++ hbfontcache.capacity += 1; | |
++ | |
++ return font; | |
++} | |
++ | |
++void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, … | |
++ ushort mode = USHRT_MAX; | |
++ unsigned int glyph_count; | |
++ int rune_idx, glyph_idx, end = start + length; | |
++ | |
++ hb_font_t *font = hbfindfont(xfont); | |
++ if (font == NULL) | |
++ return; | |
++ | |
++ hb_buffer_t *buffer = hb_buffer_create(); | |
++ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); | |
++ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_… | |
++ | |
++ /* Resize the buffer if required length is larger. */ | |
++ if (hbrunebuffer.capacity < length) { | |
++ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S… | |
++ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer… | |
++ } | |
++ | |
++ /* Fill buffer with codepoints. */ | |
++ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r… | |
++ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u; | |
++ mode = glyphs[glyph_idx].mode; | |
++ if (mode & ATTR_WDUMMY) | |
++ hbrunebuffer.runes[rune_idx] = 0x0020; | |
++ } | |
++ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt… | |
++ | |
++ /* Shape the segment. */ | |
++ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t… | |
++ | |
++ /* Get new glyph info. */ | |
++ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun… | |
++ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly… | |
++ | |
++ /* Fill the output. */ | |
++ data->buffer = buffer; | |
++ data->glyphs = info; | |
++ data->positions = pos; | |
++ data->count = glyph_count; | |
++} | |
++ | |
++void hbcleanup(HbTransformData *data) { | |
++ hb_buffer_destroy(data->buffer); | |
++ memset(data, 0, sizeof(HbTransformData)); | |
++} | |
+diff --git a/hb.h b/hb.h | |
+new file mode 100644 | |
+index 0000000..3b0ef44 | |
+--- /dev/null | |
++++ b/hb.h | |
+@@ -0,0 +1,14 @@ | |
++#include <X11/Xft/Xft.h> | |
++#include <hb.h> | |
++#include <hb-ft.h> | |
++ | |
++typedef struct { | |
++ hb_buffer_t *buffer; | |
++ hb_glyph_info_t *glyphs; | |
++ hb_glyph_position_t *positions; | |
++ unsigned int count; | |
++} HbTransformData; | |
++ | |
++void hbunloadfonts(); | |
++void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int); | |
++void hbcleanup(HbTransformData *); | |
+diff --git a/st.c b/st.c | |
+index b9f66e7..da33a85 100644 | |
+--- a/st.c | |
++++ b/st.c | |
+@@ -2658,9 +2658,10 @@ draw(void) | |
+ cx--; | |
+ | |
+ drawregion(0, 0, term.col, term.row); | |
+ xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], | |
+- term.ocx, term.ocy, term.line[term.ocy][term.ocx]); | |
++ term.ocx, term.ocy, term.line[term.ocy][term.ocx], | |
++ term.line[term.ocy], term.col); | |
+ term.ocx = cx; | |
+ term.ocy = term.c.y; | |
+ xfinishdraw(); | |
+ if (ocx != term.ocx || ocy != term.ocy) | |
+diff --git a/st.h b/st.h | |
+index 9f91e2a..b1a6256 100644 | |
+--- a/st.h | |
++++ b/st.h | |
+@@ -10,9 +10,10 @@ | |
+ #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) | |
+ #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) | |
+ #define DEFAULT(a, b) (a) = (a) ? (a) : (b) | |
+ #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)… | |
+-#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f… | |
++#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode … | |
++ (a).fg != (b).fg || \ | |
+ (a).bg != (b).bg) | |
+ #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ | |
+ (t1.tv_nsec-t2.tv_nsec)/1E6) | |
+ #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) | |
+diff --git a/win.h b/win.h | |
+index 6de960d..94679e4 100644 | |
+--- a/win.h | |
++++ b/win.h | |
+@@ -24,9 +24,9 @@ enum win_mode { | |
+ }; | |
+ | |
+ void xbell(void); | |
+ void xclipcopy(void); | |
+-void xdrawcursor(int, int, Glyph, int, int, Glyph); | |
++void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int); | |
+ void xdrawline(Line, int, int, int); | |
+ void xfinishdraw(void); | |
+ void xloadcols(void); | |
+ int xsetcolorname(int, const char *); | |
+diff --git a/x.c b/x.c | |
+index 1e12ac8..b0819ac 100644 | |
+--- a/x.c | |
++++ b/x.c | |
+@@ -18,8 +18,9 @@ | |
+ char *argv0; | |
+ #include "arg.h" | |
+ #include "st.h" | |
+ #include "win.h" | |
++#include "hb.h" | |
+ | |
+ /* types used in config.h */ | |
+ typedef struct { | |
+ uint mod; | |
+@@ -141,10 +142,11 @@ typedef struct { | |
+ GC gc; | |
+ } DC; | |
+ | |
+ static inline ushort sixd_to_16bit(int); | |
++static void xresetfontsettings(ushort mode, Font **font, int *frcflags); | |
+ static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i… | |
+-static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
++static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
+ static void xdrawglyph(Glyph, int, int); | |
+ static void xclear(int, int, int, int); | |
+ static int xgeommasktogravity(int); | |
+ static int ximopen(Display *); | |
+@@ -758,9 +760,9 @@ xresize(int col, int row) | |
+ XftDrawChange(xw.draw, xw.buf); | |
+ xclear(0, 0, win.w, win.h); | |
+ | |
+ /* resize to new width */ | |
+- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); | |
++ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4); | |
+ } | |
+ | |
+ ushort | |
+ sixd_to_16bit(int x) | |
+@@ -1070,8 +1072,11 @@ xunloadfont(Font *f) | |
+ | |
+ void | |
+ xunloadfonts(void) | |
+ { | |
++ /* Clear Harfbuzz font cache. */ | |
++ hbunloadfonts(); | |
++ | |
+ /* Free the loaded fonts in the font cache. */ | |
+ while (frclen > 0) | |
+ XftFontClose(xw.dpy, frc[--frclen].font); | |
+ | |
+@@ -1201,9 +1206,9 @@ xinit(int cols, int rows) | |
+ XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); | |
+ XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); | |
+ | |
+ /* font spec buffer */ | |
+- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); | |
++ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4); | |
+ | |
+ /* Xft rendering context */ | |
+ xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); | |
+ | |
+@@ -1255,144 +1260,155 @@ xinit(int cols, int rows) | |
+ if (xsel.xtarget == None) | |
+ xsel.xtarget = XA_STRING; | |
+ } | |
+ | |
++void | |
++xresetfontsettings(ushort mode, Font **font, int *frcflags) | |
++{ | |
++ *font = &dc.font; | |
++ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
++ *font = &dc.ibfont; | |
++ *frcflags = FRC_ITALICBOLD; | |
++ } else if (mode & ATTR_ITALIC) { | |
++ *font = &dc.ifont; | |
++ *frcflags = FRC_ITALIC; | |
++ } else if (mode & ATTR_BOLD) { | |
++ *font = &dc.bfont; | |
++ *frcflags = FRC_BOLD; | |
++ } | |
++} | |
++ | |
+ int | |
+ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in… | |
+ { | |
+ float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp,… | |
+- ushort mode, prevmode = USHRT_MAX; | |
++ ushort mode = glyphs[0].mode & ~ATTR_WRAP; | |
+ Font *font = &dc.font; | |
+ int frcflags = FRC_NORMAL; | |
+- float runewidth = win.cw; | |
++ float runewidth = win.cw * ((glyphs[0].mode & ATTR_WIDE) ? 2.0f : 1.0… | |
+ Rune rune; | |
+ FT_UInt glyphidx; | |
+ FcResult fcres; | |
+ FcPattern *fcpattern, *fontpattern; | |
+ FcFontSet *fcsets[] = { NULL }; | |
+ FcCharSet *fccharset; | |
+- int i, f, numspecs = 0; | |
++ int f, code_idx, numspecs = 0; | |
++ float cluster_xp = xp, cluster_yp = yp; | |
++ HbTransformData shaped = { 0 }; | |
+ | |
+- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { | |
+- /* Fetch rune and mode for current glyph. */ | |
+- rune = glyphs[i].u; | |
+- mode = glyphs[i].mode; | |
++ /* Initial values. */ | |
++ xresetfontsettings(mode, &font, &frcflags); | |
+ | |
+- /* Skip dummy wide-character spacing. */ | |
+- if (mode == ATTR_WDUMMY) | |
++ /* Shape the segment. */ | |
++ hbtransform(&shaped, font->match, glyphs, 0, len); | |
++ xp = winx; yp = winy + font->ascent; | |
++ cluster_xp = xp; cluster_yp = yp; | |
++ | |
++ for (code_idx = 0; code_idx < shaped.count; code_idx++) { | |
++ int idx = shaped.glyphs[code_idx].cluster; | |
++ | |
++ if (glyphs[idx].mode & ATTR_WDUMMY) | |
+ continue; | |
+ | |
+- /* Determine font for glyph if different from previous glyph.… | |
+- if (prevmode != mode) { | |
+- prevmode = mode; | |
+- font = &dc.font; | |
+- frcflags = FRC_NORMAL; | |
+- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0… | |
+- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
+- font = &dc.ibfont; | |
+- frcflags = FRC_ITALICBOLD; | |
+- } else if (mode & ATTR_ITALIC) { | |
+- font = &dc.ifont; | |
+- frcflags = FRC_ITALIC; | |
+- } else if (mode & ATTR_BOLD) { | |
+- font = &dc.bfont; | |
+- frcflags = FRC_BOLD; | |
+- } | |
+- yp = winy + font->ascent; | |
++ /* Advance the drawing cursor if we've moved to a new cluster… | |
++ if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluste… | |
++ xp += runewidth; | |
++ cluster_xp = xp; | |
++ cluster_yp = yp; | |
+ } | |
+ | |
+- /* Lookup character index with default font. */ | |
+- glyphidx = XftCharIndex(xw.dpy, font->match, rune); | |
+- if (glyphidx) { | |
++ if (shaped.glyphs[code_idx].codepoint != 0) { | |
++ /* If symbol is found, put it into the specs. */ | |
+ specs[numspecs].font = font->match; | |
+- specs[numspecs].glyph = glyphidx; | |
+- specs[numspecs].x = (short)xp; | |
+- specs[numspecs].y = (short)yp; | |
+- xp += runewidth; | |
++ specs[numspecs].glyph = shaped.glyphs[code_idx].codep… | |
++ specs[numspecs].x = cluster_xp + (short)(shaped.posit… | |
++ specs[numspecs].y = cluster_yp - (short)(shaped.posit… | |
++ cluster_xp += shaped.positions[code_idx].x_advance / … | |
++ cluster_yp += shaped.positions[code_idx].y_advance / … | |
+ numspecs++; | |
+- continue; | |
+- } | |
+- | |
+- /* Fallback on font cache, search the font cache for match. */ | |
+- for (f = 0; f < frclen; f++) { | |
+- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); | |
+- /* Everything correct. */ | |
+- if (glyphidx && frc[f].flags == frcflags) | |
+- break; | |
+- /* We got a default font for a not found glyph. */ | |
+- if (!glyphidx && frc[f].flags == frcflags | |
+- && frc[f].unicodep == rune) { | |
+- break; | |
++ } else { | |
++ /* If it's not found, try to fetch it through the fon… | |
++ rune = glyphs[idx].u; | |
++ for (f = 0; f < frclen; f++) { | |
++ glyphidx = XftCharIndex(xw.dpy, frc[f].font, … | |
++ /* Everything correct. */ | |
++ if (glyphidx && frc[f].flags == frcflags) | |
++ break; | |
++ /* We got a default font for a not found glyp… | |
++ if (!glyphidx && frc[f].flags == frcflags | |
++ && frc[f].unicodep == rune) { | |
++ break; | |
++ } | |
+ } | |
+- } | |
+ | |
+- /* Nothing was found. Use fontconfig to find matching font. */ | |
+- if (f >= frclen) { | |
+- if (!font->set) | |
+- font->set = FcFontSort(0, font->pattern, | |
+- 1, 0, &fcres); | |
+- fcsets[0] = font->set; | |
+- | |
+- /* | |
+- * Nothing was found in the cache. Now use | |
+- * some dozen of Fontconfig calls to get the | |
+- * font for one single character. | |
+- * | |
+- * Xft and fontconfig are design failures. | |
+- */ | |
+- fcpattern = FcPatternDuplicate(font->pattern); | |
+- fccharset = FcCharSetCreate(); | |
+- | |
+- FcCharSetAddChar(fccharset, rune); | |
+- FcPatternAddCharSet(fcpattern, FC_CHARSET, | |
+- fccharset); | |
+- FcPatternAddBool(fcpattern, FC_SCALABLE, 1); | |
+- | |
+- FcConfigSubstitute(0, fcpattern, | |
+- FcMatchPattern); | |
+- FcDefaultSubstitute(fcpattern); | |
+- | |
+- fontpattern = FcFontSetMatch(0, fcsets, 1, | |
+- fcpattern, &fcres); | |
+- | |
+- /* Allocate memory for the new cache entry. */ | |
+- if (frclen >= frccap) { | |
+- frccap += 16; | |
+- frc = xrealloc(frc, frccap * sizeof(Fontcache… | |
++ /* Nothing was found. Use fontconfig to find matching… | |
++ if (f >= frclen) { | |
++ if (!font->set) | |
++ font->set = FcFontSort(0, font->patte… | |
++ 1, 0, &fcres); | |
++ fcsets[0] = font->set; | |
++ | |
++ /* | |
++ * Nothing was found in the cache. Now use | |
++ * some dozen of Fontconfig calls to get the | |
++ * font for one single character. | |
++ * | |
++ * Xft and fontconfig are design failures. | |
++ */ | |
++ fcpattern = FcPatternDuplicate(font->pattern); | |
++ fccharset = FcCharSetCreate(); | |
++ | |
++ FcCharSetAddChar(fccharset, rune); | |
++ FcPatternAddCharSet(fcpattern, FC_CHARSET, | |
++ fccharset); | |
++ FcPatternAddBool(fcpattern, FC_SCALABLE, 1); | |
++ | |
++ FcConfigSubstitute(0, fcpattern, | |
++ FcMatchPattern); | |
++ FcDefaultSubstitute(fcpattern); | |
++ | |
++ fontpattern = FcFontSetMatch(0, fcsets, 1, | |
++ fcpattern, &fcres); | |
++ | |
++ /* Allocate memory for the new cache entry. */ | |
++ if (frclen >= frccap) { | |
++ frccap += 16; | |
++ frc = xrealloc(frc, frccap * sizeof(F… | |
++ } | |
++ | |
++ frc[frclen].font = XftFontOpenPattern(xw.dpy, | |
++ fontpattern); | |
++ if (!frc[frclen].font) | |
++ die("XftFontOpenPattern failed seekin… | |
++ strerror(errno)); | |
++ frc[frclen].flags = frcflags; | |
++ frc[frclen].unicodep = rune; | |
++ | |
++ glyphidx = XftCharIndex(xw.dpy, frc[frclen].f… | |
++ | |
++ f = frclen; | |
++ frclen++; | |
++ | |
++ FcPatternDestroy(fcpattern); | |
++ FcCharSetDestroy(fccharset); | |
+ } | |
+ | |
+- frc[frclen].font = XftFontOpenPattern(xw.dpy, | |
+- fontpattern); | |
+- if (!frc[frclen].font) | |
+- die("XftFontOpenPattern failed seeking fallba… | |
+- strerror(errno)); | |
+- frc[frclen].flags = frcflags; | |
+- frc[frclen].unicodep = rune; | |
+- | |
+- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run… | |
+- | |
+- f = frclen; | |
+- frclen++; | |
+- | |
+- FcPatternDestroy(fcpattern); | |
+- FcCharSetDestroy(fccharset); | |
++ specs[numspecs].font = frc[f].font; | |
++ specs[numspecs].glyph = glyphidx; | |
++ specs[numspecs].x = (short)xp; | |
++ specs[numspecs].y = (short)yp; | |
++ numspecs++; | |
+ } | |
+- | |
+- specs[numspecs].font = frc[f].font; | |
+- specs[numspecs].glyph = glyphidx; | |
+- specs[numspecs].x = (short)xp; | |
+- specs[numspecs].y = (short)yp; | |
+- xp += runewidth; | |
+- numspecs++; | |
+ } | |
+ | |
++ /* Cleanup and get ready for next segment. */ | |
++ hbcleanup(&shaped); | |
+ return numspecs; | |
+ } | |
+ | |
+ void | |
+-xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
++xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
+ { | |
+- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); | |
+ int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, | |
+ width = charlen * win.cw; | |
+ Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; | |
+ XRenderColor colfg, colbg; | |
+@@ -1526,23 +1542,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Gly… | |
+ void | |
+ xdrawglyph(Glyph g, int x, int y) | |
+ { | |
+ int numspecs; | |
+- XftGlyphFontSpec spec; | |
++ XftGlyphFontSpec *specs = xw.specbuf; | |
+ | |
+- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); | |
+- xdrawglyphfontspecs(&spec, g, numspecs, x, y); | |
++ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y); | |
++ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? … | |
+ } | |
+ | |
+ void | |
+-xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) | |
++xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int… | |
+ { | |
+ Color drawcol; | |
+ | |
+ /* remove the old cursor */ | |
+ if (selected(ox, oy)) | |
+ og.mode ^= ATTR_REVERSE; | |
+- xdrawglyph(og, ox, oy); | |
++ | |
++ /* Redraw the line where cursor was previously. | |
++ * It will restore the ligatures broken by the cursor. */ | |
++ xdrawline(line, 0, oy, len); | |
+ | |
+ if (IS_SET(MODE_HIDE)) | |
+ return; | |
+ | |
+@@ -1674,30 +1693,30 @@ xdrawline(Line line, int x1, int y1, int x2) | |
+ int i, x, ox, numspecs; | |
+ Glyph base, new; | |
+ XftGlyphFontSpec *specs = xw.specbuf; | |
+ | |
+- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); | |
+ i = ox = 0; | |
+- for (x = x1; x < x2 && i < numspecs; x++) { | |
++ for (x = x1; x < x2; x++) { | |
+ new = line[x]; | |
+ if (new.mode == ATTR_WDUMMY) | |
+ continue; | |
+ if (selected(x, y1)) | |
+ new.mode ^= ATTR_REVERSE; | |
+- if (i > 0 && ATTRCMP(base, new)) { | |
+- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
+- specs += i; | |
+- numspecs -= i; | |
++ if ((i > 0) && ATTRCMP(base, new)) { | |
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - … | |
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x … | |
+ i = 0; | |
+ } | |
+ if (i == 0) { | |
+ ox = x; | |
+ base = new; | |
+ } | |
+ i++; | |
+ } | |
+- if (i > 0) | |
+- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
++ if (i > 0) { | |
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,… | |
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox); | |
++ } | |
+ } | |
+ | |
+ void | |
+ xfinishdraw(void) | |
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-scrollb… | |
@@ -1,635 +0,0 @@ | |
-diff --git a/Makefile b/Makefile | |
-index 470ac86..38240da 100644 | |
---- a/Makefile | |
-+++ b/Makefile | |
-@@ -4,7 +4,7 @@ | |
- | |
- include config.mk | |
- | |
--SRC = st.c x.c | |
-+SRC = st.c x.c hb.c | |
- OBJ = $(SRC:.c=.o) | |
- | |
- all: st | |
-@@ -22,7 +22,8 @@ config.h: | |
- $(CC) $(STCFLAGS) -c $< | |
- | |
- st.o: config.h st.h win.h | |
--x.o: arg.h config.h st.h win.h | |
-+x.o: arg.h config.h st.h win.h hb.h | |
-+hb.o: st.h | |
- | |
- $(OBJ): config.h config.mk | |
- | |
-diff --git a/config.mk b/config.mk | |
-index 47c615e..d7439a3 100644 | |
---- a/config.mk | |
-+++ b/config.mk | |
-@@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config | |
- # includes and libs | |
- INCS = -I$(X11INC) \ | |
- `$(PKG_CONFIG) --cflags fontconfig` \ | |
-- `$(PKG_CONFIG) --cflags freetype2` | |
--LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\ | |
-+ `$(PKG_CONFIG) --cflags freetype2` \ | |
-+ `$(PKG_CONFIG) --cflags harfbuzz` | |
-+LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender \ | |
- `$(PKG_CONFIG) --libs fontconfig` \ | |
-- `$(PKG_CONFIG) --libs freetype2` | |
-+ `$(PKG_CONFIG) --libs freetype2` \ | |
-+ `$(PKG_CONFIG) --libs harfbuzz` | |
- | |
- # flags | |
- STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 | |
-diff --git a/hb.c b/hb.c | |
-new file mode 100644 | |
-index 0000000..99412c8 | |
---- /dev/null | |
-+++ b/hb.c | |
-@@ -0,0 +1,125 @@ | |
-+#include <stdlib.h> | |
-+#include <stdio.h> | |
-+#include <math.h> | |
-+#include <X11/Xft/Xft.h> | |
-+#include <X11/cursorfont.h> | |
-+#include <hb.h> | |
-+#include <hb-ft.h> | |
-+ | |
-+#include "st.h" | |
-+#include "hb.h" | |
-+ | |
-+#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start… | |
-+#define BUFFER_STEP 256 | |
-+ | |
-+hb_font_t *hbfindfont(XftFont *match); | |
-+ | |
-+typedef struct { | |
-+ XftFont *match; | |
-+ hb_font_t *font; | |
-+} HbFontMatch; | |
-+ | |
-+typedef struct { | |
-+ size_t capacity; | |
-+ HbFontMatch *fonts; | |
-+} HbFontCache; | |
-+ | |
-+static HbFontCache hbfontcache = { 0, NULL }; | |
-+ | |
-+typedef struct { | |
-+ size_t capacity; | |
-+ Rune *runes; | |
-+} RuneBuffer; | |
-+ | |
-+static RuneBuffer hbrunebuffer = { 0, NULL }; | |
-+ | |
-+/* | |
-+ * Poplulate the array with a list of font features, wrapped in FEATURE macro, | |
-+ * e. g. | |
-+ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') | |
-+ */ | |
-+hb_feature_t features[] = { }; | |
-+ | |
-+void | |
-+hbunloadfonts() | |
-+{ | |
-+ for (int i = 0; i < hbfontcache.capacity; i++) { | |
-+ hb_font_destroy(hbfontcache.fonts[i].font); | |
-+ XftUnlockFace(hbfontcache.fonts[i].match); | |
-+ } | |
-+ | |
-+ if (hbfontcache.fonts != NULL) { | |
-+ free(hbfontcache.fonts); | |
-+ hbfontcache.fonts = NULL; | |
-+ } | |
-+ hbfontcache.capacity = 0; | |
-+} | |
-+ | |
-+hb_font_t * | |
-+hbfindfont(XftFont *match) | |
-+{ | |
-+ for (int i = 0; i < hbfontcache.capacity; i++) { | |
-+ if (hbfontcache.fonts[i].match == match) | |
-+ return hbfontcache.fonts[i].font; | |
-+ } | |
-+ | |
-+ /* Font not found in cache, caching it now. */ | |
-+ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * … | |
-+ FT_Face face = XftLockFace(match); | |
-+ hb_font_t *font = hb_ft_font_create(face, NULL); | |
-+ if (font == NULL) | |
-+ die("Failed to load Harfbuzz font."); | |
-+ | |
-+ hbfontcache.fonts[hbfontcache.capacity].match = match; | |
-+ hbfontcache.fonts[hbfontcache.capacity].font = font; | |
-+ hbfontcache.capacity += 1; | |
-+ | |
-+ return font; | |
-+} | |
-+ | |
-+void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, … | |
-+ ushort mode = USHRT_MAX; | |
-+ unsigned int glyph_count; | |
-+ int rune_idx, glyph_idx, end = start + length; | |
-+ | |
-+ hb_font_t *font = hbfindfont(xfont); | |
-+ if (font == NULL) | |
-+ return; | |
-+ | |
-+ hb_buffer_t *buffer = hb_buffer_create(); | |
-+ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); | |
-+ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_… | |
-+ | |
-+ /* Resize the buffer if required length is larger. */ | |
-+ if (hbrunebuffer.capacity < length) { | |
-+ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S… | |
-+ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer… | |
-+ } | |
-+ | |
-+ /* Fill buffer with codepoints. */ | |
-+ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r… | |
-+ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u; | |
-+ mode = glyphs[glyph_idx].mode; | |
-+ if (mode & ATTR_WDUMMY) | |
-+ hbrunebuffer.runes[rune_idx] = 0x0020; | |
-+ } | |
-+ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt… | |
-+ | |
-+ /* Shape the segment. */ | |
-+ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t… | |
-+ | |
-+ /* Get new glyph info. */ | |
-+ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun… | |
-+ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly… | |
-+ | |
-+ /* Fill the output. */ | |
-+ data->buffer = buffer; | |
-+ data->glyphs = info; | |
-+ data->positions = pos; | |
-+ data->count = glyph_count; | |
-+} | |
-+ | |
-+void hbcleanup(HbTransformData *data) { | |
-+ hb_buffer_destroy(data->buffer); | |
-+ memset(data, 0, sizeof(HbTransformData)); | |
-+} | |
-diff --git a/hb.h b/hb.h | |
-new file mode 100644 | |
-index 0000000..3b0ef44 | |
---- /dev/null | |
-+++ b/hb.h | |
-@@ -0,0 +1,14 @@ | |
-+#include <X11/Xft/Xft.h> | |
-+#include <hb.h> | |
-+#include <hb-ft.h> | |
-+ | |
-+typedef struct { | |
-+ hb_buffer_t *buffer; | |
-+ hb_glyph_info_t *glyphs; | |
-+ hb_glyph_position_t *positions; | |
-+ unsigned int count; | |
-+} HbTransformData; | |
-+ | |
-+void hbunloadfonts(); | |
-+void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int); | |
-+void hbcleanup(HbTransformData *); | |
-diff --git a/st.c b/st.c | |
-index 79ee9ba..454771d 100644 | |
---- a/st.c | |
-+++ b/st.c | |
-@@ -2711,7 +2711,9 @@ draw(void) | |
- drawregion(0, 0, term.col, term.row); | |
- if (term.scr == 0) | |
- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], | |
-- term.ocx, term.ocy, term.line[term.ocy][term.… | |
-+ term.ocx, term.ocy, term.line[term.ocy][term.… | |
-+ term.line[term.ocy], term.col); | |
-+ | |
- term.ocx = cx; | |
- term.ocy = term.c.y; | |
- xfinishdraw(); | |
-diff --git a/st.h b/st.h | |
-index 78762a2..01eea49 100644 | |
---- a/st.h | |
-+++ b/st.h | |
-@@ -11,7 +11,8 @@ | |
- #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) | |
- #define DEFAULT(a, b) (a) = (a) ? (a) : (b) | |
- #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)… | |
--#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f… | |
-+#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode … | |
-+ (a).fg != (b).fg || \ | |
- (a).bg != (b).bg) | |
- #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ | |
- (t1.tv_nsec-t2.tv_nsec)/1E6) | |
-diff --git a/win.h b/win.h | |
-index 6de960d..94679e4 100644 | |
---- a/win.h | |
-+++ b/win.h | |
-@@ -25,7 +25,7 @@ enum win_mode { | |
- | |
- void xbell(void); | |
- void xclipcopy(void); | |
--void xdrawcursor(int, int, Glyph, int, int, Glyph); | |
-+void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int); | |
- void xdrawline(Line, int, int, int); | |
- void xfinishdraw(void); | |
- void xloadcols(void); | |
-diff --git a/x.c b/x.c | |
-index 27e81d1..5e11c1f 100644 | |
---- a/x.c | |
-+++ b/x.c | |
-@@ -19,6 +19,7 @@ char *argv0; | |
- #include "arg.h" | |
- #include "st.h" | |
- #include "win.h" | |
-+#include "hb.h" | |
- | |
- /* types used in config.h */ | |
- typedef struct { | |
-@@ -142,8 +143,9 @@ typedef struct { | |
- } DC; | |
- | |
- static inline ushort sixd_to_16bit(int); | |
-+static void xresetfontsettings(ushort mode, Font **font, int *frcflags); | |
- static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i… | |
--static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
-+static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
- static void xdrawglyph(Glyph, int, int); | |
- static void xclear(int, int, int, int); | |
- static int xgeommasktogravity(int); | |
-@@ -759,7 +761,7 @@ xresize(int col, int row) | |
- xclear(0, 0, win.w, win.h); | |
- | |
- /* resize to new width */ | |
-- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); | |
-+ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4); | |
- } | |
- | |
- ushort | |
-@@ -1071,6 +1073,9 @@ xunloadfont(Font *f) | |
- void | |
- xunloadfonts(void) | |
- { | |
-+ /* Clear Harfbuzz font cache. */ | |
-+ hbunloadfonts(); | |
-+ | |
- /* Free the loaded fonts in the font cache. */ | |
- while (frclen > 0) | |
- XftFontClose(xw.dpy, frc[--frclen].font); | |
-@@ -1202,7 +1207,7 @@ xinit(int cols, int rows) | |
- XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); | |
- | |
- /* font spec buffer */ | |
-- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); | |
-+ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4); | |
- | |
- /* Xft rendering context */ | |
- xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); | |
-@@ -1256,6 +1261,22 @@ xinit(int cols, int rows) | |
- xsel.xtarget = XA_STRING; | |
- } | |
- | |
-+void | |
-+xresetfontsettings(ushort mode, Font **font, int *frcflags) | |
-+{ | |
-+ *font = &dc.font; | |
-+ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
-+ *font = &dc.ibfont; | |
-+ *frcflags = FRC_ITALICBOLD; | |
-+ } else if (mode & ATTR_ITALIC) { | |
-+ *font = &dc.ifont; | |
-+ *frcflags = FRC_ITALIC; | |
-+ } else if (mode & ATTR_BOLD) { | |
-+ *font = &dc.bfont; | |
-+ *frcflags = FRC_BOLD; | |
-+ } | |
-+} | |
-+ | |
- int | |
- xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in… | |
- { | |
-@@ -1270,128 +1291,156 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const G… | |
- FcPattern *fcpattern, *fontpattern; | |
- FcFontSet *fcsets[] = { NULL }; | |
- FcCharSet *fccharset; | |
-- int i, f, numspecs = 0; | |
-+ int i, f, length = 0, start = 0, numspecs = 0; | |
-+ float cluster_xp = xp, cluster_yp = yp; | |
-+ HbTransformData shaped = { 0 }; | |
-+ | |
-+ /* Initial values. */ | |
-+ mode = prevmode = glyphs[0].mode & ~ATTR_WRAP; | |
-+ xresetfontsettings(mode, &font, &frcflags); | |
- | |
- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { | |
-- /* Fetch rune and mode for current glyph. */ | |
-- rune = glyphs[i].u; | |
-- mode = glyphs[i].mode; | |
-+ mode = glyphs[i].mode & ~ATTR_WRAP; | |
- | |
- /* Skip dummy wide-character spacing. */ | |
-- if (mode == ATTR_WDUMMY) | |
-+ if (mode & ATTR_WDUMMY && i < (len - 1)) | |
- continue; | |
- | |
-- /* Determine font for glyph if different from previous glyph.… | |
-- if (prevmode != mode) { | |
-- prevmode = mode; | |
-- font = &dc.font; | |
-- frcflags = FRC_NORMAL; | |
-- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0… | |
-- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
-- font = &dc.ibfont; | |
-- frcflags = FRC_ITALICBOLD; | |
-- } else if (mode & ATTR_ITALIC) { | |
-- font = &dc.ifont; | |
-- frcflags = FRC_ITALIC; | |
-- } else if (mode & ATTR_BOLD) { | |
-- font = &dc.bfont; | |
-- frcflags = FRC_BOLD; | |
-+ if ( | |
-+ prevmode != mode | |
-+ || ATTRCMP(glyphs[start], glyphs[i]) | |
-+ || selected(x + i, y) != selected(x + start, y) | |
-+ || i == (len - 1) | |
-+ ) { | |
-+ /* Handle 1-character wide segments and end of line */ | |
-+ length = i - start; | |
-+ if (i == start) { | |
-+ length = 1; | |
-+ } else if (i == (len - 1)) { | |
-+ length = (i - start + 1); | |
- } | |
-- yp = winy + font->ascent; | |
-- } | |
- | |
-- /* Lookup character index with default font. */ | |
-- glyphidx = XftCharIndex(xw.dpy, font->match, rune); | |
-- if (glyphidx) { | |
-- specs[numspecs].font = font->match; | |
-- specs[numspecs].glyph = glyphidx; | |
-- specs[numspecs].x = (short)xp; | |
-- specs[numspecs].y = (short)yp; | |
-- xp += runewidth; | |
-- numspecs++; | |
-- continue; | |
-- } | |
-- | |
-- /* Fallback on font cache, search the font cache for match. */ | |
-- for (f = 0; f < frclen; f++) { | |
-- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); | |
-- /* Everything correct. */ | |
-- if (glyphidx && frc[f].flags == frcflags) | |
-- break; | |
-- /* We got a default font for a not found glyph. */ | |
-- if (!glyphidx && frc[f].flags == frcflags | |
-- && frc[f].unicodep == rune) { | |
-- break; | |
-+ /* Shape the segment. */ | |
-+ hbtransform(&shaped, font->match, glyphs, start, leng… | |
-+ runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE… | |
-+ cluster_xp = xp; cluster_yp = yp; | |
-+ for (int code_idx = 0; code_idx < shaped.count; code_… | |
-+ int idx = shaped.glyphs[code_idx].cluster; | |
-+ | |
-+ if (glyphs[start + idx].mode & ATTR_WDUMMY) | |
-+ continue; | |
-+ | |
-+ /* Advance the drawing cursor if we've moved … | |
-+ if (code_idx > 0 && idx != shaped.glyphs[code… | |
-+ xp += runewidth; | |
-+ cluster_xp = xp; | |
-+ cluster_yp = yp; | |
-+ runewidth = win.cw * ((glyphs[start +… | |
-+ } | |
-+ | |
-+ if (shaped.glyphs[code_idx].codepoint != 0) { | |
-+ /* If symbol is found, put it into th… | |
-+ specs[numspecs].font = font->match; | |
-+ specs[numspecs].glyph = shaped.glyphs… | |
-+ specs[numspecs].x = cluster_xp + (sho… | |
-+ specs[numspecs].y = cluster_yp - (sho… | |
-+ cluster_xp += shaped.positions[code_i… | |
-+ cluster_yp += shaped.positions[code_i… | |
-+ numspecs++; | |
-+ } else { | |
-+ /* If it's not found, try to fetch it… | |
-+ rune = glyphs[start + idx].u; | |
-+ for (f = 0; f < frclen; f++) { | |
-+ glyphidx = XftCharIndex(xw.dp… | |
-+ /* Everything correct. */ | |
-+ if (glyphidx && frc[f].flags … | |
-+ break; | |
-+ /* We got a default font for … | |
-+ if (!glyphidx && frc[f].flags… | |
-+ && frc[f].uni… | |
-+ break; | |
-+ } | |
-+ } | |
-+ | |
-+ /* Nothing was found. Use fontconfig … | |
-+ if (f >= frclen) { | |
-+ if (!font->set) | |
-+ font->set = FcFontSor… | |
-+ … | |
-+ fcsets[0] = font->set; | |
-+ | |
-+ /* | |
-+ * Nothing was found in the c… | |
-+ * some dozen of Fontconfig c… | |
-+ * font for one single charac… | |
-+ * | |
-+ * Xft and fontconfig are des… | |
-+ */ | |
-+ fcpattern = FcPatternDuplicat… | |
-+ fccharset = FcCharSetCreate(); | |
-+ | |
-+ FcCharSetAddChar(fccharset, r… | |
-+ FcPatternAddCharSet(fcpattern… | |
-+ fccharset); | |
-+ FcPatternAddBool(fcpattern, F… | |
-+ | |
-+ FcConfigSubstitute(0, fcpatte… | |
-+ FcMatchPatter… | |
-+ FcDefaultSubstitute(fcpattern… | |
-+ | |
-+ fontpattern = FcFontSetMatch(… | |
-+ fcpattern, &f… | |
-+ | |
-+ /* Allocate memory for the ne… | |
-+ if (frclen >= frccap) { | |
-+ frccap += 16; | |
-+ frc = xrealloc(frc, f… | |
-+ } | |
-+ | |
-+ frc[frclen].font = XftFontOpe… | |
-+ fontpattern); | |
-+ if (!frc[frclen].font) | |
-+ die("XftFontOpenPatte… | |
-+ strerror(errn… | |
-+ frc[frclen].flags = frcflags; | |
-+ frc[frclen].unicodep = rune; | |
-+ | |
-+ glyphidx = XftCharIndex(xw.dp… | |
-+ | |
-+ f = frclen; | |
-+ frclen++; | |
-+ | |
-+ FcPatternDestroy(fcpattern); | |
-+ FcCharSetDestroy(fccharset); | |
-+ } | |
-+ | |
-+ specs[numspecs].font = frc[f].font; | |
-+ specs[numspecs].glyph = glyphidx; | |
-+ specs[numspecs].x = (short)xp; | |
-+ specs[numspecs].y = (short)yp; | |
-+ numspecs++; | |
-+ } | |
- } | |
-- } | |
- | |
-- /* Nothing was found. Use fontconfig to find matching font. */ | |
-- if (f >= frclen) { | |
-- if (!font->set) | |
-- font->set = FcFontSort(0, font->pattern, | |
-- 1, 0, &fcres); | |
-- fcsets[0] = font->set; | |
-+ /* Cleanup and get ready for next segment. */ | |
-+ hbcleanup(&shaped); | |
-+ start = i; | |
- | |
-- /* | |
-- * Nothing was found in the cache. Now use | |
-- * some dozen of Fontconfig calls to get the | |
-- * font for one single character. | |
-- * | |
-- * Xft and fontconfig are design failures. | |
-- */ | |
-- fcpattern = FcPatternDuplicate(font->pattern); | |
-- fccharset = FcCharSetCreate(); | |
-- | |
-- FcCharSetAddChar(fccharset, rune); | |
-- FcPatternAddCharSet(fcpattern, FC_CHARSET, | |
-- fccharset); | |
-- FcPatternAddBool(fcpattern, FC_SCALABLE, 1); | |
-- | |
-- FcConfigSubstitute(0, fcpattern, | |
-- FcMatchPattern); | |
-- FcDefaultSubstitute(fcpattern); | |
-- | |
-- fontpattern = FcFontSetMatch(0, fcsets, 1, | |
-- fcpattern, &fcres); | |
-- | |
-- /* Allocate memory for the new cache entry. */ | |
-- if (frclen >= frccap) { | |
-- frccap += 16; | |
-- frc = xrealloc(frc, frccap * sizeof(Fontcache… | |
-+ /* Determine font for glyph if different from previou… | |
-+ if (prevmode != mode) { | |
-+ prevmode = mode; | |
-+ xresetfontsettings(mode, &font, &frcflags); | |
-+ yp = winy + font->ascent; | |
- } | |
-- | |
-- frc[frclen].font = XftFontOpenPattern(xw.dpy, | |
-- fontpattern); | |
-- if (!frc[frclen].font) | |
-- die("XftFontOpenPattern failed seeking fallba… | |
-- strerror(errno)); | |
-- frc[frclen].flags = frcflags; | |
-- frc[frclen].unicodep = rune; | |
-- | |
-- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run… | |
-- | |
-- f = frclen; | |
-- frclen++; | |
-- | |
-- FcPatternDestroy(fcpattern); | |
-- FcCharSetDestroy(fccharset); | |
- } | |
-- | |
-- specs[numspecs].font = frc[f].font; | |
-- specs[numspecs].glyph = glyphidx; | |
-- specs[numspecs].x = (short)xp; | |
-- specs[numspecs].y = (short)yp; | |
-- xp += runewidth; | |
-- numspecs++; | |
- } | |
- | |
- return numspecs; | |
- } | |
- | |
- void | |
--xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
-+xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
- { | |
-- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); | |
- int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, | |
- width = charlen * win.cw; | |
- Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; | |
-@@ -1527,21 +1576,24 @@ void | |
- xdrawglyph(Glyph g, int x, int y) | |
- { | |
- int numspecs; | |
-- XftGlyphFontSpec spec; | |
-+ XftGlyphFontSpec *specs = xw.specbuf; | |
- | |
-- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); | |
-- xdrawglyphfontspecs(&spec, g, numspecs, x, y); | |
-+ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y); | |
-+ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? … | |
- } | |
- | |
- void | |
--xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) | |
-+xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int… | |
- { | |
- Color drawcol; | |
- | |
- /* remove the old cursor */ | |
- if (selected(ox, oy)) | |
- og.mode ^= ATTR_REVERSE; | |
-- xdrawglyph(og, ox, oy); | |
-+ | |
-+ /* Redraw the line where cursor was previously. | |
-+ * It will restore the ligatures broken by the cursor. */ | |
-+ xdrawline(line, 0, oy, len); | |
- | |
- if (IS_SET(MODE_HIDE)) | |
- return; | |
-@@ -1669,18 +1721,16 @@ xdrawline(Line line, int x1, int y1, int x2) | |
- Glyph base, new; | |
- XftGlyphFontSpec *specs = xw.specbuf; | |
- | |
-- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); | |
- i = ox = 0; | |
-- for (x = x1; x < x2 && i < numspecs; x++) { | |
-+ for (x = x1; x < x2; x++) { | |
- new = line[x]; | |
- if (new.mode == ATTR_WDUMMY) | |
- continue; | |
- if (selected(x, y1)) | |
- new.mode ^= ATTR_REVERSE; | |
-- if (i > 0 && ATTRCMP(base, new)) { | |
-- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
-- specs += i; | |
-- numspecs -= i; | |
-+ if ((i > 0) && ATTRCMP(base, new)) { | |
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - … | |
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x … | |
- i = 0; | |
- } | |
- if (i == 0) { | |
-@@ -1689,8 +1739,10 @@ xdrawline(Line line, int x1, int y1, int x2) | |
- } | |
- i++; | |
- } | |
-- if (i > 0) | |
-- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
-+ if (i > 0) { | |
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,… | |
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox); | |
-+ } | |
- } | |
- | |
- void | |
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-scrollb… | |
@@ -0,0 +1,650 @@ | |
+diff --git a/Makefile b/Makefile | |
+index 15db421..dfcea0f 100644 | |
+--- a/Makefile | |
++++ b/Makefile | |
+@@ -3,9 +3,9 @@ | |
+ .POSIX: | |
+ | |
+ include config.mk | |
+ | |
+-SRC = st.c x.c | |
++SRC = st.c x.c hb.c | |
+ OBJ = $(SRC:.c=.o) | |
+ | |
+ all: st | |
+ | |
+@@ -15,9 +15,10 @@ config.h: | |
+ .c.o: | |
+ $(CC) $(STCFLAGS) -c $< | |
+ | |
+ st.o: config.h st.h win.h | |
+-x.o: arg.h config.h st.h win.h | |
++x.o: arg.h config.h st.h win.h hb.h | |
++hb.o: st.h | |
+ | |
+ $(OBJ): config.h config.mk | |
+ | |
+ st: $(OBJ) | |
+diff --git a/config.mk b/config.mk | |
+index 069a6c2..977b7c7 100644 | |
+--- a/config.mk | |
++++ b/config.mk | |
+@@ -14,12 +14,14 @@ PKG_CONFIG = pkg-config | |
+ | |
+ # includes and libs | |
+ INCS = -I$(X11INC) \ | |
+ `$(PKG_CONFIG) --cflags fontconfig` \ | |
+- `$(PKG_CONFIG) --cflags freetype2` | |
++ `$(PKG_CONFIG) --cflags freetype2` \ | |
++ `$(PKG_CONFIG) --cflags harfbuzz` | |
+ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\ | |
+ `$(PKG_CONFIG) --libs fontconfig` \ | |
+- `$(PKG_CONFIG) --libs freetype2` | |
++ `$(PKG_CONFIG) --libs freetype2` \ | |
++ `$(PKG_CONFIG) --libs harfbuzz` | |
+ | |
+ # flags | |
+ STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 | |
+ STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS) | |
+@@ -28,9 +30,10 @@ STLDFLAGS = $(LIBS) $(LDFLAGS) | |
+ # OpenBSD: | |
+ #CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE | |
+ #LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \ | |
+ # `$(PKG_CONFIG) --libs fontconfig` \ | |
+-# `$(PKG_CONFIG) --libs freetype2` | |
++# `$(PKG_CONFIG) --libs freetype2` \ | |
++# `$(PKG_CONFIG) --libs harfbuzz` | |
+ #MANPREFIX = ${PREFIX}/man | |
+ | |
+ # compiler and linker | |
+ # CC = c99 | |
+diff --git a/hb.c b/hb.c | |
+new file mode 100644 | |
+index 0000000..99412c8 | |
+--- /dev/null | |
++++ b/hb.c | |
+@@ -0,0 +1,125 @@ | |
++#include <stdlib.h> | |
++#include <stdio.h> | |
++#include <math.h> | |
++#include <X11/Xft/Xft.h> | |
++#include <X11/cursorfont.h> | |
++#include <hb.h> | |
++#include <hb-ft.h> | |
++ | |
++#include "st.h" | |
++#include "hb.h" | |
++ | |
++#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start… | |
++#define BUFFER_STEP 256 | |
++ | |
++hb_font_t *hbfindfont(XftFont *match); | |
++ | |
++typedef struct { | |
++ XftFont *match; | |
++ hb_font_t *font; | |
++} HbFontMatch; | |
++ | |
++typedef struct { | |
++ size_t capacity; | |
++ HbFontMatch *fonts; | |
++} HbFontCache; | |
++ | |
++static HbFontCache hbfontcache = { 0, NULL }; | |
++ | |
++typedef struct { | |
++ size_t capacity; | |
++ Rune *runes; | |
++} RuneBuffer; | |
++ | |
++static RuneBuffer hbrunebuffer = { 0, NULL }; | |
++ | |
++/* | |
++ * Poplulate the array with a list of font features, wrapped in FEATURE macro, | |
++ * e. g. | |
++ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') | |
++ */ | |
++hb_feature_t features[] = { }; | |
++ | |
++void | |
++hbunloadfonts() | |
++{ | |
++ for (int i = 0; i < hbfontcache.capacity; i++) { | |
++ hb_font_destroy(hbfontcache.fonts[i].font); | |
++ XftUnlockFace(hbfontcache.fonts[i].match); | |
++ } | |
++ | |
++ if (hbfontcache.fonts != NULL) { | |
++ free(hbfontcache.fonts); | |
++ hbfontcache.fonts = NULL; | |
++ } | |
++ hbfontcache.capacity = 0; | |
++} | |
++ | |
++hb_font_t * | |
++hbfindfont(XftFont *match) | |
++{ | |
++ for (int i = 0; i < hbfontcache.capacity; i++) { | |
++ if (hbfontcache.fonts[i].match == match) | |
++ return hbfontcache.fonts[i].font; | |
++ } | |
++ | |
++ /* Font not found in cache, caching it now. */ | |
++ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * … | |
++ FT_Face face = XftLockFace(match); | |
++ hb_font_t *font = hb_ft_font_create(face, NULL); | |
++ if (font == NULL) | |
++ die("Failed to load Harfbuzz font."); | |
++ | |
++ hbfontcache.fonts[hbfontcache.capacity].match = match; | |
++ hbfontcache.fonts[hbfontcache.capacity].font = font; | |
++ hbfontcache.capacity += 1; | |
++ | |
++ return font; | |
++} | |
++ | |
++void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, … | |
++ ushort mode = USHRT_MAX; | |
++ unsigned int glyph_count; | |
++ int rune_idx, glyph_idx, end = start + length; | |
++ | |
++ hb_font_t *font = hbfindfont(xfont); | |
++ if (font == NULL) | |
++ return; | |
++ | |
++ hb_buffer_t *buffer = hb_buffer_create(); | |
++ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); | |
++ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_… | |
++ | |
++ /* Resize the buffer if required length is larger. */ | |
++ if (hbrunebuffer.capacity < length) { | |
++ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S… | |
++ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer… | |
++ } | |
++ | |
++ /* Fill buffer with codepoints. */ | |
++ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r… | |
++ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u; | |
++ mode = glyphs[glyph_idx].mode; | |
++ if (mode & ATTR_WDUMMY) | |
++ hbrunebuffer.runes[rune_idx] = 0x0020; | |
++ } | |
++ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt… | |
++ | |
++ /* Shape the segment. */ | |
++ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t… | |
++ | |
++ /* Get new glyph info. */ | |
++ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun… | |
++ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly… | |
++ | |
++ /* Fill the output. */ | |
++ data->buffer = buffer; | |
++ data->glyphs = info; | |
++ data->positions = pos; | |
++ data->count = glyph_count; | |
++} | |
++ | |
++void hbcleanup(HbTransformData *data) { | |
++ hb_buffer_destroy(data->buffer); | |
++ memset(data, 0, sizeof(HbTransformData)); | |
++} | |
+diff --git a/hb.h b/hb.h | |
+new file mode 100644 | |
+index 0000000..3b0ef44 | |
+--- /dev/null | |
++++ b/hb.h | |
+@@ -0,0 +1,14 @@ | |
++#include <X11/Xft/Xft.h> | |
++#include <hb.h> | |
++#include <hb-ft.h> | |
++ | |
++typedef struct { | |
++ hb_buffer_t *buffer; | |
++ hb_glyph_info_t *glyphs; | |
++ hb_glyph_position_t *positions; | |
++ unsigned int count; | |
++} HbTransformData; | |
++ | |
++void hbunloadfonts(); | |
++void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int); | |
++void hbcleanup(HbTransformData *); | |
+diff --git a/st.c b/st.c | |
+index 2478942..bba90d3 100644 | |
+--- a/st.c | |
++++ b/st.c | |
+@@ -2729,9 +2729,10 @@ draw(void) | |
+ | |
+ drawregion(0, 0, term.col, term.row); | |
+ if (term.scr == 0) | |
+ xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], | |
+- term.ocx, term.ocy, term.line[term.ocy][term.… | |
++ term.ocx, term.ocy, term.line[term.ocy][term.… | |
++ term.line[term.ocy], term.col); | |
+ term.ocx = cx; | |
+ term.ocy = term.c.y; | |
+ xfinishdraw(); | |
+ if (ocx != term.ocx || ocy != term.ocy) | |
+diff --git a/st.h b/st.h | |
+index 78762a2..01eea49 100644 | |
+--- a/st.h | |
++++ b/st.h | |
+@@ -10,9 +10,10 @@ | |
+ #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) | |
+ #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) | |
+ #define DEFAULT(a, b) (a) = (a) ? (a) : (b) | |
+ #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)… | |
+-#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f… | |
++#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode … | |
++ (a).fg != (b).fg || \ | |
+ (a).bg != (b).bg) | |
+ #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ | |
+ (t1.tv_nsec-t2.tv_nsec)/1E6) | |
+ #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) | |
+diff --git a/win.h b/win.h | |
+index 6de960d..94679e4 100644 | |
+--- a/win.h | |
++++ b/win.h | |
+@@ -24,9 +24,9 @@ enum win_mode { | |
+ }; | |
+ | |
+ void xbell(void); | |
+ void xclipcopy(void); | |
+-void xdrawcursor(int, int, Glyph, int, int, Glyph); | |
++void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int); | |
+ void xdrawline(Line, int, int, int); | |
+ void xfinishdraw(void); | |
+ void xloadcols(void); | |
+ int xsetcolorname(int, const char *); | |
+diff --git a/x.c b/x.c | |
+index 1e12ac8..b0819ac 100644 | |
+--- a/x.c | |
++++ b/x.c | |
+@@ -18,8 +18,9 @@ | |
+ char *argv0; | |
+ #include "arg.h" | |
+ #include "st.h" | |
+ #include "win.h" | |
++#include "hb.h" | |
+ | |
+ /* types used in config.h */ | |
+ typedef struct { | |
+ uint mod; | |
+@@ -141,10 +142,11 @@ typedef struct { | |
+ GC gc; | |
+ } DC; | |
+ | |
+ static inline ushort sixd_to_16bit(int); | |
++static void xresetfontsettings(ushort mode, Font **font, int *frcflags); | |
+ static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i… | |
+-static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
++static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
+ static void xdrawglyph(Glyph, int, int); | |
+ static void xclear(int, int, int, int); | |
+ static int xgeommasktogravity(int); | |
+ static int ximopen(Display *); | |
+@@ -758,9 +760,9 @@ xresize(int col, int row) | |
+ XftDrawChange(xw.draw, xw.buf); | |
+ xclear(0, 0, win.w, win.h); | |
+ | |
+ /* resize to new width */ | |
+- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); | |
++ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4); | |
+ } | |
+ | |
+ ushort | |
+ sixd_to_16bit(int x) | |
+@@ -1070,8 +1072,11 @@ xunloadfont(Font *f) | |
+ | |
+ void | |
+ xunloadfonts(void) | |
+ { | |
++ /* Clear Harfbuzz font cache. */ | |
++ hbunloadfonts(); | |
++ | |
+ /* Free the loaded fonts in the font cache. */ | |
+ while (frclen > 0) | |
+ XftFontClose(xw.dpy, frc[--frclen].font); | |
+ | |
+@@ -1201,9 +1206,9 @@ xinit(int cols, int rows) | |
+ XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); | |
+ XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); | |
+ | |
+ /* font spec buffer */ | |
+- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); | |
++ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4); | |
+ | |
+ /* Xft rendering context */ | |
+ xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); | |
+ | |
+@@ -1255,144 +1260,155 @@ xinit(int cols, int rows) | |
+ if (xsel.xtarget == None) | |
+ xsel.xtarget = XA_STRING; | |
+ } | |
+ | |
++void | |
++xresetfontsettings(ushort mode, Font **font, int *frcflags) | |
++{ | |
++ *font = &dc.font; | |
++ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
++ *font = &dc.ibfont; | |
++ *frcflags = FRC_ITALICBOLD; | |
++ } else if (mode & ATTR_ITALIC) { | |
++ *font = &dc.ifont; | |
++ *frcflags = FRC_ITALIC; | |
++ } else if (mode & ATTR_BOLD) { | |
++ *font = &dc.bfont; | |
++ *frcflags = FRC_BOLD; | |
++ } | |
++} | |
++ | |
+ int | |
+ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in… | |
+ { | |
+ float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp,… | |
+- ushort mode, prevmode = USHRT_MAX; | |
++ ushort mode = glyphs[0].mode & ~ATTR_WRAP; | |
+ Font *font = &dc.font; | |
+ int frcflags = FRC_NORMAL; | |
+- float runewidth = win.cw; | |
++ float runewidth = win.cw * ((glyphs[0].mode & ATTR_WIDE) ? 2.0f : 1.0… | |
+ Rune rune; | |
+ FT_UInt glyphidx; | |
+ FcResult fcres; | |
+ FcPattern *fcpattern, *fontpattern; | |
+ FcFontSet *fcsets[] = { NULL }; | |
+ FcCharSet *fccharset; | |
+- int i, f, numspecs = 0; | |
++ int f, code_idx, numspecs = 0; | |
++ float cluster_xp = xp, cluster_yp = yp; | |
++ HbTransformData shaped = { 0 }; | |
+ | |
+- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { | |
+- /* Fetch rune and mode for current glyph. */ | |
+- rune = glyphs[i].u; | |
+- mode = glyphs[i].mode; | |
++ /* Initial values. */ | |
++ xresetfontsettings(mode, &font, &frcflags); | |
+ | |
+- /* Skip dummy wide-character spacing. */ | |
+- if (mode == ATTR_WDUMMY) | |
++ /* Shape the segment. */ | |
++ hbtransform(&shaped, font->match, glyphs, 0, len); | |
++ xp = winx; yp = winy + font->ascent; | |
++ cluster_xp = xp; cluster_yp = yp; | |
++ | |
++ for (code_idx = 0; code_idx < shaped.count; code_idx++) { | |
++ int idx = shaped.glyphs[code_idx].cluster; | |
++ | |
++ if (glyphs[idx].mode & ATTR_WDUMMY) | |
+ continue; | |
+ | |
+- /* Determine font for glyph if different from previous glyph.… | |
+- if (prevmode != mode) { | |
+- prevmode = mode; | |
+- font = &dc.font; | |
+- frcflags = FRC_NORMAL; | |
+- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0… | |
+- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
+- font = &dc.ibfont; | |
+- frcflags = FRC_ITALICBOLD; | |
+- } else if (mode & ATTR_ITALIC) { | |
+- font = &dc.ifont; | |
+- frcflags = FRC_ITALIC; | |
+- } else if (mode & ATTR_BOLD) { | |
+- font = &dc.bfont; | |
+- frcflags = FRC_BOLD; | |
+- } | |
+- yp = winy + font->ascent; | |
++ /* Advance the drawing cursor if we've moved to a new cluster… | |
++ if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluste… | |
++ xp += runewidth; | |
++ cluster_xp = xp; | |
++ cluster_yp = yp; | |
+ } | |
+ | |
+- /* Lookup character index with default font. */ | |
+- glyphidx = XftCharIndex(xw.dpy, font->match, rune); | |
+- if (glyphidx) { | |
++ if (shaped.glyphs[code_idx].codepoint != 0) { | |
++ /* If symbol is found, put it into the specs. */ | |
+ specs[numspecs].font = font->match; | |
+- specs[numspecs].glyph = glyphidx; | |
+- specs[numspecs].x = (short)xp; | |
+- specs[numspecs].y = (short)yp; | |
+- xp += runewidth; | |
++ specs[numspecs].glyph = shaped.glyphs[code_idx].codep… | |
++ specs[numspecs].x = cluster_xp + (short)(shaped.posit… | |
++ specs[numspecs].y = cluster_yp - (short)(shaped.posit… | |
++ cluster_xp += shaped.positions[code_idx].x_advance / … | |
++ cluster_yp += shaped.positions[code_idx].y_advance / … | |
+ numspecs++; | |
+- continue; | |
+- } | |
+- | |
+- /* Fallback on font cache, search the font cache for match. */ | |
+- for (f = 0; f < frclen; f++) { | |
+- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); | |
+- /* Everything correct. */ | |
+- if (glyphidx && frc[f].flags == frcflags) | |
+- break; | |
+- /* We got a default font for a not found glyph. */ | |
+- if (!glyphidx && frc[f].flags == frcflags | |
+- && frc[f].unicodep == rune) { | |
+- break; | |
++ } else { | |
++ /* If it's not found, try to fetch it through the fon… | |
++ rune = glyphs[idx].u; | |
++ for (f = 0; f < frclen; f++) { | |
++ glyphidx = XftCharIndex(xw.dpy, frc[f].font, … | |
++ /* Everything correct. */ | |
++ if (glyphidx && frc[f].flags == frcflags) | |
++ break; | |
++ /* We got a default font for a not found glyp… | |
++ if (!glyphidx && frc[f].flags == frcflags | |
++ && frc[f].unicodep == rune) { | |
++ break; | |
++ } | |
+ } | |
+- } | |
+ | |
+- /* Nothing was found. Use fontconfig to find matching font. */ | |
+- if (f >= frclen) { | |
+- if (!font->set) | |
+- font->set = FcFontSort(0, font->pattern, | |
+- 1, 0, &fcres); | |
+- fcsets[0] = font->set; | |
+- | |
+- /* | |
+- * Nothing was found in the cache. Now use | |
+- * some dozen of Fontconfig calls to get the | |
+- * font for one single character. | |
+- * | |
+- * Xft and fontconfig are design failures. | |
+- */ | |
+- fcpattern = FcPatternDuplicate(font->pattern); | |
+- fccharset = FcCharSetCreate(); | |
+- | |
+- FcCharSetAddChar(fccharset, rune); | |
+- FcPatternAddCharSet(fcpattern, FC_CHARSET, | |
+- fccharset); | |
+- FcPatternAddBool(fcpattern, FC_SCALABLE, 1); | |
+- | |
+- FcConfigSubstitute(0, fcpattern, | |
+- FcMatchPattern); | |
+- FcDefaultSubstitute(fcpattern); | |
+- | |
+- fontpattern = FcFontSetMatch(0, fcsets, 1, | |
+- fcpattern, &fcres); | |
+- | |
+- /* Allocate memory for the new cache entry. */ | |
+- if (frclen >= frccap) { | |
+- frccap += 16; | |
+- frc = xrealloc(frc, frccap * sizeof(Fontcache… | |
++ /* Nothing was found. Use fontconfig to find matching… | |
++ if (f >= frclen) { | |
++ if (!font->set) | |
++ font->set = FcFontSort(0, font->patte… | |
++ 1, 0, &fcres); | |
++ fcsets[0] = font->set; | |
++ | |
++ /* | |
++ * Nothing was found in the cache. Now use | |
++ * some dozen of Fontconfig calls to get the | |
++ * font for one single character. | |
++ * | |
++ * Xft and fontconfig are design failures. | |
++ */ | |
++ fcpattern = FcPatternDuplicate(font->pattern); | |
++ fccharset = FcCharSetCreate(); | |
++ | |
++ FcCharSetAddChar(fccharset, rune); | |
++ FcPatternAddCharSet(fcpattern, FC_CHARSET, | |
++ fccharset); | |
++ FcPatternAddBool(fcpattern, FC_SCALABLE, 1); | |
++ | |
++ FcConfigSubstitute(0, fcpattern, | |
++ FcMatchPattern); | |
++ FcDefaultSubstitute(fcpattern); | |
++ | |
++ fontpattern = FcFontSetMatch(0, fcsets, 1, | |
++ fcpattern, &fcres); | |
++ | |
++ /* Allocate memory for the new cache entry. */ | |
++ if (frclen >= frccap) { | |
++ frccap += 16; | |
++ frc = xrealloc(frc, frccap * sizeof(F… | |
++ } | |
++ | |
++ frc[frclen].font = XftFontOpenPattern(xw.dpy, | |
++ fontpattern); | |
++ if (!frc[frclen].font) | |
++ die("XftFontOpenPattern failed seekin… | |
++ strerror(errno)); | |
++ frc[frclen].flags = frcflags; | |
++ frc[frclen].unicodep = rune; | |
++ | |
++ glyphidx = XftCharIndex(xw.dpy, frc[frclen].f… | |
++ | |
++ f = frclen; | |
++ frclen++; | |
++ | |
++ FcPatternDestroy(fcpattern); | |
++ FcCharSetDestroy(fccharset); | |
+ } | |
+ | |
+- frc[frclen].font = XftFontOpenPattern(xw.dpy, | |
+- fontpattern); | |
+- if (!frc[frclen].font) | |
+- die("XftFontOpenPattern failed seeking fallba… | |
+- strerror(errno)); | |
+- frc[frclen].flags = frcflags; | |
+- frc[frclen].unicodep = rune; | |
+- | |
+- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run… | |
+- | |
+- f = frclen; | |
+- frclen++; | |
+- | |
+- FcPatternDestroy(fcpattern); | |
+- FcCharSetDestroy(fccharset); | |
++ specs[numspecs].font = frc[f].font; | |
++ specs[numspecs].glyph = glyphidx; | |
++ specs[numspecs].x = (short)xp; | |
++ specs[numspecs].y = (short)yp; | |
++ numspecs++; | |
+ } | |
+- | |
+- specs[numspecs].font = frc[f].font; | |
+- specs[numspecs].glyph = glyphidx; | |
+- specs[numspecs].x = (short)xp; | |
+- specs[numspecs].y = (short)yp; | |
+- xp += runewidth; | |
+- numspecs++; | |
+ } | |
+ | |
++ /* Cleanup and get ready for next segment. */ | |
++ hbcleanup(&shaped); | |
+ return numspecs; | |
+ } | |
+ | |
+ void | |
+-xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
++xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
+ { | |
+- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); | |
+ int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, | |
+ width = charlen * win.cw; | |
+ Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; | |
+ XRenderColor colfg, colbg; | |
+@@ -1526,23 +1542,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Gly… | |
+ void | |
+ xdrawglyph(Glyph g, int x, int y) | |
+ { | |
+ int numspecs; | |
+- XftGlyphFontSpec spec; | |
++ XftGlyphFontSpec *specs = xw.specbuf; | |
+ | |
+- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); | |
+- xdrawglyphfontspecs(&spec, g, numspecs, x, y); | |
++ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y); | |
++ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? … | |
+ } | |
+ | |
+ void | |
+-xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) | |
++xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int… | |
+ { | |
+ Color drawcol; | |
+ | |
+ /* remove the old cursor */ | |
+ if (selected(ox, oy)) | |
+ og.mode ^= ATTR_REVERSE; | |
+- xdrawglyph(og, ox, oy); | |
++ | |
++ /* Redraw the line where cursor was previously. | |
++ * It will restore the ligatures broken by the cursor. */ | |
++ xdrawline(line, 0, oy, len); | |
+ | |
+ if (IS_SET(MODE_HIDE)) | |
+ return; | |
+ | |
+@@ -1674,30 +1693,30 @@ xdrawline(Line line, int x1, int y1, int x2) | |
+ int i, x, ox, numspecs; | |
+ Glyph base, new; | |
+ XftGlyphFontSpec *specs = xw.specbuf; | |
+ | |
+- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); | |
+ i = ox = 0; | |
+- for (x = x1; x < x2 && i < numspecs; x++) { | |
++ for (x = x1; x < x2; x++) { | |
+ new = line[x]; | |
+ if (new.mode == ATTR_WDUMMY) | |
+ continue; | |
+ if (selected(x, y1)) | |
+ new.mode ^= ATTR_REVERSE; | |
+- if (i > 0 && ATTRCMP(base, new)) { | |
+- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
+- specs += i; | |
+- numspecs -= i; | |
++ if ((i > 0) && ATTRCMP(base, new)) { | |
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - … | |
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x … | |
+ i = 0; | |
+ } | |
+ if (i == 0) { | |
+ ox = x; | |
+ base = new; | |
+ } | |
+ i++; | |
+ } | |
+- if (i > 0) | |
+- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
++ if (i > 0) { | |
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,… | |
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox); | |
++ } | |
+ } | |
+ | |
+ void | |
+ xfinishdraw(void) | |
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-scrollb… | |
@@ -1,635 +0,0 @@ | |
-diff --git a/Makefile b/Makefile | |
-index 470ac86..38240da 100644 | |
---- a/Makefile | |
-+++ b/Makefile | |
-@@ -4,7 +4,7 @@ | |
- | |
- include config.mk | |
- | |
--SRC = st.c x.c | |
-+SRC = st.c x.c hb.c | |
- OBJ = $(SRC:.c=.o) | |
- | |
- all: st | |
-@@ -22,7 +22,8 @@ config.h: | |
- $(CC) $(STCFLAGS) -c $< | |
- | |
- st.o: config.h st.h win.h | |
--x.o: arg.h config.h st.h win.h | |
-+x.o: arg.h config.h st.h win.h hb.h | |
-+hb.o: st.h | |
- | |
- $(OBJ): config.h config.mk | |
- | |
-diff --git a/config.mk b/config.mk | |
-index 47c615e..d7439a3 100644 | |
---- a/config.mk | |
-+++ b/config.mk | |
-@@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config | |
- # includes and libs | |
- INCS = -I$(X11INC) \ | |
- `$(PKG_CONFIG) --cflags fontconfig` \ | |
-- `$(PKG_CONFIG) --cflags freetype2` | |
--LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\ | |
-+ `$(PKG_CONFIG) --cflags freetype2` \ | |
-+ `$(PKG_CONFIG) --cflags harfbuzz` | |
-+LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender \ | |
- `$(PKG_CONFIG) --libs fontconfig` \ | |
-- `$(PKG_CONFIG) --libs freetype2` | |
-+ `$(PKG_CONFIG) --libs freetype2` \ | |
-+ `$(PKG_CONFIG) --libs harfbuzz` | |
- | |
- # flags | |
- STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 | |
-diff --git a/hb.c b/hb.c | |
-new file mode 100644 | |
-index 0000000..99412c8 | |
---- /dev/null | |
-+++ b/hb.c | |
-@@ -0,0 +1,125 @@ | |
-+#include <stdlib.h> | |
-+#include <stdio.h> | |
-+#include <math.h> | |
-+#include <X11/Xft/Xft.h> | |
-+#include <X11/cursorfont.h> | |
-+#include <hb.h> | |
-+#include <hb-ft.h> | |
-+ | |
-+#include "st.h" | |
-+#include "hb.h" | |
-+ | |
-+#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start… | |
-+#define BUFFER_STEP 256 | |
-+ | |
-+hb_font_t *hbfindfont(XftFont *match); | |
-+ | |
-+typedef struct { | |
-+ XftFont *match; | |
-+ hb_font_t *font; | |
-+} HbFontMatch; | |
-+ | |
-+typedef struct { | |
-+ size_t capacity; | |
-+ HbFontMatch *fonts; | |
-+} HbFontCache; | |
-+ | |
-+static HbFontCache hbfontcache = { 0, NULL }; | |
-+ | |
-+typedef struct { | |
-+ size_t capacity; | |
-+ Rune *runes; | |
-+} RuneBuffer; | |
-+ | |
-+static RuneBuffer hbrunebuffer = { 0, NULL }; | |
-+ | |
-+/* | |
-+ * Poplulate the array with a list of font features, wrapped in FEATURE macro, | |
-+ * e. g. | |
-+ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') | |
-+ */ | |
-+hb_feature_t features[] = { }; | |
-+ | |
-+void | |
-+hbunloadfonts() | |
-+{ | |
-+ for (int i = 0; i < hbfontcache.capacity; i++) { | |
-+ hb_font_destroy(hbfontcache.fonts[i].font); | |
-+ XftUnlockFace(hbfontcache.fonts[i].match); | |
-+ } | |
-+ | |
-+ if (hbfontcache.fonts != NULL) { | |
-+ free(hbfontcache.fonts); | |
-+ hbfontcache.fonts = NULL; | |
-+ } | |
-+ hbfontcache.capacity = 0; | |
-+} | |
-+ | |
-+hb_font_t * | |
-+hbfindfont(XftFont *match) | |
-+{ | |
-+ for (int i = 0; i < hbfontcache.capacity; i++) { | |
-+ if (hbfontcache.fonts[i].match == match) | |
-+ return hbfontcache.fonts[i].font; | |
-+ } | |
-+ | |
-+ /* Font not found in cache, caching it now. */ | |
-+ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * … | |
-+ FT_Face face = XftLockFace(match); | |
-+ hb_font_t *font = hb_ft_font_create(face, NULL); | |
-+ if (font == NULL) | |
-+ die("Failed to load Harfbuzz font."); | |
-+ | |
-+ hbfontcache.fonts[hbfontcache.capacity].match = match; | |
-+ hbfontcache.fonts[hbfontcache.capacity].font = font; | |
-+ hbfontcache.capacity += 1; | |
-+ | |
-+ return font; | |
-+} | |
-+ | |
-+void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, … | |
-+ ushort mode = USHRT_MAX; | |
-+ unsigned int glyph_count; | |
-+ int rune_idx, glyph_idx, end = start + length; | |
-+ | |
-+ hb_font_t *font = hbfindfont(xfont); | |
-+ if (font == NULL) | |
-+ return; | |
-+ | |
-+ hb_buffer_t *buffer = hb_buffer_create(); | |
-+ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); | |
-+ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_… | |
-+ | |
-+ /* Resize the buffer if required length is larger. */ | |
-+ if (hbrunebuffer.capacity < length) { | |
-+ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S… | |
-+ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer… | |
-+ } | |
-+ | |
-+ /* Fill buffer with codepoints. */ | |
-+ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r… | |
-+ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u; | |
-+ mode = glyphs[glyph_idx].mode; | |
-+ if (mode & ATTR_WDUMMY) | |
-+ hbrunebuffer.runes[rune_idx] = 0x0020; | |
-+ } | |
-+ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt… | |
-+ | |
-+ /* Shape the segment. */ | |
-+ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t… | |
-+ | |
-+ /* Get new glyph info. */ | |
-+ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun… | |
-+ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly… | |
-+ | |
-+ /* Fill the output. */ | |
-+ data->buffer = buffer; | |
-+ data->glyphs = info; | |
-+ data->positions = pos; | |
-+ data->count = glyph_count; | |
-+} | |
-+ | |
-+void hbcleanup(HbTransformData *data) { | |
-+ hb_buffer_destroy(data->buffer); | |
-+ memset(data, 0, sizeof(HbTransformData)); | |
-+} | |
-diff --git a/hb.h b/hb.h | |
-new file mode 100644 | |
-index 0000000..3b0ef44 | |
---- /dev/null | |
-+++ b/hb.h | |
-@@ -0,0 +1,14 @@ | |
-+#include <X11/Xft/Xft.h> | |
-+#include <hb.h> | |
-+#include <hb-ft.h> | |
-+ | |
-+typedef struct { | |
-+ hb_buffer_t *buffer; | |
-+ hb_glyph_info_t *glyphs; | |
-+ hb_glyph_position_t *positions; | |
-+ unsigned int count; | |
-+} HbTransformData; | |
-+ | |
-+void hbunloadfonts(); | |
-+void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int); | |
-+void hbcleanup(HbTransformData *); | |
-diff --git a/st.c b/st.c | |
-index c44797b..18aa1bf 100644 | |
---- a/st.c | |
-+++ b/st.c | |
-@@ -2759,7 +2759,9 @@ draw(void) | |
- drawregion(0, 0, term.col, term.row); | |
- if (TSCREEN.off == 0) | |
- xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx], | |
-- term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]… | |
-+ term.ocx, term.ocy, TLINE(term.ocy)[term.ocx], | |
-+ TLINE(term.ocy), term.col); | |
-+ | |
- term.ocx = cx; | |
- term.ocy = term.c.y; | |
- xfinishdraw(); | |
-diff --git a/st.h b/st.h | |
-index 073851a..d0b071d 100644 | |
---- a/st.h | |
-+++ b/st.h | |
-@@ -11,7 +11,8 @@ | |
- #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) | |
- #define DEFAULT(a, b) (a) = (a) ? (a) : (b) | |
- #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)… | |
--#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f… | |
-+#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode … | |
-+ (a).fg != (b).fg || \ | |
- (a).bg != (b).bg) | |
- #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ | |
- (t1.tv_nsec-t2.tv_nsec)/1E6) | |
-diff --git a/win.h b/win.h | |
-index 6de960d..94679e4 100644 | |
---- a/win.h | |
-+++ b/win.h | |
-@@ -25,7 +25,7 @@ enum win_mode { | |
- | |
- void xbell(void); | |
- void xclipcopy(void); | |
--void xdrawcursor(int, int, Glyph, int, int, Glyph); | |
-+void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int); | |
- void xdrawline(Line, int, int, int); | |
- void xfinishdraw(void); | |
- void xloadcols(void); | |
-diff --git a/x.c b/x.c | |
-index b81f5be..c1611bb 100644 | |
---- a/x.c | |
-+++ b/x.c | |
-@@ -19,6 +19,7 @@ char *argv0; | |
- #include "arg.h" | |
- #include "st.h" | |
- #include "win.h" | |
-+#include "hb.h" | |
- | |
- /* types used in config.h */ | |
- typedef struct { | |
-@@ -144,8 +145,9 @@ typedef struct { | |
- } DC; | |
- | |
- static inline ushort sixd_to_16bit(int); | |
-+static void xresetfontsettings(ushort mode, Font **font, int *frcflags); | |
- static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i… | |
--static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
-+static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
- static void xdrawglyph(Glyph, int, int); | |
- static void xclear(int, int, int, int); | |
- static int xgeommasktogravity(int); | |
-@@ -761,7 +763,7 @@ xresize(int col, int row) | |
- xclear(0, 0, win.w, win.h); | |
- | |
- /* resize to new width */ | |
-- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); | |
-+ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4); | |
- } | |
- | |
- ushort | |
-@@ -1073,6 +1075,9 @@ xunloadfont(Font *f) | |
- void | |
- xunloadfonts(void) | |
- { | |
-+ /* Clear Harfbuzz font cache. */ | |
-+ hbunloadfonts(); | |
-+ | |
- /* Free the loaded fonts in the font cache. */ | |
- while (frclen > 0) | |
- XftFontClose(xw.dpy, frc[--frclen].font); | |
-@@ -1204,7 +1209,7 @@ xinit(int cols, int rows) | |
- XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); | |
- | |
- /* font spec buffer */ | |
-- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); | |
-+ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4); | |
- | |
- /* Xft rendering context */ | |
- xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); | |
-@@ -1258,6 +1263,22 @@ xinit(int cols, int rows) | |
- xsel.xtarget = XA_STRING; | |
- } | |
- | |
-+void | |
-+xresetfontsettings(ushort mode, Font **font, int *frcflags) | |
-+{ | |
-+ *font = &dc.font; | |
-+ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
-+ *font = &dc.ibfont; | |
-+ *frcflags = FRC_ITALICBOLD; | |
-+ } else if (mode & ATTR_ITALIC) { | |
-+ *font = &dc.ifont; | |
-+ *frcflags = FRC_ITALIC; | |
-+ } else if (mode & ATTR_BOLD) { | |
-+ *font = &dc.bfont; | |
-+ *frcflags = FRC_BOLD; | |
-+ } | |
-+} | |
-+ | |
- int | |
- xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in… | |
- { | |
-@@ -1272,128 +1293,156 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const G… | |
- FcPattern *fcpattern, *fontpattern; | |
- FcFontSet *fcsets[] = { NULL }; | |
- FcCharSet *fccharset; | |
-- int i, f, numspecs = 0; | |
-+ int i, f, length = 0, start = 0, numspecs = 0; | |
-+ float cluster_xp = xp, cluster_yp = yp; | |
-+ HbTransformData shaped = { 0 }; | |
-+ | |
-+ /* Initial values. */ | |
-+ mode = prevmode = glyphs[0].mode & ~ATTR_WRAP; | |
-+ xresetfontsettings(mode, &font, &frcflags); | |
- | |
- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { | |
-- /* Fetch rune and mode for current glyph. */ | |
-- rune = glyphs[i].u; | |
-- mode = glyphs[i].mode; | |
-+ mode = glyphs[i].mode & ~ATTR_WRAP; | |
- | |
- /* Skip dummy wide-character spacing. */ | |
-- if (mode == ATTR_WDUMMY) | |
-+ if (mode & ATTR_WDUMMY && i < (len - 1)) | |
- continue; | |
- | |
-- /* Determine font for glyph if different from previous glyph.… | |
-- if (prevmode != mode) { | |
-- prevmode = mode; | |
-- font = &dc.font; | |
-- frcflags = FRC_NORMAL; | |
-- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0… | |
-- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
-- font = &dc.ibfont; | |
-- frcflags = FRC_ITALICBOLD; | |
-- } else if (mode & ATTR_ITALIC) { | |
-- font = &dc.ifont; | |
-- frcflags = FRC_ITALIC; | |
-- } else if (mode & ATTR_BOLD) { | |
-- font = &dc.bfont; | |
-- frcflags = FRC_BOLD; | |
-+ if ( | |
-+ prevmode != mode | |
-+ || ATTRCMP(glyphs[start], glyphs[i]) | |
-+ || selected(x + i, y) != selected(x + start, y) | |
-+ || i == (len - 1) | |
-+ ) { | |
-+ /* Handle 1-character wide segments and end of line */ | |
-+ length = i - start; | |
-+ if (i == start) { | |
-+ length = 1; | |
-+ } else if (i == (len - 1)) { | |
-+ length = (i - start + 1); | |
- } | |
-- yp = winy + font->ascent; | |
-- } | |
- | |
-- /* Lookup character index with default font. */ | |
-- glyphidx = XftCharIndex(xw.dpy, font->match, rune); | |
-- if (glyphidx) { | |
-- specs[numspecs].font = font->match; | |
-- specs[numspecs].glyph = glyphidx; | |
-- specs[numspecs].x = (short)xp; | |
-- specs[numspecs].y = (short)yp; | |
-- xp += runewidth; | |
-- numspecs++; | |
-- continue; | |
-- } | |
-- | |
-- /* Fallback on font cache, search the font cache for match. */ | |
-- for (f = 0; f < frclen; f++) { | |
-- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); | |
-- /* Everything correct. */ | |
-- if (glyphidx && frc[f].flags == frcflags) | |
-- break; | |
-- /* We got a default font for a not found glyph. */ | |
-- if (!glyphidx && frc[f].flags == frcflags | |
-- && frc[f].unicodep == rune) { | |
-- break; | |
-+ /* Shape the segment. */ | |
-+ hbtransform(&shaped, font->match, glyphs, start, leng… | |
-+ runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE… | |
-+ cluster_xp = xp; cluster_yp = yp; | |
-+ for (int code_idx = 0; code_idx < shaped.count; code_… | |
-+ int idx = shaped.glyphs[code_idx].cluster; | |
-+ | |
-+ if (glyphs[start + idx].mode & ATTR_WDUMMY) | |
-+ continue; | |
-+ | |
-+ /* Advance the drawing cursor if we've moved … | |
-+ if (code_idx > 0 && idx != shaped.glyphs[code… | |
-+ xp += runewidth; | |
-+ cluster_xp = xp; | |
-+ cluster_yp = yp; | |
-+ runewidth = win.cw * ((glyphs[start +… | |
-+ } | |
-+ | |
-+ if (shaped.glyphs[code_idx].codepoint != 0) { | |
-+ /* If symbol is found, put it into th… | |
-+ specs[numspecs].font = font->match; | |
-+ specs[numspecs].glyph = shaped.glyphs… | |
-+ specs[numspecs].x = cluster_xp + (sho… | |
-+ specs[numspecs].y = cluster_yp - (sho… | |
-+ cluster_xp += shaped.positions[code_i… | |
-+ cluster_yp += shaped.positions[code_i… | |
-+ numspecs++; | |
-+ } else { | |
-+ /* If it's not found, try to fetch it… | |
-+ rune = glyphs[start + idx].u; | |
-+ for (f = 0; f < frclen; f++) { | |
-+ glyphidx = XftCharIndex(xw.dp… | |
-+ /* Everything correct. */ | |
-+ if (glyphidx && frc[f].flags … | |
-+ break; | |
-+ /* We got a default font for … | |
-+ if (!glyphidx && frc[f].flags… | |
-+ && frc[f].uni… | |
-+ break; | |
-+ } | |
-+ } | |
-+ | |
-+ /* Nothing was found. Use fontconfig … | |
-+ if (f >= frclen) { | |
-+ if (!font->set) | |
-+ font->set = FcFontSor… | |
-+ … | |
-+ fcsets[0] = font->set; | |
-+ | |
-+ /* | |
-+ * Nothing was found in the c… | |
-+ * some dozen of Fontconfig c… | |
-+ * font for one single charac… | |
-+ * | |
-+ * Xft and fontconfig are des… | |
-+ */ | |
-+ fcpattern = FcPatternDuplicat… | |
-+ fccharset = FcCharSetCreate(); | |
-+ | |
-+ FcCharSetAddChar(fccharset, r… | |
-+ FcPatternAddCharSet(fcpattern… | |
-+ fccharset); | |
-+ FcPatternAddBool(fcpattern, F… | |
-+ | |
-+ FcConfigSubstitute(0, fcpatte… | |
-+ FcMatchPatter… | |
-+ FcDefaultSubstitute(fcpattern… | |
-+ | |
-+ fontpattern = FcFontSetMatch(… | |
-+ fcpattern, &f… | |
-+ | |
-+ /* Allocate memory for the ne… | |
-+ if (frclen >= frccap) { | |
-+ frccap += 16; | |
-+ frc = xrealloc(frc, f… | |
-+ } | |
-+ | |
-+ frc[frclen].font = XftFontOpe… | |
-+ fontpattern); | |
-+ if (!frc[frclen].font) | |
-+ die("XftFontOpenPatte… | |
-+ strerror(errn… | |
-+ frc[frclen].flags = frcflags; | |
-+ frc[frclen].unicodep = rune; | |
-+ | |
-+ glyphidx = XftCharIndex(xw.dp… | |
-+ | |
-+ f = frclen; | |
-+ frclen++; | |
-+ | |
-+ FcPatternDestroy(fcpattern); | |
-+ FcCharSetDestroy(fccharset); | |
-+ } | |
-+ | |
-+ specs[numspecs].font = frc[f].font; | |
-+ specs[numspecs].glyph = glyphidx; | |
-+ specs[numspecs].x = (short)xp; | |
-+ specs[numspecs].y = (short)yp; | |
-+ numspecs++; | |
-+ } | |
- } | |
-- } | |
- | |
-- /* Nothing was found. Use fontconfig to find matching font. */ | |
-- if (f >= frclen) { | |
-- if (!font->set) | |
-- font->set = FcFontSort(0, font->pattern, | |
-- 1, 0, &fcres); | |
-- fcsets[0] = font->set; | |
-+ /* Cleanup and get ready for next segment. */ | |
-+ hbcleanup(&shaped); | |
-+ start = i; | |
- | |
-- /* | |
-- * Nothing was found in the cache. Now use | |
-- * some dozen of Fontconfig calls to get the | |
-- * font for one single character. | |
-- * | |
-- * Xft and fontconfig are design failures. | |
-- */ | |
-- fcpattern = FcPatternDuplicate(font->pattern); | |
-- fccharset = FcCharSetCreate(); | |
-- | |
-- FcCharSetAddChar(fccharset, rune); | |
-- FcPatternAddCharSet(fcpattern, FC_CHARSET, | |
-- fccharset); | |
-- FcPatternAddBool(fcpattern, FC_SCALABLE, 1); | |
-- | |
-- FcConfigSubstitute(0, fcpattern, | |
-- FcMatchPattern); | |
-- FcDefaultSubstitute(fcpattern); | |
-- | |
-- fontpattern = FcFontSetMatch(0, fcsets, 1, | |
-- fcpattern, &fcres); | |
-- | |
-- /* Allocate memory for the new cache entry. */ | |
-- if (frclen >= frccap) { | |
-- frccap += 16; | |
-- frc = xrealloc(frc, frccap * sizeof(Fontcache… | |
-+ /* Determine font for glyph if different from previou… | |
-+ if (prevmode != mode) { | |
-+ prevmode = mode; | |
-+ xresetfontsettings(mode, &font, &frcflags); | |
-+ yp = winy + font->ascent; | |
- } | |
-- | |
-- frc[frclen].font = XftFontOpenPattern(xw.dpy, | |
-- fontpattern); | |
-- if (!frc[frclen].font) | |
-- die("XftFontOpenPattern failed seeking fallba… | |
-- strerror(errno)); | |
-- frc[frclen].flags = frcflags; | |
-- frc[frclen].unicodep = rune; | |
-- | |
-- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run… | |
-- | |
-- f = frclen; | |
-- frclen++; | |
-- | |
-- FcPatternDestroy(fcpattern); | |
-- FcCharSetDestroy(fccharset); | |
- } | |
-- | |
-- specs[numspecs].font = frc[f].font; | |
-- specs[numspecs].glyph = glyphidx; | |
-- specs[numspecs].x = (short)xp; | |
-- specs[numspecs].y = (short)yp; | |
-- xp += runewidth; | |
-- numspecs++; | |
- } | |
- | |
- return numspecs; | |
- } | |
- | |
- void | |
--xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
-+xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
- { | |
-- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); | |
- int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, | |
- width = charlen * win.cw; | |
- Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; | |
-@@ -1529,21 +1578,24 @@ void | |
- xdrawglyph(Glyph g, int x, int y) | |
- { | |
- int numspecs; | |
-- XftGlyphFontSpec spec; | |
-+ XftGlyphFontSpec *specs = xw.specbuf; | |
- | |
-- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); | |
-- xdrawglyphfontspecs(&spec, g, numspecs, x, y); | |
-+ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y); | |
-+ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? … | |
- } | |
- | |
- void | |
--xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) | |
-+xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int… | |
- { | |
- Color drawcol; | |
- | |
- /* remove the old cursor */ | |
- if (selected(ox, oy)) | |
- og.mode ^= ATTR_REVERSE; | |
-- xdrawglyph(og, ox, oy); | |
-+ | |
-+ /* Redraw the line where cursor was previously. | |
-+ * It will restore the ligatures broken by the cursor. */ | |
-+ xdrawline(line, 0, oy, len); | |
- | |
- if (IS_SET(MODE_HIDE)) | |
- return; | |
-@@ -1671,18 +1723,16 @@ xdrawline(Line line, int x1, int y1, int x2) | |
- Glyph base, new; | |
- XftGlyphFontSpec *specs = xw.specbuf; | |
- | |
-- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); | |
- i = ox = 0; | |
-- for (x = x1; x < x2 && i < numspecs; x++) { | |
-+ for (x = x1; x < x2; x++) { | |
- new = line[x]; | |
- if (new.mode == ATTR_WDUMMY) | |
- continue; | |
- if (selected(x, y1)) | |
- new.mode ^= ATTR_REVERSE; | |
-- if (i > 0 && ATTRCMP(base, new)) { | |
-- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
-- specs += i; | |
-- numspecs -= i; | |
-+ if ((i > 0) && ATTRCMP(base, new)) { | |
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - … | |
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x … | |
- i = 0; | |
- } | |
- if (i == 0) { | |
-@@ -1691,8 +1741,10 @@ xdrawline(Line line, int x1, int y1, int x2) | |
- } | |
- i++; | |
- } | |
-- if (i > 0) | |
-- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
-+ if (i > 0) { | |
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,… | |
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox); | |
-+ } | |
- } | |
- | |
- void | |
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-alpha-scrollb… | |
@@ -0,0 +1,650 @@ | |
+diff --git a/Makefile b/Makefile | |
+index 15db421..dfcea0f 100644 | |
+--- a/Makefile | |
++++ b/Makefile | |
+@@ -3,9 +3,9 @@ | |
+ .POSIX: | |
+ | |
+ include config.mk | |
+ | |
+-SRC = st.c x.c | |
++SRC = st.c x.c hb.c | |
+ OBJ = $(SRC:.c=.o) | |
+ | |
+ all: st | |
+ | |
+@@ -15,9 +15,10 @@ config.h: | |
+ .c.o: | |
+ $(CC) $(STCFLAGS) -c $< | |
+ | |
+ st.o: config.h st.h win.h | |
+-x.o: arg.h config.h st.h win.h | |
++x.o: arg.h config.h st.h win.h hb.h | |
++hb.o: st.h | |
+ | |
+ $(OBJ): config.h config.mk | |
+ | |
+ st: $(OBJ) | |
+diff --git a/config.mk b/config.mk | |
+index 069a6c2..977b7c7 100644 | |
+--- a/config.mk | |
++++ b/config.mk | |
+@@ -14,12 +14,14 @@ PKG_CONFIG = pkg-config | |
+ | |
+ # includes and libs | |
+ INCS = -I$(X11INC) \ | |
+ `$(PKG_CONFIG) --cflags fontconfig` \ | |
+- `$(PKG_CONFIG) --cflags freetype2` | |
++ `$(PKG_CONFIG) --cflags freetype2` \ | |
++ `$(PKG_CONFIG) --cflags harfbuzz` | |
+ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\ | |
+ `$(PKG_CONFIG) --libs fontconfig` \ | |
+- `$(PKG_CONFIG) --libs freetype2` | |
++ `$(PKG_CONFIG) --libs freetype2` \ | |
++ `$(PKG_CONFIG) --libs harfbuzz` | |
+ | |
+ # flags | |
+ STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 | |
+ STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS) | |
+@@ -28,9 +30,10 @@ STLDFLAGS = $(LIBS) $(LDFLAGS) | |
+ # OpenBSD: | |
+ #CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE | |
+ #LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \ | |
+ # `$(PKG_CONFIG) --libs fontconfig` \ | |
+-# `$(PKG_CONFIG) --libs freetype2` | |
++# `$(PKG_CONFIG) --libs freetype2` \ | |
++# `$(PKG_CONFIG) --libs harfbuzz` | |
+ #MANPREFIX = ${PREFIX}/man | |
+ | |
+ # compiler and linker | |
+ # CC = c99 | |
+diff --git a/hb.c b/hb.c | |
+new file mode 100644 | |
+index 0000000..99412c8 | |
+--- /dev/null | |
++++ b/hb.c | |
+@@ -0,0 +1,125 @@ | |
++#include <stdlib.h> | |
++#include <stdio.h> | |
++#include <math.h> | |
++#include <X11/Xft/Xft.h> | |
++#include <X11/cursorfont.h> | |
++#include <hb.h> | |
++#include <hb-ft.h> | |
++ | |
++#include "st.h" | |
++#include "hb.h" | |
++ | |
++#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start… | |
++#define BUFFER_STEP 256 | |
++ | |
++hb_font_t *hbfindfont(XftFont *match); | |
++ | |
++typedef struct { | |
++ XftFont *match; | |
++ hb_font_t *font; | |
++} HbFontMatch; | |
++ | |
++typedef struct { | |
++ size_t capacity; | |
++ HbFontMatch *fonts; | |
++} HbFontCache; | |
++ | |
++static HbFontCache hbfontcache = { 0, NULL }; | |
++ | |
++typedef struct { | |
++ size_t capacity; | |
++ Rune *runes; | |
++} RuneBuffer; | |
++ | |
++static RuneBuffer hbrunebuffer = { 0, NULL }; | |
++ | |
++/* | |
++ * Poplulate the array with a list of font features, wrapped in FEATURE macro, | |
++ * e. g. | |
++ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') | |
++ */ | |
++hb_feature_t features[] = { }; | |
++ | |
++void | |
++hbunloadfonts() | |
++{ | |
++ for (int i = 0; i < hbfontcache.capacity; i++) { | |
++ hb_font_destroy(hbfontcache.fonts[i].font); | |
++ XftUnlockFace(hbfontcache.fonts[i].match); | |
++ } | |
++ | |
++ if (hbfontcache.fonts != NULL) { | |
++ free(hbfontcache.fonts); | |
++ hbfontcache.fonts = NULL; | |
++ } | |
++ hbfontcache.capacity = 0; | |
++} | |
++ | |
++hb_font_t * | |
++hbfindfont(XftFont *match) | |
++{ | |
++ for (int i = 0; i < hbfontcache.capacity; i++) { | |
++ if (hbfontcache.fonts[i].match == match) | |
++ return hbfontcache.fonts[i].font; | |
++ } | |
++ | |
++ /* Font not found in cache, caching it now. */ | |
++ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * … | |
++ FT_Face face = XftLockFace(match); | |
++ hb_font_t *font = hb_ft_font_create(face, NULL); | |
++ if (font == NULL) | |
++ die("Failed to load Harfbuzz font."); | |
++ | |
++ hbfontcache.fonts[hbfontcache.capacity].match = match; | |
++ hbfontcache.fonts[hbfontcache.capacity].font = font; | |
++ hbfontcache.capacity += 1; | |
++ | |
++ return font; | |
++} | |
++ | |
++void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, … | |
++ ushort mode = USHRT_MAX; | |
++ unsigned int glyph_count; | |
++ int rune_idx, glyph_idx, end = start + length; | |
++ | |
++ hb_font_t *font = hbfindfont(xfont); | |
++ if (font == NULL) | |
++ return; | |
++ | |
++ hb_buffer_t *buffer = hb_buffer_create(); | |
++ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); | |
++ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_… | |
++ | |
++ /* Resize the buffer if required length is larger. */ | |
++ if (hbrunebuffer.capacity < length) { | |
++ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S… | |
++ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer… | |
++ } | |
++ | |
++ /* Fill buffer with codepoints. */ | |
++ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r… | |
++ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u; | |
++ mode = glyphs[glyph_idx].mode; | |
++ if (mode & ATTR_WDUMMY) | |
++ hbrunebuffer.runes[rune_idx] = 0x0020; | |
++ } | |
++ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt… | |
++ | |
++ /* Shape the segment. */ | |
++ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t… | |
++ | |
++ /* Get new glyph info. */ | |
++ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun… | |
++ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly… | |
++ | |
++ /* Fill the output. */ | |
++ data->buffer = buffer; | |
++ data->glyphs = info; | |
++ data->positions = pos; | |
++ data->count = glyph_count; | |
++} | |
++ | |
++void hbcleanup(HbTransformData *data) { | |
++ hb_buffer_destroy(data->buffer); | |
++ memset(data, 0, sizeof(HbTransformData)); | |
++} | |
+diff --git a/hb.h b/hb.h | |
+new file mode 100644 | |
+index 0000000..3b0ef44 | |
+--- /dev/null | |
++++ b/hb.h | |
+@@ -0,0 +1,14 @@ | |
++#include <X11/Xft/Xft.h> | |
++#include <hb.h> | |
++#include <hb-ft.h> | |
++ | |
++typedef struct { | |
++ hb_buffer_t *buffer; | |
++ hb_glyph_info_t *glyphs; | |
++ hb_glyph_position_t *positions; | |
++ unsigned int count; | |
++} HbTransformData; | |
++ | |
++void hbunloadfonts(); | |
++void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int); | |
++void hbcleanup(HbTransformData *); | |
+diff --git a/st.c b/st.c | |
+index d9b163e..fbca4ba 100644 | |
+--- a/st.c | |
++++ b/st.c | |
+@@ -2777,9 +2777,10 @@ draw(void) | |
+ | |
+ drawregion(0, 0, term.col, term.row); | |
+ if (TSCREEN.off == 0) | |
+ xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx], | |
+- term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]… | |
++ term.ocx, term.ocy, TLINE(term.ocy)[term.ocx], | |
++ TLINE(term.ocy), term.col); | |
+ term.ocx = cx; | |
+ term.ocy = term.c.y; | |
+ xfinishdraw(); | |
+ if (ocx != term.ocx || ocy != term.ocy) | |
+diff --git a/st.h b/st.h | |
+index 073851a..d0b071d 100644 | |
+--- a/st.h | |
++++ b/st.h | |
+@@ -10,9 +10,10 @@ | |
+ #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) | |
+ #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) | |
+ #define DEFAULT(a, b) (a) = (a) ? (a) : (b) | |
+ #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)… | |
+-#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f… | |
++#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode … | |
++ (a).fg != (b).fg || \ | |
+ (a).bg != (b).bg) | |
+ #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ | |
+ (t1.tv_nsec-t2.tv_nsec)/1E6) | |
+ #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) | |
+diff --git a/win.h b/win.h | |
+index 6de960d..94679e4 100644 | |
+--- a/win.h | |
++++ b/win.h | |
+@@ -24,9 +24,9 @@ enum win_mode { | |
+ }; | |
+ | |
+ void xbell(void); | |
+ void xclipcopy(void); | |
+-void xdrawcursor(int, int, Glyph, int, int, Glyph); | |
++void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int); | |
+ void xdrawline(Line, int, int, int); | |
+ void xfinishdraw(void); | |
+ void xloadcols(void); | |
+ int xsetcolorname(int, const char *); | |
+diff --git a/x.c b/x.c | |
+index c497e53..a213e52 100644 | |
+--- a/x.c | |
++++ b/x.c | |
+@@ -18,8 +18,9 @@ | |
+ char *argv0; | |
+ #include "arg.h" | |
+ #include "st.h" | |
+ #include "win.h" | |
++#include "hb.h" | |
+ | |
+ /* types used in config.h */ | |
+ typedef struct { | |
+ uint mod; | |
+@@ -143,10 +144,11 @@ typedef struct { | |
+ GC gc; | |
+ } DC; | |
+ | |
+ static inline ushort sixd_to_16bit(int); | |
++static void xresetfontsettings(ushort mode, Font **font, int *frcflags); | |
+ static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i… | |
+-static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
++static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
+ static void xdrawglyph(Glyph, int, int); | |
+ static void xclear(int, int, int, int); | |
+ static int xgeommasktogravity(int); | |
+ static int ximopen(Display *); | |
+@@ -760,9 +762,9 @@ xresize(int col, int row) | |
+ XftDrawChange(xw.draw, xw.buf); | |
+ xclear(0, 0, win.w, win.h); | |
+ | |
+ /* resize to new width */ | |
+- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); | |
++ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4); | |
+ } | |
+ | |
+ ushort | |
+ sixd_to_16bit(int x) | |
+@@ -1072,8 +1074,11 @@ xunloadfont(Font *f) | |
+ | |
+ void | |
+ xunloadfonts(void) | |
+ { | |
++ /* Clear Harfbuzz font cache. */ | |
++ hbunloadfonts(); | |
++ | |
+ /* Free the loaded fonts in the font cache. */ | |
+ while (frclen > 0) | |
+ XftFontClose(xw.dpy, frc[--frclen].font); | |
+ | |
+@@ -1203,9 +1208,9 @@ xinit(int cols, int rows) | |
+ XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); | |
+ XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); | |
+ | |
+ /* font spec buffer */ | |
+- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); | |
++ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4); | |
+ | |
+ /* Xft rendering context */ | |
+ xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); | |
+ | |
+@@ -1257,144 +1262,155 @@ xinit(int cols, int rows) | |
+ if (xsel.xtarget == None) | |
+ xsel.xtarget = XA_STRING; | |
+ } | |
+ | |
++void | |
++xresetfontsettings(ushort mode, Font **font, int *frcflags) | |
++{ | |
++ *font = &dc.font; | |
++ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
++ *font = &dc.ibfont; | |
++ *frcflags = FRC_ITALICBOLD; | |
++ } else if (mode & ATTR_ITALIC) { | |
++ *font = &dc.ifont; | |
++ *frcflags = FRC_ITALIC; | |
++ } else if (mode & ATTR_BOLD) { | |
++ *font = &dc.bfont; | |
++ *frcflags = FRC_BOLD; | |
++ } | |
++} | |
++ | |
+ int | |
+ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in… | |
+ { | |
+ float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp,… | |
+- ushort mode, prevmode = USHRT_MAX; | |
++ ushort mode = glyphs[0].mode & ~ATTR_WRAP; | |
+ Font *font = &dc.font; | |
+ int frcflags = FRC_NORMAL; | |
+- float runewidth = win.cw; | |
++ float runewidth = win.cw * ((glyphs[0].mode & ATTR_WIDE) ? 2.0f : 1.0… | |
+ Rune rune; | |
+ FT_UInt glyphidx; | |
+ FcResult fcres; | |
+ FcPattern *fcpattern, *fontpattern; | |
+ FcFontSet *fcsets[] = { NULL }; | |
+ FcCharSet *fccharset; | |
+- int i, f, numspecs = 0; | |
++ int f, code_idx, numspecs = 0; | |
++ float cluster_xp = xp, cluster_yp = yp; | |
++ HbTransformData shaped = { 0 }; | |
+ | |
+- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { | |
+- /* Fetch rune and mode for current glyph. */ | |
+- rune = glyphs[i].u; | |
+- mode = glyphs[i].mode; | |
++ /* Initial values. */ | |
++ xresetfontsettings(mode, &font, &frcflags); | |
+ | |
+- /* Skip dummy wide-character spacing. */ | |
+- if (mode == ATTR_WDUMMY) | |
++ /* Shape the segment. */ | |
++ hbtransform(&shaped, font->match, glyphs, 0, len); | |
++ xp = winx; yp = winy + font->ascent; | |
++ cluster_xp = xp; cluster_yp = yp; | |
++ | |
++ for (code_idx = 0; code_idx < shaped.count; code_idx++) { | |
++ int idx = shaped.glyphs[code_idx].cluster; | |
++ | |
++ if (glyphs[idx].mode & ATTR_WDUMMY) | |
+ continue; | |
+ | |
+- /* Determine font for glyph if different from previous glyph.… | |
+- if (prevmode != mode) { | |
+- prevmode = mode; | |
+- font = &dc.font; | |
+- frcflags = FRC_NORMAL; | |
+- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0… | |
+- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
+- font = &dc.ibfont; | |
+- frcflags = FRC_ITALICBOLD; | |
+- } else if (mode & ATTR_ITALIC) { | |
+- font = &dc.ifont; | |
+- frcflags = FRC_ITALIC; | |
+- } else if (mode & ATTR_BOLD) { | |
+- font = &dc.bfont; | |
+- frcflags = FRC_BOLD; | |
+- } | |
+- yp = winy + font->ascent; | |
++ /* Advance the drawing cursor if we've moved to a new cluster… | |
++ if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluste… | |
++ xp += runewidth; | |
++ cluster_xp = xp; | |
++ cluster_yp = yp; | |
+ } | |
+ | |
+- /* Lookup character index with default font. */ | |
+- glyphidx = XftCharIndex(xw.dpy, font->match, rune); | |
+- if (glyphidx) { | |
++ if (shaped.glyphs[code_idx].codepoint != 0) { | |
++ /* If symbol is found, put it into the specs. */ | |
+ specs[numspecs].font = font->match; | |
+- specs[numspecs].glyph = glyphidx; | |
+- specs[numspecs].x = (short)xp; | |
+- specs[numspecs].y = (short)yp; | |
+- xp += runewidth; | |
++ specs[numspecs].glyph = shaped.glyphs[code_idx].codep… | |
++ specs[numspecs].x = cluster_xp + (short)(shaped.posit… | |
++ specs[numspecs].y = cluster_yp - (short)(shaped.posit… | |
++ cluster_xp += shaped.positions[code_idx].x_advance / … | |
++ cluster_yp += shaped.positions[code_idx].y_advance / … | |
+ numspecs++; | |
+- continue; | |
+- } | |
+- | |
+- /* Fallback on font cache, search the font cache for match. */ | |
+- for (f = 0; f < frclen; f++) { | |
+- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); | |
+- /* Everything correct. */ | |
+- if (glyphidx && frc[f].flags == frcflags) | |
+- break; | |
+- /* We got a default font for a not found glyph. */ | |
+- if (!glyphidx && frc[f].flags == frcflags | |
+- && frc[f].unicodep == rune) { | |
+- break; | |
++ } else { | |
++ /* If it's not found, try to fetch it through the fon… | |
++ rune = glyphs[idx].u; | |
++ for (f = 0; f < frclen; f++) { | |
++ glyphidx = XftCharIndex(xw.dpy, frc[f].font, … | |
++ /* Everything correct. */ | |
++ if (glyphidx && frc[f].flags == frcflags) | |
++ break; | |
++ /* We got a default font for a not found glyp… | |
++ if (!glyphidx && frc[f].flags == frcflags | |
++ && frc[f].unicodep == rune) { | |
++ break; | |
++ } | |
+ } | |
+- } | |
+ | |
+- /* Nothing was found. Use fontconfig to find matching font. */ | |
+- if (f >= frclen) { | |
+- if (!font->set) | |
+- font->set = FcFontSort(0, font->pattern, | |
+- 1, 0, &fcres); | |
+- fcsets[0] = font->set; | |
+- | |
+- /* | |
+- * Nothing was found in the cache. Now use | |
+- * some dozen of Fontconfig calls to get the | |
+- * font for one single character. | |
+- * | |
+- * Xft and fontconfig are design failures. | |
+- */ | |
+- fcpattern = FcPatternDuplicate(font->pattern); | |
+- fccharset = FcCharSetCreate(); | |
+- | |
+- FcCharSetAddChar(fccharset, rune); | |
+- FcPatternAddCharSet(fcpattern, FC_CHARSET, | |
+- fccharset); | |
+- FcPatternAddBool(fcpattern, FC_SCALABLE, 1); | |
+- | |
+- FcConfigSubstitute(0, fcpattern, | |
+- FcMatchPattern); | |
+- FcDefaultSubstitute(fcpattern); | |
+- | |
+- fontpattern = FcFontSetMatch(0, fcsets, 1, | |
+- fcpattern, &fcres); | |
+- | |
+- /* Allocate memory for the new cache entry. */ | |
+- if (frclen >= frccap) { | |
+- frccap += 16; | |
+- frc = xrealloc(frc, frccap * sizeof(Fontcache… | |
++ /* Nothing was found. Use fontconfig to find matching… | |
++ if (f >= frclen) { | |
++ if (!font->set) | |
++ font->set = FcFontSort(0, font->patte… | |
++ 1, 0, &fcres); | |
++ fcsets[0] = font->set; | |
++ | |
++ /* | |
++ * Nothing was found in the cache. Now use | |
++ * some dozen of Fontconfig calls to get the | |
++ * font for one single character. | |
++ * | |
++ * Xft and fontconfig are design failures. | |
++ */ | |
++ fcpattern = FcPatternDuplicate(font->pattern); | |
++ fccharset = FcCharSetCreate(); | |
++ | |
++ FcCharSetAddChar(fccharset, rune); | |
++ FcPatternAddCharSet(fcpattern, FC_CHARSET, | |
++ fccharset); | |
++ FcPatternAddBool(fcpattern, FC_SCALABLE, 1); | |
++ | |
++ FcConfigSubstitute(0, fcpattern, | |
++ FcMatchPattern); | |
++ FcDefaultSubstitute(fcpattern); | |
++ | |
++ fontpattern = FcFontSetMatch(0, fcsets, 1, | |
++ fcpattern, &fcres); | |
++ | |
++ /* Allocate memory for the new cache entry. */ | |
++ if (frclen >= frccap) { | |
++ frccap += 16; | |
++ frc = xrealloc(frc, frccap * sizeof(F… | |
++ } | |
++ | |
++ frc[frclen].font = XftFontOpenPattern(xw.dpy, | |
++ fontpattern); | |
++ if (!frc[frclen].font) | |
++ die("XftFontOpenPattern failed seekin… | |
++ strerror(errno)); | |
++ frc[frclen].flags = frcflags; | |
++ frc[frclen].unicodep = rune; | |
++ | |
++ glyphidx = XftCharIndex(xw.dpy, frc[frclen].f… | |
++ | |
++ f = frclen; | |
++ frclen++; | |
++ | |
++ FcPatternDestroy(fcpattern); | |
++ FcCharSetDestroy(fccharset); | |
+ } | |
+ | |
+- frc[frclen].font = XftFontOpenPattern(xw.dpy, | |
+- fontpattern); | |
+- if (!frc[frclen].font) | |
+- die("XftFontOpenPattern failed seeking fallba… | |
+- strerror(errno)); | |
+- frc[frclen].flags = frcflags; | |
+- frc[frclen].unicodep = rune; | |
+- | |
+- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run… | |
+- | |
+- f = frclen; | |
+- frclen++; | |
+- | |
+- FcPatternDestroy(fcpattern); | |
+- FcCharSetDestroy(fccharset); | |
++ specs[numspecs].font = frc[f].font; | |
++ specs[numspecs].glyph = glyphidx; | |
++ specs[numspecs].x = (short)xp; | |
++ specs[numspecs].y = (short)yp; | |
++ numspecs++; | |
+ } | |
+- | |
+- specs[numspecs].font = frc[f].font; | |
+- specs[numspecs].glyph = glyphidx; | |
+- specs[numspecs].x = (short)xp; | |
+- specs[numspecs].y = (short)yp; | |
+- xp += runewidth; | |
+- numspecs++; | |
+ } | |
+ | |
++ /* Cleanup and get ready for next segment. */ | |
++ hbcleanup(&shaped); | |
+ return numspecs; | |
+ } | |
+ | |
+ void | |
+-xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
++xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
+ { | |
+- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); | |
+ int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, | |
+ width = charlen * win.cw; | |
+ Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; | |
+ XRenderColor colfg, colbg; | |
+@@ -1528,23 +1544,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Gly… | |
+ void | |
+ xdrawglyph(Glyph g, int x, int y) | |
+ { | |
+ int numspecs; | |
+- XftGlyphFontSpec spec; | |
++ XftGlyphFontSpec *specs = xw.specbuf; | |
+ | |
+- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); | |
+- xdrawglyphfontspecs(&spec, g, numspecs, x, y); | |
++ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y); | |
++ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? … | |
+ } | |
+ | |
+ void | |
+-xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) | |
++xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int… | |
+ { | |
+ Color drawcol; | |
+ | |
+ /* remove the old cursor */ | |
+ if (selected(ox, oy)) | |
+ og.mode ^= ATTR_REVERSE; | |
+- xdrawglyph(og, ox, oy); | |
++ | |
++ /* Redraw the line where cursor was previously. | |
++ * It will restore the ligatures broken by the cursor. */ | |
++ xdrawline(line, 0, oy, len); | |
+ | |
+ if (IS_SET(MODE_HIDE)) | |
+ return; | |
+ | |
+@@ -1676,30 +1695,30 @@ xdrawline(Line line, int x1, int y1, int x2) | |
+ int i, x, ox, numspecs; | |
+ Glyph base, new; | |
+ XftGlyphFontSpec *specs = xw.specbuf; | |
+ | |
+- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); | |
+ i = ox = 0; | |
+- for (x = x1; x < x2 && i < numspecs; x++) { | |
++ for (x = x1; x < x2; x++) { | |
+ new = line[x]; | |
+ if (new.mode == ATTR_WDUMMY) | |
+ continue; | |
+ if (selected(x, y1)) | |
+ new.mode ^= ATTR_REVERSE; | |
+- if (i > 0 && ATTRCMP(base, new)) { | |
+- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
+- specs += i; | |
+- numspecs -= i; | |
++ if ((i > 0) && ATTRCMP(base, new)) { | |
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - … | |
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x … | |
+ i = 0; | |
+ } | |
+ if (i == 0) { | |
+ ox = x; | |
+ base = new; | |
+ } | |
+ i++; | |
+ } | |
+- if (i > 0) | |
+- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
++ if (i > 0) { | |
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,… | |
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox); | |
++ } | |
+ } | |
+ | |
+ void | |
+ xfinishdraw(void) | |
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-boxdraw-20240… | |
@@ -1,647 +0,0 @@ | |
-diff --git a/Makefile b/Makefile | |
-index 6dfa212..adfa07a 100644 | |
---- a/Makefile | |
-+++ b/Makefile | |
-@@ -4,7 +4,7 @@ | |
- | |
- include config.mk | |
- | |
--SRC = st.c x.c boxdraw.c | |
-+SRC = st.c x.c boxdraw.c hb.c | |
- OBJ = $(SRC:.c=.o) | |
- | |
- all: st | |
-@@ -22,8 +22,9 @@ config.h: | |
- $(CC) $(STCFLAGS) -c $< | |
- | |
- st.o: config.h st.h win.h | |
--x.o: arg.h config.h st.h win.h | |
-+x.o: arg.h config.h st.h win.h hb.h | |
- boxdraw.o: config.h st.h boxdraw_data.h | |
-+hb.o: st.h | |
- | |
- $(OBJ): config.h config.mk | |
- | |
-diff --git a/config.mk b/config.mk | |
-index 1e306f8..3e13e53 100644 | |
---- a/config.mk | |
-+++ b/config.mk | |
-@@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config | |
- # includes and libs | |
- INCS = -I$(X11INC) \ | |
- `$(PKG_CONFIG) --cflags fontconfig` \ | |
-- `$(PKG_CONFIG) --cflags freetype2` | |
-+ `$(PKG_CONFIG) --cflags freetype2` \ | |
-+ `$(PKG_CONFIG) --cflags harfbuzz` | |
- LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ | |
- `$(PKG_CONFIG) --libs fontconfig` \ | |
-- `$(PKG_CONFIG) --libs freetype2` | |
-+ `$(PKG_CONFIG) --libs freetype2` \ | |
-+ `$(PKG_CONFIG) --libs harfbuzz` | |
- | |
- # flags | |
- STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 | |
-diff --git a/hb.c b/hb.c | |
-new file mode 100644 | |
-index 0000000..99412c8 | |
---- /dev/null | |
-+++ b/hb.c | |
-@@ -0,0 +1,125 @@ | |
-+#include <stdlib.h> | |
-+#include <stdio.h> | |
-+#include <math.h> | |
-+#include <X11/Xft/Xft.h> | |
-+#include <X11/cursorfont.h> | |
-+#include <hb.h> | |
-+#include <hb-ft.h> | |
-+ | |
-+#include "st.h" | |
-+#include "hb.h" | |
-+ | |
-+#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start… | |
-+#define BUFFER_STEP 256 | |
-+ | |
-+hb_font_t *hbfindfont(XftFont *match); | |
-+ | |
-+typedef struct { | |
-+ XftFont *match; | |
-+ hb_font_t *font; | |
-+} HbFontMatch; | |
-+ | |
-+typedef struct { | |
-+ size_t capacity; | |
-+ HbFontMatch *fonts; | |
-+} HbFontCache; | |
-+ | |
-+static HbFontCache hbfontcache = { 0, NULL }; | |
-+ | |
-+typedef struct { | |
-+ size_t capacity; | |
-+ Rune *runes; | |
-+} RuneBuffer; | |
-+ | |
-+static RuneBuffer hbrunebuffer = { 0, NULL }; | |
-+ | |
-+/* | |
-+ * Poplulate the array with a list of font features, wrapped in FEATURE macro, | |
-+ * e. g. | |
-+ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') | |
-+ */ | |
-+hb_feature_t features[] = { }; | |
-+ | |
-+void | |
-+hbunloadfonts() | |
-+{ | |
-+ for (int i = 0; i < hbfontcache.capacity; i++) { | |
-+ hb_font_destroy(hbfontcache.fonts[i].font); | |
-+ XftUnlockFace(hbfontcache.fonts[i].match); | |
-+ } | |
-+ | |
-+ if (hbfontcache.fonts != NULL) { | |
-+ free(hbfontcache.fonts); | |
-+ hbfontcache.fonts = NULL; | |
-+ } | |
-+ hbfontcache.capacity = 0; | |
-+} | |
-+ | |
-+hb_font_t * | |
-+hbfindfont(XftFont *match) | |
-+{ | |
-+ for (int i = 0; i < hbfontcache.capacity; i++) { | |
-+ if (hbfontcache.fonts[i].match == match) | |
-+ return hbfontcache.fonts[i].font; | |
-+ } | |
-+ | |
-+ /* Font not found in cache, caching it now. */ | |
-+ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * … | |
-+ FT_Face face = XftLockFace(match); | |
-+ hb_font_t *font = hb_ft_font_create(face, NULL); | |
-+ if (font == NULL) | |
-+ die("Failed to load Harfbuzz font."); | |
-+ | |
-+ hbfontcache.fonts[hbfontcache.capacity].match = match; | |
-+ hbfontcache.fonts[hbfontcache.capacity].font = font; | |
-+ hbfontcache.capacity += 1; | |
-+ | |
-+ return font; | |
-+} | |
-+ | |
-+void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, … | |
-+ ushort mode = USHRT_MAX; | |
-+ unsigned int glyph_count; | |
-+ int rune_idx, glyph_idx, end = start + length; | |
-+ | |
-+ hb_font_t *font = hbfindfont(xfont); | |
-+ if (font == NULL) | |
-+ return; | |
-+ | |
-+ hb_buffer_t *buffer = hb_buffer_create(); | |
-+ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); | |
-+ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_… | |
-+ | |
-+ /* Resize the buffer if required length is larger. */ | |
-+ if (hbrunebuffer.capacity < length) { | |
-+ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S… | |
-+ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer… | |
-+ } | |
-+ | |
-+ /* Fill buffer with codepoints. */ | |
-+ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r… | |
-+ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u; | |
-+ mode = glyphs[glyph_idx].mode; | |
-+ if (mode & ATTR_WDUMMY) | |
-+ hbrunebuffer.runes[rune_idx] = 0x0020; | |
-+ } | |
-+ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt… | |
-+ | |
-+ /* Shape the segment. */ | |
-+ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t… | |
-+ | |
-+ /* Get new glyph info. */ | |
-+ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun… | |
-+ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly… | |
-+ | |
-+ /* Fill the output. */ | |
-+ data->buffer = buffer; | |
-+ data->glyphs = info; | |
-+ data->positions = pos; | |
-+ data->count = glyph_count; | |
-+} | |
-+ | |
-+void hbcleanup(HbTransformData *data) { | |
-+ hb_buffer_destroy(data->buffer); | |
-+ memset(data, 0, sizeof(HbTransformData)); | |
-+} | |
-diff --git a/hb.h b/hb.h | |
-new file mode 100644 | |
-index 0000000..3b0ef44 | |
---- /dev/null | |
-+++ b/hb.h | |
-@@ -0,0 +1,14 @@ | |
-+#include <X11/Xft/Xft.h> | |
-+#include <hb.h> | |
-+#include <hb-ft.h> | |
-+ | |
-+typedef struct { | |
-+ hb_buffer_t *buffer; | |
-+ hb_glyph_info_t *glyphs; | |
-+ hb_glyph_position_t *positions; | |
-+ unsigned int count; | |
-+} HbTransformData; | |
-+ | |
-+void hbunloadfonts(); | |
-+void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int); | |
-+void hbcleanup(HbTransformData *); | |
-diff --git a/st.c b/st.c | |
-index 41d5ace..1c2edd6 100644 | |
---- a/st.c | |
-+++ b/st.c | |
-@@ -2643,7 +2643,8 @@ draw(void) | |
- | |
- drawregion(0, 0, term.col, term.row); | |
- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], | |
-- term.ocx, term.ocy, term.line[term.ocy][term.ocx]); | |
-+ term.ocx, term.ocy, term.line[term.ocy][term.ocx], | |
-+ term.line[term.ocy], term.col); | |
- term.ocx = cx; | |
- term.ocy = term.c.y; | |
- xfinishdraw(); | |
-diff --git a/st.h b/st.h | |
-index 808f5f7..ae41368 100644 | |
---- a/st.h | |
-+++ b/st.h | |
-@@ -11,7 +11,8 @@ | |
- #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) | |
- #define DEFAULT(a, b) (a) = (a) ? (a) : (b) | |
- #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)… | |
--#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f… | |
-+#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode … | |
-+ (a).fg != (b).fg || \ | |
- (a).bg != (b).bg) | |
- #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ | |
- (t1.tv_nsec-t2.tv_nsec)/1E6) | |
-diff --git a/win.h b/win.h | |
-index 6de960d..94679e4 100644 | |
---- a/win.h | |
-+++ b/win.h | |
-@@ -25,7 +25,7 @@ enum win_mode { | |
- | |
- void xbell(void); | |
- void xclipcopy(void); | |
--void xdrawcursor(int, int, Glyph, int, int, Glyph); | |
-+void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int); | |
- void xdrawline(Line, int, int, int); | |
- void xfinishdraw(void); | |
- void xloadcols(void); | |
-diff --git a/x.c b/x.c | |
-index bf6bbf9..96b117f 100644 | |
---- a/x.c | |
-+++ b/x.c | |
-@@ -19,6 +19,7 @@ char *argv0; | |
- #include "arg.h" | |
- #include "st.h" | |
- #include "win.h" | |
-+#include "hb.h" | |
- | |
- /* types used in config.h */ | |
- typedef struct { | |
-@@ -141,8 +142,9 @@ typedef struct { | |
- } DC; | |
- | |
- static inline ushort sixd_to_16bit(int); | |
-+static void xresetfontsettings(ushort mode, Font **font, int *frcflags); | |
- static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i… | |
--static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
-+static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
- static void xdrawglyph(Glyph, int, int); | |
- static void xclear(int, int, int, int); | |
- static int xgeommasktogravity(int); | |
-@@ -757,7 +759,7 @@ xresize(int col, int row) | |
- xclear(0, 0, win.w, win.h); | |
- | |
- /* resize to new width */ | |
-- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); | |
-+ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4); | |
- } | |
- | |
- ushort | |
-@@ -1062,6 +1064,9 @@ xunloadfont(Font *f) | |
- void | |
- xunloadfonts(void) | |
- { | |
-+ /* Clear Harfbuzz font cache. */ | |
-+ hbunloadfonts(); | |
-+ | |
- /* Free the loaded fonts in the font cache. */ | |
- while (frclen > 0) | |
- XftFontClose(xw.dpy, frc[--frclen].font); | |
-@@ -1185,7 +1190,7 @@ xinit(int cols, int rows) | |
- XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); | |
- | |
- /* font spec buffer */ | |
-- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); | |
-+ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4); | |
- | |
- /* Xft rendering context */ | |
- xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); | |
-@@ -1241,6 +1246,22 @@ xinit(int cols, int rows) | |
- boxdraw_xinit(xw.dpy, xw.cmap, xw.draw, xw.vis); | |
- } | |
- | |
-+void | |
-+xresetfontsettings(ushort mode, Font **font, int *frcflags) | |
-+{ | |
-+ *font = &dc.font; | |
-+ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
-+ *font = &dc.ibfont; | |
-+ *frcflags = FRC_ITALICBOLD; | |
-+ } else if (mode & ATTR_ITALIC) { | |
-+ *font = &dc.ifont; | |
-+ *frcflags = FRC_ITALIC; | |
-+ } else if (mode & ATTR_BOLD) { | |
-+ *font = &dc.bfont; | |
-+ *frcflags = FRC_BOLD; | |
-+ } | |
-+} | |
-+ | |
- int | |
- xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in… | |
- { | |
-@@ -1255,133 +1276,164 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const G… | |
- FcPattern *fcpattern, *fontpattern; | |
- FcFontSet *fcsets[] = { NULL }; | |
- FcCharSet *fccharset; | |
-- int i, f, numspecs = 0; | |
-+ int i, f, length = 0, start = 0, numspecs = 0; | |
-+ float cluster_xp = xp, cluster_yp = yp; | |
-+ HbTransformData shaped = { 0 }; | |
-+ | |
-+ /* Initial values. */ | |
-+ mode = prevmode = glyphs[0].mode & ~ATTR_WRAP; | |
-+ xresetfontsettings(mode, &font, &frcflags); | |
- | |
- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { | |
-- /* Fetch rune and mode for current glyph. */ | |
-- rune = glyphs[i].u; | |
-- mode = glyphs[i].mode; | |
-+ mode = glyphs[i].mode & ~ATTR_WRAP; | |
- | |
- /* Skip dummy wide-character spacing. */ | |
-- if (mode == ATTR_WDUMMY) | |
-+ if (mode & ATTR_WDUMMY && i < (len - 1)) | |
- continue; | |
- | |
-- /* Determine font for glyph if different from previous glyph.… | |
-- if (prevmode != mode) { | |
-- prevmode = mode; | |
-- font = &dc.font; | |
-- frcflags = FRC_NORMAL; | |
-- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0… | |
-- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
-- font = &dc.ibfont; | |
-- frcflags = FRC_ITALICBOLD; | |
-- } else if (mode & ATTR_ITALIC) { | |
-- font = &dc.ifont; | |
-- frcflags = FRC_ITALIC; | |
-- } else if (mode & ATTR_BOLD) { | |
-- font = &dc.bfont; | |
-- frcflags = FRC_BOLD; | |
-+ if ( | |
-+ prevmode != mode | |
-+ || ATTRCMP(glyphs[start], glyphs[i]) | |
-+ || selected(x + i, y) != selected(x + start, y) | |
-+ || i == (len - 1) | |
-+ ) { | |
-+ /* Handle 1-character wide segments and end of line */ | |
-+ length = i - start; | |
-+ if (i == start) { | |
-+ length = 1; | |
-+ } else if (i == (len - 1)) { | |
-+ length = (i - start + 1); | |
- } | |
-- yp = winy + font->ascent; | |
-- } | |
- | |
-- if (mode & ATTR_BOXDRAW) { | |
-- /* minor shoehorning: boxdraw uses only this ushort */ | |
-- glyphidx = boxdrawindex(&glyphs[i]); | |
-- } else { | |
-- /* Lookup character index with default font. */ | |
-- glyphidx = XftCharIndex(xw.dpy, font->match, rune); | |
-- } | |
-- if (glyphidx) { | |
-- specs[numspecs].font = font->match; | |
-- specs[numspecs].glyph = glyphidx; | |
-- specs[numspecs].x = (short)xp; | |
-- specs[numspecs].y = (short)yp; | |
-- xp += runewidth; | |
-- numspecs++; | |
-- continue; | |
-- } | |
-- | |
-- /* Fallback on font cache, search the font cache for match. */ | |
-- for (f = 0; f < frclen; f++) { | |
-- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); | |
-- /* Everything correct. */ | |
-- if (glyphidx && frc[f].flags == frcflags) | |
-- break; | |
-- /* We got a default font for a not found glyph. */ | |
-- if (!glyphidx && frc[f].flags == frcflags | |
-- && frc[f].unicodep == rune) { | |
-- break; | |
-+ /* Shape the segment. */ | |
-+ hbtransform(&shaped, font->match, glyphs, start, leng… | |
-+ runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE… | |
-+ cluster_xp = xp; cluster_yp = yp; | |
-+ for (int code_idx = 0; code_idx < shaped.count; code_… | |
-+ int idx = shaped.glyphs[code_idx].cluster; | |
-+ | |
-+ if (glyphs[start + idx].mode & ATTR_WDUMMY) | |
-+ continue; | |
-+ | |
-+ /* Advance the drawing cursor if we've moved … | |
-+ if (code_idx > 0 && idx != shaped.glyphs[code… | |
-+ xp += runewidth; | |
-+ cluster_xp = xp; | |
-+ cluster_yp = yp; | |
-+ runewidth = win.cw * ((glyphs[start +… | |
-+ } | |
-+ | |
-+ if (glyphs[start + idx].mode & ATTR_BOXDRAW) { | |
-+ /* minor shoehorning: boxdraw uses on… | |
-+ specs[numspecs].font = font->match; | |
-+ specs[numspecs].glyph = boxdrawindex(… | |
-+ specs[numspecs].x = xp; | |
-+ specs[numspecs].y = yp; | |
-+ numspecs++; | |
-+ } else if (shaped.glyphs[code_idx].codepoint … | |
-+ /* If symbol is found, put it into th… | |
-+ specs[numspecs].font = font->match; | |
-+ specs[numspecs].glyph = shaped.glyphs… | |
-+ specs[numspecs].x = cluster_xp + (sho… | |
-+ specs[numspecs].y = cluster_yp - (sho… | |
-+ cluster_xp += shaped.positions[code_i… | |
-+ cluster_yp += shaped.positions[code_i… | |
-+ numspecs++; | |
-+ } else { | |
-+ /* If it's not found, try to fetch it… | |
-+ rune = glyphs[start + idx].u; | |
-+ for (f = 0; f < frclen; f++) { | |
-+ glyphidx = XftCharIndex(xw.dp… | |
-+ /* Everything correct. */ | |
-+ if (glyphidx && frc[f].flags … | |
-+ break; | |
-+ /* We got a default font for … | |
-+ if (!glyphidx && frc[f].flags… | |
-+ && frc[f].uni… | |
-+ break; | |
-+ } | |
-+ } | |
-+ | |
-+ /* Nothing was found. Use fontconfig … | |
-+ if (f >= frclen) { | |
-+ if (!font->set) | |
-+ font->set = FcFontSor… | |
-+ … | |
-+ fcsets[0] = font->set; | |
-+ | |
-+ /* | |
-+ * Nothing was found in the c… | |
-+ * some dozen of Fontconfig c… | |
-+ * font for one single charac… | |
-+ * | |
-+ * Xft and fontconfig are des… | |
-+ */ | |
-+ fcpattern = FcPatternDuplicat… | |
-+ fccharset = FcCharSetCreate(); | |
-+ | |
-+ FcCharSetAddChar(fccharset, r… | |
-+ FcPatternAddCharSet(fcpattern… | |
-+ fccharset); | |
-+ FcPatternAddBool(fcpattern, F… | |
-+ | |
-+ FcConfigSubstitute(0, fcpatte… | |
-+ FcMatchPatter… | |
-+ FcDefaultSubstitute(fcpattern… | |
-+ | |
-+ fontpattern = FcFontSetMatch(… | |
-+ fcpattern, &f… | |
-+ | |
-+ /* Allocate memory for the ne… | |
-+ if (frclen >= frccap) { | |
-+ frccap += 16; | |
-+ frc = xrealloc(frc, f… | |
-+ } | |
-+ | |
-+ frc[frclen].font = XftFontOpe… | |
-+ fontpattern); | |
-+ if (!frc[frclen].font) | |
-+ die("XftFontOpenPatte… | |
-+ strerror(errn… | |
-+ frc[frclen].flags = frcflags; | |
-+ frc[frclen].unicodep = rune; | |
-+ | |
-+ glyphidx = XftCharIndex(xw.dp… | |
-+ | |
-+ f = frclen; | |
-+ frclen++; | |
-+ | |
-+ FcPatternDestroy(fcpattern); | |
-+ FcCharSetDestroy(fccharset); | |
-+ } | |
-+ | |
-+ specs[numspecs].font = frc[f].font; | |
-+ specs[numspecs].glyph = glyphidx; | |
-+ specs[numspecs].x = (short)xp; | |
-+ specs[numspecs].y = (short)yp; | |
-+ numspecs++; | |
-+ } | |
- } | |
-- } | |
- | |
-- /* Nothing was found. Use fontconfig to find matching font. */ | |
-- if (f >= frclen) { | |
-- if (!font->set) | |
-- font->set = FcFontSort(0, font->pattern, | |
-- 1, 0, &fcres); | |
-- fcsets[0] = font->set; | |
-+ /* Cleanup and get ready for next segment. */ | |
-+ hbcleanup(&shaped); | |
-+ start = i; | |
- | |
-- /* | |
-- * Nothing was found in the cache. Now use | |
-- * some dozen of Fontconfig calls to get the | |
-- * font for one single character. | |
-- * | |
-- * Xft and fontconfig are design failures. | |
-- */ | |
-- fcpattern = FcPatternDuplicate(font->pattern); | |
-- fccharset = FcCharSetCreate(); | |
-- | |
-- FcCharSetAddChar(fccharset, rune); | |
-- FcPatternAddCharSet(fcpattern, FC_CHARSET, | |
-- fccharset); | |
-- FcPatternAddBool(fcpattern, FC_SCALABLE, 1); | |
-- | |
-- FcConfigSubstitute(0, fcpattern, | |
-- FcMatchPattern); | |
-- FcDefaultSubstitute(fcpattern); | |
-- | |
-- fontpattern = FcFontSetMatch(0, fcsets, 1, | |
-- fcpattern, &fcres); | |
-- | |
-- /* Allocate memory for the new cache entry. */ | |
-- if (frclen >= frccap) { | |
-- frccap += 16; | |
-- frc = xrealloc(frc, frccap * sizeof(Fontcache… | |
-+ /* Determine font for glyph if different from previou… | |
-+ if (prevmode != mode) { | |
-+ prevmode = mode; | |
-+ xresetfontsettings(mode, &font, &frcflags); | |
-+ yp = winy + font->ascent; | |
- } | |
-- | |
-- frc[frclen].font = XftFontOpenPattern(xw.dpy, | |
-- fontpattern); | |
-- if (!frc[frclen].font) | |
-- die("XftFontOpenPattern failed seeking fallba… | |
-- strerror(errno)); | |
-- frc[frclen].flags = frcflags; | |
-- frc[frclen].unicodep = rune; | |
-- | |
-- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run… | |
-- | |
-- f = frclen; | |
-- frclen++; | |
-- | |
-- FcPatternDestroy(fcpattern); | |
-- FcCharSetDestroy(fccharset); | |
- } | |
-- | |
-- specs[numspecs].font = frc[f].font; | |
-- specs[numspecs].glyph = glyphidx; | |
-- specs[numspecs].x = (short)xp; | |
-- specs[numspecs].y = (short)yp; | |
-- xp += runewidth; | |
-- numspecs++; | |
- } | |
- | |
-+ hbcleanup(&shaped); | |
- return numspecs; | |
- } | |
- | |
- void | |
--xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
-+xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
- { | |
-- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); | |
- int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, | |
- width = charlen * win.cw; | |
- Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; | |
-@@ -1521,21 +1573,24 @@ void | |
- xdrawglyph(Glyph g, int x, int y) | |
- { | |
- int numspecs; | |
-- XftGlyphFontSpec spec; | |
-+ XftGlyphFontSpec *specs = xw.specbuf; | |
- | |
-- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); | |
-- xdrawglyphfontspecs(&spec, g, numspecs, x, y); | |
-+ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y); | |
-+ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? … | |
- } | |
- | |
- void | |
--xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) | |
-+xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int… | |
- { | |
- Color drawcol; | |
- | |
- /* remove the old cursor */ | |
- if (selected(ox, oy)) | |
- og.mode ^= ATTR_REVERSE; | |
-- xdrawglyph(og, ox, oy); | |
-+ | |
-+ /* Redraw the line where cursor was previously. | |
-+ * It will restore the ligatures broken by the cursor. */ | |
-+ xdrawline(line, 0, oy, len); | |
- | |
- if (IS_SET(MODE_HIDE)) | |
- return; | |
-@@ -1663,18 +1718,16 @@ xdrawline(Line line, int x1, int y1, int x2) | |
- Glyph base, new; | |
- XftGlyphFontSpec *specs = xw.specbuf; | |
- | |
-- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); | |
- i = ox = 0; | |
-- for (x = x1; x < x2 && i < numspecs; x++) { | |
-+ for (x = x1; x < x2; x++) { | |
- new = line[x]; | |
- if (new.mode == ATTR_WDUMMY) | |
- continue; | |
- if (selected(x, y1)) | |
- new.mode ^= ATTR_REVERSE; | |
-- if (i > 0 && ATTRCMP(base, new)) { | |
-- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
-- specs += i; | |
-- numspecs -= i; | |
-+ if ((i > 0) && ATTRCMP(base, new)) { | |
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - … | |
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x … | |
- i = 0; | |
- } | |
- if (i == 0) { | |
-@@ -1683,8 +1736,10 @@ xdrawline(Line line, int x1, int y1, int x2) | |
- } | |
- i++; | |
- } | |
-- if (i > 0) | |
-- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
-+ if (i > 0) { | |
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,… | |
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox); | |
-+ } | |
- } | |
- | |
- void | |
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-boxdraw-20241… | |
@@ -0,0 +1,662 @@ | |
+diff --git a/Makefile b/Makefile | |
+index a64b4c2..05124bf 100644 | |
+--- a/Makefile | |
++++ b/Makefile | |
+@@ -3,9 +3,9 @@ | |
+ .POSIX: | |
+ | |
+ include config.mk | |
+ | |
+-SRC = st.c x.c boxdraw.c | |
++SRC = st.c x.c boxdraw.c hb.c | |
+ OBJ = $(SRC:.c=.o) | |
+ | |
+ all: st | |
+ | |
+@@ -15,10 +15,11 @@ config.h: | |
+ .c.o: | |
+ $(CC) $(STCFLAGS) -c $< | |
+ | |
+ st.o: config.h st.h win.h | |
+-x.o: arg.h config.h st.h win.h | |
++x.o: arg.h config.h st.h win.h hb.h | |
+ boxdraw.o: config.h st.h boxdraw_data.h | |
++hb.o: st.h | |
+ | |
+ $(OBJ): config.h config.mk | |
+ | |
+ st: $(OBJ) | |
+diff --git a/config.mk b/config.mk | |
+index fdc29a7..6833b3b 100644 | |
+--- a/config.mk | |
++++ b/config.mk | |
+@@ -14,12 +14,14 @@ PKG_CONFIG = pkg-config | |
+ | |
+ # includes and libs | |
+ INCS = -I$(X11INC) \ | |
+ `$(PKG_CONFIG) --cflags fontconfig` \ | |
+- `$(PKG_CONFIG) --cflags freetype2` | |
++ `$(PKG_CONFIG) --cflags freetype2` \ | |
++ `$(PKG_CONFIG) --cflags harfbuzz` | |
+ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ | |
+ `$(PKG_CONFIG) --libs fontconfig` \ | |
+- `$(PKG_CONFIG) --libs freetype2` | |
++ `$(PKG_CONFIG) --libs freetype2` \ | |
++ `$(PKG_CONFIG) --libs harfbuzz` | |
+ | |
+ # flags | |
+ STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 | |
+ STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS) | |
+@@ -28,9 +30,10 @@ STLDFLAGS = $(LIBS) $(LDFLAGS) | |
+ # OpenBSD: | |
+ #CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE | |
+ #LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \ | |
+ # `$(PKG_CONFIG) --libs fontconfig` \ | |
+-# `$(PKG_CONFIG) --libs freetype2` | |
++# `$(PKG_CONFIG) --libs freetype2` \ | |
++# `$(PKG_CONFIG) --libs harfbuzz` | |
+ #MANPREFIX = ${PREFIX}/man | |
+ | |
+ # compiler and linker | |
+ # CC = c99 | |
+diff --git a/hb.c b/hb.c | |
+new file mode 100644 | |
+index 0000000..99412c8 | |
+--- /dev/null | |
++++ b/hb.c | |
+@@ -0,0 +1,125 @@ | |
++#include <stdlib.h> | |
++#include <stdio.h> | |
++#include <math.h> | |
++#include <X11/Xft/Xft.h> | |
++#include <X11/cursorfont.h> | |
++#include <hb.h> | |
++#include <hb-ft.h> | |
++ | |
++#include "st.h" | |
++#include "hb.h" | |
++ | |
++#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start… | |
++#define BUFFER_STEP 256 | |
++ | |
++hb_font_t *hbfindfont(XftFont *match); | |
++ | |
++typedef struct { | |
++ XftFont *match; | |
++ hb_font_t *font; | |
++} HbFontMatch; | |
++ | |
++typedef struct { | |
++ size_t capacity; | |
++ HbFontMatch *fonts; | |
++} HbFontCache; | |
++ | |
++static HbFontCache hbfontcache = { 0, NULL }; | |
++ | |
++typedef struct { | |
++ size_t capacity; | |
++ Rune *runes; | |
++} RuneBuffer; | |
++ | |
++static RuneBuffer hbrunebuffer = { 0, NULL }; | |
++ | |
++/* | |
++ * Poplulate the array with a list of font features, wrapped in FEATURE macro, | |
++ * e. g. | |
++ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') | |
++ */ | |
++hb_feature_t features[] = { }; | |
++ | |
++void | |
++hbunloadfonts() | |
++{ | |
++ for (int i = 0; i < hbfontcache.capacity; i++) { | |
++ hb_font_destroy(hbfontcache.fonts[i].font); | |
++ XftUnlockFace(hbfontcache.fonts[i].match); | |
++ } | |
++ | |
++ if (hbfontcache.fonts != NULL) { | |
++ free(hbfontcache.fonts); | |
++ hbfontcache.fonts = NULL; | |
++ } | |
++ hbfontcache.capacity = 0; | |
++} | |
++ | |
++hb_font_t * | |
++hbfindfont(XftFont *match) | |
++{ | |
++ for (int i = 0; i < hbfontcache.capacity; i++) { | |
++ if (hbfontcache.fonts[i].match == match) | |
++ return hbfontcache.fonts[i].font; | |
++ } | |
++ | |
++ /* Font not found in cache, caching it now. */ | |
++ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * … | |
++ FT_Face face = XftLockFace(match); | |
++ hb_font_t *font = hb_ft_font_create(face, NULL); | |
++ if (font == NULL) | |
++ die("Failed to load Harfbuzz font."); | |
++ | |
++ hbfontcache.fonts[hbfontcache.capacity].match = match; | |
++ hbfontcache.fonts[hbfontcache.capacity].font = font; | |
++ hbfontcache.capacity += 1; | |
++ | |
++ return font; | |
++} | |
++ | |
++void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, … | |
++ ushort mode = USHRT_MAX; | |
++ unsigned int glyph_count; | |
++ int rune_idx, glyph_idx, end = start + length; | |
++ | |
++ hb_font_t *font = hbfindfont(xfont); | |
++ if (font == NULL) | |
++ return; | |
++ | |
++ hb_buffer_t *buffer = hb_buffer_create(); | |
++ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); | |
++ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_… | |
++ | |
++ /* Resize the buffer if required length is larger. */ | |
++ if (hbrunebuffer.capacity < length) { | |
++ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S… | |
++ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer… | |
++ } | |
++ | |
++ /* Fill buffer with codepoints. */ | |
++ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r… | |
++ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u; | |
++ mode = glyphs[glyph_idx].mode; | |
++ if (mode & ATTR_WDUMMY) | |
++ hbrunebuffer.runes[rune_idx] = 0x0020; | |
++ } | |
++ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt… | |
++ | |
++ /* Shape the segment. */ | |
++ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t… | |
++ | |
++ /* Get new glyph info. */ | |
++ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun… | |
++ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly… | |
++ | |
++ /* Fill the output. */ | |
++ data->buffer = buffer; | |
++ data->glyphs = info; | |
++ data->positions = pos; | |
++ data->count = glyph_count; | |
++} | |
++ | |
++void hbcleanup(HbTransformData *data) { | |
++ hb_buffer_destroy(data->buffer); | |
++ memset(data, 0, sizeof(HbTransformData)); | |
++} | |
+diff --git a/hb.h b/hb.h | |
+new file mode 100644 | |
+index 0000000..3b0ef44 | |
+--- /dev/null | |
++++ b/hb.h | |
+@@ -0,0 +1,14 @@ | |
++#include <X11/Xft/Xft.h> | |
++#include <hb.h> | |
++#include <hb-ft.h> | |
++ | |
++typedef struct { | |
++ hb_buffer_t *buffer; | |
++ hb_glyph_info_t *glyphs; | |
++ hb_glyph_position_t *positions; | |
++ unsigned int count; | |
++} HbTransformData; | |
++ | |
++void hbunloadfonts(); | |
++void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int); | |
++void hbcleanup(HbTransformData *); | |
+diff --git a/st.c b/st.c | |
+index ec6fbf3..1385f77 100644 | |
+--- a/st.c | |
++++ b/st.c | |
+@@ -2661,9 +2661,10 @@ draw(void) | |
+ cx--; | |
+ | |
+ drawregion(0, 0, term.col, term.row); | |
+ xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], | |
+- term.ocx, term.ocy, term.line[term.ocy][term.ocx]); | |
++ term.ocx, term.ocy, term.line[term.ocy][term.ocx], | |
++ term.line[term.ocy], term.col); | |
+ term.ocx = cx; | |
+ term.ocy = term.c.y; | |
+ xfinishdraw(); | |
+ if (ocx != term.ocx || ocy != term.ocy) | |
+diff --git a/st.h b/st.h | |
+index 808f5f7..ae41368 100644 | |
+--- a/st.h | |
++++ b/st.h | |
+@@ -10,9 +10,10 @@ | |
+ #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) | |
+ #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) | |
+ #define DEFAULT(a, b) (a) = (a) ? (a) : (b) | |
+ #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)… | |
+-#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f… | |
++#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode … | |
++ (a).fg != (b).fg || \ | |
+ (a).bg != (b).bg) | |
+ #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ | |
+ (t1.tv_nsec-t2.tv_nsec)/1E6) | |
+ #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) | |
+diff --git a/win.h b/win.h | |
+index 6de960d..94679e4 100644 | |
+--- a/win.h | |
++++ b/win.h | |
+@@ -24,9 +24,9 @@ enum win_mode { | |
+ }; | |
+ | |
+ void xbell(void); | |
+ void xclipcopy(void); | |
+-void xdrawcursor(int, int, Glyph, int, int, Glyph); | |
++void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int); | |
+ void xdrawline(Line, int, int, int); | |
+ void xfinishdraw(void); | |
+ void xloadcols(void); | |
+ int xsetcolorname(int, const char *); | |
+diff --git a/x.c b/x.c | |
+index 978a8fc..c2d9993 100644 | |
+--- a/x.c | |
++++ b/x.c | |
+@@ -18,8 +18,9 @@ | |
+ char *argv0; | |
+ #include "arg.h" | |
+ #include "st.h" | |
+ #include "win.h" | |
++#include "hb.h" | |
+ | |
+ /* types used in config.h */ | |
+ typedef struct { | |
+ uint mod; | |
+@@ -140,10 +141,11 @@ typedef struct { | |
+ GC gc; | |
+ } DC; | |
+ | |
+ static inline ushort sixd_to_16bit(int); | |
++static void xresetfontsettings(ushort mode, Font **font, int *frcflags); | |
+ static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i… | |
+-static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
++static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
+ static void xdrawglyph(Glyph, int, int); | |
+ static void xclear(int, int, int, int); | |
+ static int xgeommasktogravity(int); | |
+ static int ximopen(Display *); | |
+@@ -756,9 +758,9 @@ xresize(int col, int row) | |
+ XftDrawChange(xw.draw, xw.buf); | |
+ xclear(0, 0, win.w, win.h); | |
+ | |
+ /* resize to new width */ | |
+- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); | |
++ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4); | |
+ } | |
+ | |
+ ushort | |
+ sixd_to_16bit(int x) | |
+@@ -1061,8 +1063,11 @@ xunloadfont(Font *f) | |
+ | |
+ void | |
+ xunloadfonts(void) | |
+ { | |
++ /* Clear Harfbuzz font cache. */ | |
++ hbunloadfonts(); | |
++ | |
+ /* Free the loaded fonts in the font cache. */ | |
+ while (frclen > 0) | |
+ XftFontClose(xw.dpy, frc[--frclen].font); | |
+ | |
+@@ -1184,9 +1189,9 @@ xinit(int cols, int rows) | |
+ XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); | |
+ XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); | |
+ | |
+ /* font spec buffer */ | |
+- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); | |
++ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4); | |
+ | |
+ /* Xft rendering context */ | |
+ xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); | |
+ | |
+@@ -1240,149 +1245,162 @@ xinit(int cols, int rows) | |
+ | |
+ boxdraw_xinit(xw.dpy, xw.cmap, xw.draw, xw.vis); | |
+ } | |
+ | |
++void | |
++xresetfontsettings(ushort mode, Font **font, int *frcflags) | |
++{ | |
++ *font = &dc.font; | |
++ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
++ *font = &dc.ibfont; | |
++ *frcflags = FRC_ITALICBOLD; | |
++ } else if (mode & ATTR_ITALIC) { | |
++ *font = &dc.ifont; | |
++ *frcflags = FRC_ITALIC; | |
++ } else if (mode & ATTR_BOLD) { | |
++ *font = &dc.bfont; | |
++ *frcflags = FRC_BOLD; | |
++ } | |
++} | |
++ | |
+ int | |
+ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in… | |
+ { | |
+ float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp,… | |
+- ushort mode, prevmode = USHRT_MAX; | |
++ ushort mode = glyphs[0].mode & ~ATTR_WRAP; | |
+ Font *font = &dc.font; | |
+ int frcflags = FRC_NORMAL; | |
+- float runewidth = win.cw; | |
++ float runewidth = win.cw * ((glyphs[0].mode & ATTR_WIDE) ? 2.0f : 1.0… | |
+ Rune rune; | |
+ FT_UInt glyphidx; | |
+ FcResult fcres; | |
+ FcPattern *fcpattern, *fontpattern; | |
+ FcFontSet *fcsets[] = { NULL }; | |
+ FcCharSet *fccharset; | |
+- int i, f, numspecs = 0; | |
++ int f, code_idx, numspecs = 0; | |
++ float cluster_xp = xp, cluster_yp = yp; | |
++ HbTransformData shaped = { 0 }; | |
+ | |
+- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { | |
+- /* Fetch rune and mode for current glyph. */ | |
+- rune = glyphs[i].u; | |
+- mode = glyphs[i].mode; | |
++ /* Initial values. */ | |
++ xresetfontsettings(mode, &font, &frcflags); | |
+ | |
+- /* Skip dummy wide-character spacing. */ | |
+- if (mode == ATTR_WDUMMY) | |
++ /* Shape the segment. */ | |
++ hbtransform(&shaped, font->match, glyphs, 0, len); | |
++ xp = winx; yp = winy + font->ascent; | |
++ cluster_xp = xp; cluster_yp = yp; | |
++ | |
++ for (code_idx = 0; code_idx < shaped.count; code_idx++) { | |
++ int idx = shaped.glyphs[code_idx].cluster; | |
++ | |
++ if (glyphs[idx].mode & ATTR_WDUMMY) | |
+ continue; | |
+ | |
+- /* Determine font for glyph if different from previous glyph.… | |
+- if (prevmode != mode) { | |
+- prevmode = mode; | |
+- font = &dc.font; | |
+- frcflags = FRC_NORMAL; | |
+- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0… | |
+- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
+- font = &dc.ibfont; | |
+- frcflags = FRC_ITALICBOLD; | |
+- } else if (mode & ATTR_ITALIC) { | |
+- font = &dc.ifont; | |
+- frcflags = FRC_ITALIC; | |
+- } else if (mode & ATTR_BOLD) { | |
+- font = &dc.bfont; | |
+- frcflags = FRC_BOLD; | |
+- } | |
+- yp = winy + font->ascent; | |
++ /* Advance the drawing cursor if we've moved to a new cluster… | |
++ if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluste… | |
++ xp += runewidth; | |
++ cluster_xp = xp; | |
++ cluster_yp = yp; | |
+ } | |
+ | |
+- if (mode & ATTR_BOXDRAW) { | |
++ if (glyphs[idx].mode & ATTR_BOXDRAW) { | |
+ /* minor shoehorning: boxdraw uses only this ushort */ | |
+- glyphidx = boxdrawindex(&glyphs[i]); | |
+- } else { | |
+- /* Lookup character index with default font. */ | |
+- glyphidx = XftCharIndex(xw.dpy, font->match, rune); | |
+- } | |
+- if (glyphidx) { | |
+ specs[numspecs].font = font->match; | |
+- specs[numspecs].glyph = glyphidx; | |
+- specs[numspecs].x = (short)xp; | |
+- specs[numspecs].y = (short)yp; | |
+- xp += runewidth; | |
++ specs[numspecs].glyph = boxdrawindex(&glyphs[idx]); | |
++ specs[numspecs].x = xp; | |
++ specs[numspecs].y = yp; | |
+ numspecs++; | |
+- continue; | |
+- } | |
+- | |
+- /* Fallback on font cache, search the font cache for match. */ | |
+- for (f = 0; f < frclen; f++) { | |
+- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); | |
+- /* Everything correct. */ | |
+- if (glyphidx && frc[f].flags == frcflags) | |
+- break; | |
+- /* We got a default font for a not found glyph. */ | |
+- if (!glyphidx && frc[f].flags == frcflags | |
+- && frc[f].unicodep == rune) { | |
+- break; | |
++ } else if (shaped.glyphs[code_idx].codepoint != 0) { | |
++ /* If symbol is found, put it into the specs. */ | |
++ specs[numspecs].font = font->match; | |
++ specs[numspecs].glyph = shaped.glyphs[code_idx].codep… | |
++ specs[numspecs].x = cluster_xp + (short)(shaped.posit… | |
++ specs[numspecs].y = cluster_yp - (short)(shaped.posit… | |
++ cluster_xp += shaped.positions[code_idx].x_advance / … | |
++ cluster_yp += shaped.positions[code_idx].y_advance / … | |
++ numspecs++; | |
++ } else { | |
++ /* If it's not found, try to fetch it through the fon… | |
++ rune = glyphs[idx].u; | |
++ for (f = 0; f < frclen; f++) { | |
++ glyphidx = XftCharIndex(xw.dpy, frc[f].font, … | |
++ /* Everything correct. */ | |
++ if (glyphidx && frc[f].flags == frcflags) | |
++ break; | |
++ /* We got a default font for a not found glyp… | |
++ if (!glyphidx && frc[f].flags == frcflags | |
++ && frc[f].unicodep == rune) { | |
++ break; | |
++ } | |
+ } | |
+- } | |
+- | |
+- /* Nothing was found. Use fontconfig to find matching font. */ | |
+- if (f >= frclen) { | |
+- if (!font->set) | |
+- font->set = FcFontSort(0, font->pattern, | |
+- 1, 0, &fcres); | |
+- fcsets[0] = font->set; | |
+- | |
+- /* | |
+- * Nothing was found in the cache. Now use | |
+- * some dozen of Fontconfig calls to get the | |
+- * font for one single character. | |
+- * | |
+- * Xft and fontconfig are design failures. | |
+- */ | |
+- fcpattern = FcPatternDuplicate(font->pattern); | |
+- fccharset = FcCharSetCreate(); | |
+- | |
+- FcCharSetAddChar(fccharset, rune); | |
+- FcPatternAddCharSet(fcpattern, FC_CHARSET, | |
+- fccharset); | |
+- FcPatternAddBool(fcpattern, FC_SCALABLE, 1); | |
+- | |
+- FcConfigSubstitute(0, fcpattern, | |
+- FcMatchPattern); | |
+- FcDefaultSubstitute(fcpattern); | |
+ | |
+- fontpattern = FcFontSetMatch(0, fcsets, 1, | |
+- fcpattern, &fcres); | |
+- | |
+- /* Allocate memory for the new cache entry. */ | |
+- if (frclen >= frccap) { | |
+- frccap += 16; | |
+- frc = xrealloc(frc, frccap * sizeof(Fontcache… | |
++ /* Nothing was found. Use fontconfig to find matching… | |
++ if (f >= frclen) { | |
++ if (!font->set) | |
++ font->set = FcFontSort(0, font->patte… | |
++ 1, 0, &fcres); | |
++ fcsets[0] = font->set; | |
++ | |
++ /* | |
++ * Nothing was found in the cache. Now use | |
++ * some dozen of Fontconfig calls to get the | |
++ * font for one single character. | |
++ * | |
++ * Xft and fontconfig are design failures. | |
++ */ | |
++ fcpattern = FcPatternDuplicate(font->pattern); | |
++ fccharset = FcCharSetCreate(); | |
++ | |
++ FcCharSetAddChar(fccharset, rune); | |
++ FcPatternAddCharSet(fcpattern, FC_CHARSET, | |
++ fccharset); | |
++ FcPatternAddBool(fcpattern, FC_SCALABLE, 1); | |
++ | |
++ FcConfigSubstitute(0, fcpattern, | |
++ FcMatchPattern); | |
++ FcDefaultSubstitute(fcpattern); | |
++ | |
++ fontpattern = FcFontSetMatch(0, fcsets, 1, | |
++ fcpattern, &fcres); | |
++ | |
++ /* Allocate memory for the new cache entry. */ | |
++ if (frclen >= frccap) { | |
++ frccap += 16; | |
++ frc = xrealloc(frc, frccap * sizeof(F… | |
++ } | |
++ | |
++ frc[frclen].font = XftFontOpenPattern(xw.dpy, | |
++ fontpattern); | |
++ if (!frc[frclen].font) | |
++ die("XftFontOpenPattern failed seekin… | |
++ strerror(errno)); | |
++ frc[frclen].flags = frcflags; | |
++ frc[frclen].unicodep = rune; | |
++ | |
++ glyphidx = XftCharIndex(xw.dpy, frc[frclen].f… | |
++ | |
++ f = frclen; | |
++ frclen++; | |
++ | |
++ FcPatternDestroy(fcpattern); | |
++ FcCharSetDestroy(fccharset); | |
+ } | |
+ | |
+- frc[frclen].font = XftFontOpenPattern(xw.dpy, | |
+- fontpattern); | |
+- if (!frc[frclen].font) | |
+- die("XftFontOpenPattern failed seeking fallba… | |
+- strerror(errno)); | |
+- frc[frclen].flags = frcflags; | |
+- frc[frclen].unicodep = rune; | |
+- | |
+- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run… | |
+- | |
+- f = frclen; | |
+- frclen++; | |
+- | |
+- FcPatternDestroy(fcpattern); | |
+- FcCharSetDestroy(fccharset); | |
++ specs[numspecs].font = frc[f].font; | |
++ specs[numspecs].glyph = glyphidx; | |
++ specs[numspecs].x = (short)xp; | |
++ specs[numspecs].y = (short)yp; | |
++ numspecs++; | |
+ } | |
+- | |
+- specs[numspecs].font = frc[f].font; | |
+- specs[numspecs].glyph = glyphidx; | |
+- specs[numspecs].x = (short)xp; | |
+- specs[numspecs].y = (short)yp; | |
+- xp += runewidth; | |
+- numspecs++; | |
+ } | |
+ | |
++ /* Cleanup and get ready for next segment. */ | |
++ hbcleanup(&shaped); | |
+ return numspecs; | |
+ } | |
+ | |
+ void | |
+-xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
++xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
+ { | |
+- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); | |
+ int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, | |
+ width = charlen * win.cw; | |
+ Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; | |
+ XRenderColor colfg, colbg; | |
+@@ -1520,23 +1538,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Gly… | |
+ void | |
+ xdrawglyph(Glyph g, int x, int y) | |
+ { | |
+ int numspecs; | |
+- XftGlyphFontSpec spec; | |
++ XftGlyphFontSpec *specs = xw.specbuf; | |
+ | |
+- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); | |
+- xdrawglyphfontspecs(&spec, g, numspecs, x, y); | |
++ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y); | |
++ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? … | |
+ } | |
+ | |
+ void | |
+-xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) | |
++xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int… | |
+ { | |
+ Color drawcol; | |
+ | |
+ /* remove the old cursor */ | |
+ if (selected(ox, oy)) | |
+ og.mode ^= ATTR_REVERSE; | |
+- xdrawglyph(og, ox, oy); | |
++ | |
++ /* Redraw the line where cursor was previously. | |
++ * It will restore the ligatures broken by the cursor. */ | |
++ xdrawline(line, 0, oy, len); | |
+ | |
+ if (IS_SET(MODE_HIDE)) | |
+ return; | |
+ | |
+@@ -1668,30 +1689,30 @@ xdrawline(Line line, int x1, int y1, int x2) | |
+ int i, x, ox, numspecs; | |
+ Glyph base, new; | |
+ XftGlyphFontSpec *specs = xw.specbuf; | |
+ | |
+- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); | |
+ i = ox = 0; | |
+- for (x = x1; x < x2 && i < numspecs; x++) { | |
++ for (x = x1; x < x2; x++) { | |
+ new = line[x]; | |
+ if (new.mode == ATTR_WDUMMY) | |
+ continue; | |
+ if (selected(x, y1)) | |
+ new.mode ^= ATTR_REVERSE; | |
+- if (i > 0 && ATTRCMP(base, new)) { | |
+- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
+- specs += i; | |
+- numspecs -= i; | |
++ if ((i > 0) && ATTRCMP(base, new)) { | |
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - … | |
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x … | |
+ i = 0; | |
+ } | |
+ if (i == 0) { | |
+ ox = x; | |
+ base = new; | |
+ } | |
+ i++; | |
+ } | |
+- if (i > 0) | |
+- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
++ if (i > 0) { | |
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,… | |
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox); | |
++ } | |
+ } | |
+ | |
+ void | |
+ xfinishdraw(void) | |
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-scrollback-20… | |
@@ -1,634 +0,0 @@ | |
-diff --git a/Makefile b/Makefile | |
-index 470ac86..38240da 100644 | |
---- a/Makefile | |
-+++ b/Makefile | |
-@@ -4,7 +4,7 @@ | |
- | |
- include config.mk | |
- | |
--SRC = st.c x.c | |
-+SRC = st.c x.c hb.c | |
- OBJ = $(SRC:.c=.o) | |
- | |
- all: st | |
-@@ -22,7 +22,8 @@ config.h: | |
- $(CC) $(STCFLAGS) -c $< | |
- | |
- st.o: config.h st.h win.h | |
--x.o: arg.h config.h st.h win.h | |
-+x.o: arg.h config.h st.h win.h hb.h | |
-+hb.o: st.h | |
- | |
- $(OBJ): config.h config.mk | |
- | |
-diff --git a/config.mk b/config.mk | |
-index 1e306f8..3e13e53 100644 | |
---- a/config.mk | |
-+++ b/config.mk | |
-@@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config | |
- # includes and libs | |
- INCS = -I$(X11INC) \ | |
- `$(PKG_CONFIG) --cflags fontconfig` \ | |
-- `$(PKG_CONFIG) --cflags freetype2` | |
-+ `$(PKG_CONFIG) --cflags freetype2` \ | |
-+ `$(PKG_CONFIG) --cflags harfbuzz` | |
- LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ | |
- `$(PKG_CONFIG) --libs fontconfig` \ | |
-- `$(PKG_CONFIG) --libs freetype2` | |
-+ `$(PKG_CONFIG) --libs freetype2` \ | |
-+ `$(PKG_CONFIG) --libs harfbuzz` | |
- | |
- # flags | |
- STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 | |
-diff --git a/hb.c b/hb.c | |
-new file mode 100644 | |
-index 0000000..99412c8 | |
---- /dev/null | |
-+++ b/hb.c | |
-@@ -0,0 +1,125 @@ | |
-+#include <stdlib.h> | |
-+#include <stdio.h> | |
-+#include <math.h> | |
-+#include <X11/Xft/Xft.h> | |
-+#include <X11/cursorfont.h> | |
-+#include <hb.h> | |
-+#include <hb-ft.h> | |
-+ | |
-+#include "st.h" | |
-+#include "hb.h" | |
-+ | |
-+#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start… | |
-+#define BUFFER_STEP 256 | |
-+ | |
-+hb_font_t *hbfindfont(XftFont *match); | |
-+ | |
-+typedef struct { | |
-+ XftFont *match; | |
-+ hb_font_t *font; | |
-+} HbFontMatch; | |
-+ | |
-+typedef struct { | |
-+ size_t capacity; | |
-+ HbFontMatch *fonts; | |
-+} HbFontCache; | |
-+ | |
-+static HbFontCache hbfontcache = { 0, NULL }; | |
-+ | |
-+typedef struct { | |
-+ size_t capacity; | |
-+ Rune *runes; | |
-+} RuneBuffer; | |
-+ | |
-+static RuneBuffer hbrunebuffer = { 0, NULL }; | |
-+ | |
-+/* | |
-+ * Poplulate the array with a list of font features, wrapped in FEATURE macro, | |
-+ * e. g. | |
-+ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') | |
-+ */ | |
-+hb_feature_t features[] = { }; | |
-+ | |
-+void | |
-+hbunloadfonts() | |
-+{ | |
-+ for (int i = 0; i < hbfontcache.capacity; i++) { | |
-+ hb_font_destroy(hbfontcache.fonts[i].font); | |
-+ XftUnlockFace(hbfontcache.fonts[i].match); | |
-+ } | |
-+ | |
-+ if (hbfontcache.fonts != NULL) { | |
-+ free(hbfontcache.fonts); | |
-+ hbfontcache.fonts = NULL; | |
-+ } | |
-+ hbfontcache.capacity = 0; | |
-+} | |
-+ | |
-+hb_font_t * | |
-+hbfindfont(XftFont *match) | |
-+{ | |
-+ for (int i = 0; i < hbfontcache.capacity; i++) { | |
-+ if (hbfontcache.fonts[i].match == match) | |
-+ return hbfontcache.fonts[i].font; | |
-+ } | |
-+ | |
-+ /* Font not found in cache, caching it now. */ | |
-+ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * … | |
-+ FT_Face face = XftLockFace(match); | |
-+ hb_font_t *font = hb_ft_font_create(face, NULL); | |
-+ if (font == NULL) | |
-+ die("Failed to load Harfbuzz font."); | |
-+ | |
-+ hbfontcache.fonts[hbfontcache.capacity].match = match; | |
-+ hbfontcache.fonts[hbfontcache.capacity].font = font; | |
-+ hbfontcache.capacity += 1; | |
-+ | |
-+ return font; | |
-+} | |
-+ | |
-+void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, … | |
-+ ushort mode = USHRT_MAX; | |
-+ unsigned int glyph_count; | |
-+ int rune_idx, glyph_idx, end = start + length; | |
-+ | |
-+ hb_font_t *font = hbfindfont(xfont); | |
-+ if (font == NULL) | |
-+ return; | |
-+ | |
-+ hb_buffer_t *buffer = hb_buffer_create(); | |
-+ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); | |
-+ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_… | |
-+ | |
-+ /* Resize the buffer if required length is larger. */ | |
-+ if (hbrunebuffer.capacity < length) { | |
-+ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S… | |
-+ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer… | |
-+ } | |
-+ | |
-+ /* Fill buffer with codepoints. */ | |
-+ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r… | |
-+ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u; | |
-+ mode = glyphs[glyph_idx].mode; | |
-+ if (mode & ATTR_WDUMMY) | |
-+ hbrunebuffer.runes[rune_idx] = 0x0020; | |
-+ } | |
-+ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt… | |
-+ | |
-+ /* Shape the segment. */ | |
-+ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t… | |
-+ | |
-+ /* Get new glyph info. */ | |
-+ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun… | |
-+ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly… | |
-+ | |
-+ /* Fill the output. */ | |
-+ data->buffer = buffer; | |
-+ data->glyphs = info; | |
-+ data->positions = pos; | |
-+ data->count = glyph_count; | |
-+} | |
-+ | |
-+void hbcleanup(HbTransformData *data) { | |
-+ hb_buffer_destroy(data->buffer); | |
-+ memset(data, 0, sizeof(HbTransformData)); | |
-+} | |
-diff --git a/hb.h b/hb.h | |
-new file mode 100644 | |
-index 0000000..3b0ef44 | |
---- /dev/null | |
-+++ b/hb.h | |
-@@ -0,0 +1,14 @@ | |
-+#include <X11/Xft/Xft.h> | |
-+#include <hb.h> | |
-+#include <hb-ft.h> | |
-+ | |
-+typedef struct { | |
-+ hb_buffer_t *buffer; | |
-+ hb_glyph_info_t *glyphs; | |
-+ hb_glyph_position_t *positions; | |
-+ unsigned int count; | |
-+} HbTransformData; | |
-+ | |
-+void hbunloadfonts(); | |
-+void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int); | |
-+void hbcleanup(HbTransformData *); | |
-diff --git a/st.c b/st.c | |
-index 79ee9ba..7675db6 100644 | |
---- a/st.c | |
-+++ b/st.c | |
-@@ -2711,7 +2711,8 @@ draw(void) | |
- drawregion(0, 0, term.col, term.row); | |
- if (term.scr == 0) | |
- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], | |
-- term.ocx, term.ocy, term.line[term.ocy][term.… | |
-+ term.ocx, term.ocy, term.line[term.ocy][term.… | |
-+ term.line[term.ocy], term.col); | |
- term.ocx = cx; | |
- term.ocy = term.c.y; | |
- xfinishdraw(); | |
-diff --git a/st.h b/st.h | |
-index 818a6f8..4e584b6 100644 | |
---- a/st.h | |
-+++ b/st.h | |
-@@ -11,7 +11,8 @@ | |
- #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) | |
- #define DEFAULT(a, b) (a) = (a) ? (a) : (b) | |
- #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)… | |
--#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f… | |
-+#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode … | |
-+ (a).fg != (b).fg || \ | |
- (a).bg != (b).bg) | |
- #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ | |
- (t1.tv_nsec-t2.tv_nsec)/1E6) | |
-diff --git a/win.h b/win.h | |
-index 6de960d..94679e4 100644 | |
---- a/win.h | |
-+++ b/win.h | |
-@@ -25,7 +25,7 @@ enum win_mode { | |
- | |
- void xbell(void); | |
- void xclipcopy(void); | |
--void xdrawcursor(int, int, Glyph, int, int, Glyph); | |
-+void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int); | |
- void xdrawline(Line, int, int, int); | |
- void xfinishdraw(void); | |
- void xloadcols(void); | |
-diff --git a/x.c b/x.c | |
-index 2a3bd38..0bb51ff 100644 | |
---- a/x.c | |
-+++ b/x.c | |
-@@ -19,6 +19,7 @@ char *argv0; | |
- #include "arg.h" | |
- #include "st.h" | |
- #include "win.h" | |
-+#include "hb.h" | |
- | |
- /* types used in config.h */ | |
- typedef struct { | |
-@@ -141,8 +142,9 @@ typedef struct { | |
- } DC; | |
- | |
- static inline ushort sixd_to_16bit(int); | |
-+static void xresetfontsettings(ushort mode, Font **font, int *frcflags); | |
- static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i… | |
--static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
-+static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
- static void xdrawglyph(Glyph, int, int); | |
- static void xclear(int, int, int, int); | |
- static int xgeommasktogravity(int); | |
-@@ -757,7 +759,7 @@ xresize(int col, int row) | |
- xclear(0, 0, win.w, win.h); | |
- | |
- /* resize to new width */ | |
-- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); | |
-+ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4); | |
- } | |
- | |
- ushort | |
-@@ -1062,6 +1064,9 @@ xunloadfont(Font *f) | |
- void | |
- xunloadfonts(void) | |
- { | |
-+ /* Clear Harfbuzz font cache. */ | |
-+ hbunloadfonts(); | |
-+ | |
- /* Free the loaded fonts in the font cache. */ | |
- while (frclen > 0) | |
- XftFontClose(xw.dpy, frc[--frclen].font); | |
-@@ -1185,7 +1190,7 @@ xinit(int cols, int rows) | |
- XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); | |
- | |
- /* font spec buffer */ | |
-- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); | |
-+ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4); | |
- | |
- /* Xft rendering context */ | |
- xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); | |
-@@ -1239,6 +1244,22 @@ xinit(int cols, int rows) | |
- xsel.xtarget = XA_STRING; | |
- } | |
- | |
-+void | |
-+xresetfontsettings(ushort mode, Font **font, int *frcflags) | |
-+{ | |
-+ *font = &dc.font; | |
-+ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
-+ *font = &dc.ibfont; | |
-+ *frcflags = FRC_ITALICBOLD; | |
-+ } else if (mode & ATTR_ITALIC) { | |
-+ *font = &dc.ifont; | |
-+ *frcflags = FRC_ITALIC; | |
-+ } else if (mode & ATTR_BOLD) { | |
-+ *font = &dc.bfont; | |
-+ *frcflags = FRC_BOLD; | |
-+ } | |
-+} | |
-+ | |
- int | |
- xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in… | |
- { | |
-@@ -1253,128 +1274,157 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const G… | |
- FcPattern *fcpattern, *fontpattern; | |
- FcFontSet *fcsets[] = { NULL }; | |
- FcCharSet *fccharset; | |
-- int i, f, numspecs = 0; | |
-+ int i, f, length = 0, start = 0, numspecs = 0; | |
-+ float cluster_xp = xp, cluster_yp = yp; | |
-+ HbTransformData shaped = { 0 }; | |
-+ | |
-+ /* Initial values. */ | |
-+ mode = prevmode = glyphs[0].mode & ~ATTR_WRAP; | |
-+ xresetfontsettings(mode, &font, &frcflags); | |
- | |
- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { | |
-- /* Fetch rune and mode for current glyph. */ | |
-- rune = glyphs[i].u; | |
-- mode = glyphs[i].mode; | |
-+ mode = glyphs[i].mode & ~ATTR_WRAP; | |
- | |
- /* Skip dummy wide-character spacing. */ | |
-- if (mode == ATTR_WDUMMY) | |
-+ if (mode & ATTR_WDUMMY && i < (len - 1)) | |
- continue; | |
- | |
-- /* Determine font for glyph if different from previous glyph.… | |
-- if (prevmode != mode) { | |
-- prevmode = mode; | |
-- font = &dc.font; | |
-- frcflags = FRC_NORMAL; | |
-- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0… | |
-- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
-- font = &dc.ibfont; | |
-- frcflags = FRC_ITALICBOLD; | |
-- } else if (mode & ATTR_ITALIC) { | |
-- font = &dc.ifont; | |
-- frcflags = FRC_ITALIC; | |
-- } else if (mode & ATTR_BOLD) { | |
-- font = &dc.bfont; | |
-- frcflags = FRC_BOLD; | |
-+ if ( | |
-+ prevmode != mode | |
-+ || ATTRCMP(glyphs[start], glyphs[i]) | |
-+ || selected(x + i, y) != selected(x + start, y) | |
-+ || i == (len - 1) | |
-+ ) { | |
-+ /* Handle 1-character wide segments and end of line */ | |
-+ length = i - start; | |
-+ if (i == start) { | |
-+ length = 1; | |
-+ } else if (i == (len - 1)) { | |
-+ length = (i - start + 1); | |
- } | |
-- yp = winy + font->ascent; | |
-- } | |
- | |
-- /* Lookup character index with default font. */ | |
-- glyphidx = XftCharIndex(xw.dpy, font->match, rune); | |
-- if (glyphidx) { | |
-- specs[numspecs].font = font->match; | |
-- specs[numspecs].glyph = glyphidx; | |
-- specs[numspecs].x = (short)xp; | |
-- specs[numspecs].y = (short)yp; | |
-- xp += runewidth; | |
-- numspecs++; | |
-- continue; | |
-- } | |
-- | |
-- /* Fallback on font cache, search the font cache for match. */ | |
-- for (f = 0; f < frclen; f++) { | |
-- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); | |
-- /* Everything correct. */ | |
-- if (glyphidx && frc[f].flags == frcflags) | |
-- break; | |
-- /* We got a default font for a not found glyph. */ | |
-- if (!glyphidx && frc[f].flags == frcflags | |
-- && frc[f].unicodep == rune) { | |
-- break; | |
-+ /* Shape the segment. */ | |
-+ hbtransform(&shaped, font->match, glyphs, start, leng… | |
-+ runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE… | |
-+ cluster_xp = xp; cluster_yp = yp; | |
-+ for (int code_idx = 0; code_idx < shaped.count; code_… | |
-+ int idx = shaped.glyphs[code_idx].cluster; | |
-+ | |
-+ if (glyphs[start + idx].mode & ATTR_WDUMMY) | |
-+ continue; | |
-+ | |
-+ /* Advance the drawing cursor if we've moved … | |
-+ if (code_idx > 0 && idx != shaped.glyphs[code… | |
-+ xp += runewidth; | |
-+ cluster_xp = xp; | |
-+ cluster_yp = yp; | |
-+ runewidth = win.cw * ((glyphs[start +… | |
-+ } | |
-+ | |
-+ if (shaped.glyphs[code_idx].codepoint != 0) { | |
-+ /* If symbol is found, put it into th… | |
-+ specs[numspecs].font = font->match; | |
-+ specs[numspecs].glyph = shaped.glyphs… | |
-+ specs[numspecs].x = cluster_xp + (sho… | |
-+ specs[numspecs].y = cluster_yp - (sho… | |
-+ cluster_xp += shaped.positions[code_i… | |
-+ cluster_yp += shaped.positions[code_i… | |
-+ numspecs++; | |
-+ } else { | |
-+ /* If it's not found, try to fetch it… | |
-+ rune = glyphs[start + idx].u; | |
-+ for (f = 0; f < frclen; f++) { | |
-+ glyphidx = XftCharIndex(xw.dp… | |
-+ /* Everything correct. */ | |
-+ if (glyphidx && frc[f].flags … | |
-+ break; | |
-+ /* We got a default font for … | |
-+ if (!glyphidx && frc[f].flags… | |
-+ && frc[f].uni… | |
-+ break; | |
-+ } | |
-+ } | |
-+ | |
-+ /* Nothing was found. Use fontconfig … | |
-+ if (f >= frclen) { | |
-+ if (!font->set) | |
-+ font->set = FcFontSor… | |
-+ … | |
-+ fcsets[0] = font->set; | |
-+ | |
-+ /* | |
-+ * Nothing was found in the c… | |
-+ * some dozen of Fontconfig c… | |
-+ * font for one single charac… | |
-+ * | |
-+ * Xft and fontconfig are des… | |
-+ */ | |
-+ fcpattern = FcPatternDuplicat… | |
-+ fccharset = FcCharSetCreate(); | |
-+ | |
-+ FcCharSetAddChar(fccharset, r… | |
-+ FcPatternAddCharSet(fcpattern… | |
-+ fccharset); | |
-+ FcPatternAddBool(fcpattern, F… | |
-+ | |
-+ FcConfigSubstitute(0, fcpatte… | |
-+ FcMatchPatter… | |
-+ FcDefaultSubstitute(fcpattern… | |
-+ | |
-+ fontpattern = FcFontSetMatch(… | |
-+ fcpattern, &f… | |
-+ | |
-+ /* Allocate memory for the ne… | |
-+ if (frclen >= frccap) { | |
-+ frccap += 16; | |
-+ frc = xrealloc(frc, f… | |
-+ } | |
-+ | |
-+ frc[frclen].font = XftFontOpe… | |
-+ fontpattern); | |
-+ if (!frc[frclen].font) | |
-+ die("XftFontOpenPatte… | |
-+ strerror(errn… | |
-+ frc[frclen].flags = frcflags; | |
-+ frc[frclen].unicodep = rune; | |
-+ | |
-+ glyphidx = XftCharIndex(xw.dp… | |
-+ | |
-+ f = frclen; | |
-+ frclen++; | |
-+ | |
-+ FcPatternDestroy(fcpattern); | |
-+ FcCharSetDestroy(fccharset); | |
-+ } | |
-+ | |
-+ specs[numspecs].font = frc[f].font; | |
-+ specs[numspecs].glyph = glyphidx; | |
-+ specs[numspecs].x = (short)xp; | |
-+ specs[numspecs].y = (short)yp; | |
-+ numspecs++; | |
-+ } | |
- } | |
-- } | |
- | |
-- /* Nothing was found. Use fontconfig to find matching font. */ | |
-- if (f >= frclen) { | |
-- if (!font->set) | |
-- font->set = FcFontSort(0, font->pattern, | |
-- 1, 0, &fcres); | |
-- fcsets[0] = font->set; | |
-+ /* Cleanup and get ready for next segment. */ | |
-+ hbcleanup(&shaped); | |
-+ start = i; | |
- | |
-- /* | |
-- * Nothing was found in the cache. Now use | |
-- * some dozen of Fontconfig calls to get the | |
-- * font for one single character. | |
-- * | |
-- * Xft and fontconfig are design failures. | |
-- */ | |
-- fcpattern = FcPatternDuplicate(font->pattern); | |
-- fccharset = FcCharSetCreate(); | |
-- | |
-- FcCharSetAddChar(fccharset, rune); | |
-- FcPatternAddCharSet(fcpattern, FC_CHARSET, | |
-- fccharset); | |
-- FcPatternAddBool(fcpattern, FC_SCALABLE, 1); | |
-- | |
-- FcConfigSubstitute(0, fcpattern, | |
-- FcMatchPattern); | |
-- FcDefaultSubstitute(fcpattern); | |
-- | |
-- fontpattern = FcFontSetMatch(0, fcsets, 1, | |
-- fcpattern, &fcres); | |
-- | |
-- /* Allocate memory for the new cache entry. */ | |
-- if (frclen >= frccap) { | |
-- frccap += 16; | |
-- frc = xrealloc(frc, frccap * sizeof(Fontcache… | |
-+ /* Determine font for glyph if different from previou… | |
-+ if (prevmode != mode) { | |
-+ prevmode = mode; | |
-+ xresetfontsettings(mode, &font, &frcflags); | |
-+ yp = winy + font->ascent; | |
- } | |
-- | |
-- frc[frclen].font = XftFontOpenPattern(xw.dpy, | |
-- fontpattern); | |
-- if (!frc[frclen].font) | |
-- die("XftFontOpenPattern failed seeking fallba… | |
-- strerror(errno)); | |
-- frc[frclen].flags = frcflags; | |
-- frc[frclen].unicodep = rune; | |
-- | |
-- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run… | |
-- | |
-- f = frclen; | |
-- frclen++; | |
-- | |
-- FcPatternDestroy(fcpattern); | |
-- FcCharSetDestroy(fccharset); | |
- } | |
-- | |
-- specs[numspecs].font = frc[f].font; | |
-- specs[numspecs].glyph = glyphidx; | |
-- specs[numspecs].x = (short)xp; | |
-- specs[numspecs].y = (short)yp; | |
-- xp += runewidth; | |
-- numspecs++; | |
- } | |
- | |
-+ hbcleanup(&shaped); | |
- return numspecs; | |
- } | |
- | |
- void | |
--xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
-+xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
- { | |
-- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); | |
- int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, | |
- width = charlen * win.cw; | |
- Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; | |
-@@ -1510,21 +1560,24 @@ void | |
- xdrawglyph(Glyph g, int x, int y) | |
- { | |
- int numspecs; | |
-- XftGlyphFontSpec spec; | |
-+ XftGlyphFontSpec *specs = xw.specbuf; | |
- | |
-- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); | |
-- xdrawglyphfontspecs(&spec, g, numspecs, x, y); | |
-+ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y); | |
-+ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? … | |
- } | |
- | |
- void | |
--xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) | |
-+xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int… | |
- { | |
- Color drawcol; | |
- | |
- /* remove the old cursor */ | |
- if (selected(ox, oy)) | |
- og.mode ^= ATTR_REVERSE; | |
-- xdrawglyph(og, ox, oy); | |
-+ | |
-+ /* Redraw the line where cursor was previously. | |
-+ * It will restore the ligatures broken by the cursor. */ | |
-+ xdrawline(line, 0, oy, len); | |
- | |
- if (IS_SET(MODE_HIDE)) | |
- return; | |
-@@ -1652,18 +1705,16 @@ xdrawline(Line line, int x1, int y1, int x2) | |
- Glyph base, new; | |
- XftGlyphFontSpec *specs = xw.specbuf; | |
- | |
-- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); | |
- i = ox = 0; | |
-- for (x = x1; x < x2 && i < numspecs; x++) { | |
-+ for (x = x1; x < x2; x++) { | |
- new = line[x]; | |
- if (new.mode == ATTR_WDUMMY) | |
- continue; | |
- if (selected(x, y1)) | |
- new.mode ^= ATTR_REVERSE; | |
-- if (i > 0 && ATTRCMP(base, new)) { | |
-- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
-- specs += i; | |
-- numspecs -= i; | |
-+ if ((i > 0) && ATTRCMP(base, new)) { | |
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - … | |
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x … | |
- i = 0; | |
- } | |
- if (i == 0) { | |
-@@ -1672,8 +1723,10 @@ xdrawline(Line line, int x1, int y1, int x2) | |
- } | |
- i++; | |
- } | |
-- if (i > 0) | |
-- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
-+ if (i > 0) { | |
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,… | |
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox); | |
-+ } | |
- } | |
- | |
- void | |
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-scrollback-20… | |
@@ -0,0 +1,650 @@ | |
+diff --git a/Makefile b/Makefile | |
+index 15db421..dfcea0f 100644 | |
+--- a/Makefile | |
++++ b/Makefile | |
+@@ -3,9 +3,9 @@ | |
+ .POSIX: | |
+ | |
+ include config.mk | |
+ | |
+-SRC = st.c x.c | |
++SRC = st.c x.c hb.c | |
+ OBJ = $(SRC:.c=.o) | |
+ | |
+ all: st | |
+ | |
+@@ -15,9 +15,10 @@ config.h: | |
+ .c.o: | |
+ $(CC) $(STCFLAGS) -c $< | |
+ | |
+ st.o: config.h st.h win.h | |
+-x.o: arg.h config.h st.h win.h | |
++x.o: arg.h config.h st.h win.h hb.h | |
++hb.o: st.h | |
+ | |
+ $(OBJ): config.h config.mk | |
+ | |
+ st: $(OBJ) | |
+diff --git a/config.mk b/config.mk | |
+index fdc29a7..6833b3b 100644 | |
+--- a/config.mk | |
++++ b/config.mk | |
+@@ -14,12 +14,14 @@ PKG_CONFIG = pkg-config | |
+ | |
+ # includes and libs | |
+ INCS = -I$(X11INC) \ | |
+ `$(PKG_CONFIG) --cflags fontconfig` \ | |
+- `$(PKG_CONFIG) --cflags freetype2` | |
++ `$(PKG_CONFIG) --cflags freetype2` \ | |
++ `$(PKG_CONFIG) --cflags harfbuzz` | |
+ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ | |
+ `$(PKG_CONFIG) --libs fontconfig` \ | |
+- `$(PKG_CONFIG) --libs freetype2` | |
++ `$(PKG_CONFIG) --libs freetype2` \ | |
++ `$(PKG_CONFIG) --libs harfbuzz` | |
+ | |
+ # flags | |
+ STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 | |
+ STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS) | |
+@@ -28,9 +30,10 @@ STLDFLAGS = $(LIBS) $(LDFLAGS) | |
+ # OpenBSD: | |
+ #CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE | |
+ #LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \ | |
+ # `$(PKG_CONFIG) --libs fontconfig` \ | |
+-# `$(PKG_CONFIG) --libs freetype2` | |
++# `$(PKG_CONFIG) --libs freetype2` \ | |
++# `$(PKG_CONFIG) --libs harfbuzz` | |
+ #MANPREFIX = ${PREFIX}/man | |
+ | |
+ # compiler and linker | |
+ # CC = c99 | |
+diff --git a/hb.c b/hb.c | |
+new file mode 100644 | |
+index 0000000..99412c8 | |
+--- /dev/null | |
++++ b/hb.c | |
+@@ -0,0 +1,125 @@ | |
++#include <stdlib.h> | |
++#include <stdio.h> | |
++#include <math.h> | |
++#include <X11/Xft/Xft.h> | |
++#include <X11/cursorfont.h> | |
++#include <hb.h> | |
++#include <hb-ft.h> | |
++ | |
++#include "st.h" | |
++#include "hb.h" | |
++ | |
++#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start… | |
++#define BUFFER_STEP 256 | |
++ | |
++hb_font_t *hbfindfont(XftFont *match); | |
++ | |
++typedef struct { | |
++ XftFont *match; | |
++ hb_font_t *font; | |
++} HbFontMatch; | |
++ | |
++typedef struct { | |
++ size_t capacity; | |
++ HbFontMatch *fonts; | |
++} HbFontCache; | |
++ | |
++static HbFontCache hbfontcache = { 0, NULL }; | |
++ | |
++typedef struct { | |
++ size_t capacity; | |
++ Rune *runes; | |
++} RuneBuffer; | |
++ | |
++static RuneBuffer hbrunebuffer = { 0, NULL }; | |
++ | |
++/* | |
++ * Poplulate the array with a list of font features, wrapped in FEATURE macro, | |
++ * e. g. | |
++ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') | |
++ */ | |
++hb_feature_t features[] = { }; | |
++ | |
++void | |
++hbunloadfonts() | |
++{ | |
++ for (int i = 0; i < hbfontcache.capacity; i++) { | |
++ hb_font_destroy(hbfontcache.fonts[i].font); | |
++ XftUnlockFace(hbfontcache.fonts[i].match); | |
++ } | |
++ | |
++ if (hbfontcache.fonts != NULL) { | |
++ free(hbfontcache.fonts); | |
++ hbfontcache.fonts = NULL; | |
++ } | |
++ hbfontcache.capacity = 0; | |
++} | |
++ | |
++hb_font_t * | |
++hbfindfont(XftFont *match) | |
++{ | |
++ for (int i = 0; i < hbfontcache.capacity; i++) { | |
++ if (hbfontcache.fonts[i].match == match) | |
++ return hbfontcache.fonts[i].font; | |
++ } | |
++ | |
++ /* Font not found in cache, caching it now. */ | |
++ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * … | |
++ FT_Face face = XftLockFace(match); | |
++ hb_font_t *font = hb_ft_font_create(face, NULL); | |
++ if (font == NULL) | |
++ die("Failed to load Harfbuzz font."); | |
++ | |
++ hbfontcache.fonts[hbfontcache.capacity].match = match; | |
++ hbfontcache.fonts[hbfontcache.capacity].font = font; | |
++ hbfontcache.capacity += 1; | |
++ | |
++ return font; | |
++} | |
++ | |
++void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, … | |
++ ushort mode = USHRT_MAX; | |
++ unsigned int glyph_count; | |
++ int rune_idx, glyph_idx, end = start + length; | |
++ | |
++ hb_font_t *font = hbfindfont(xfont); | |
++ if (font == NULL) | |
++ return; | |
++ | |
++ hb_buffer_t *buffer = hb_buffer_create(); | |
++ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); | |
++ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_… | |
++ | |
++ /* Resize the buffer if required length is larger. */ | |
++ if (hbrunebuffer.capacity < length) { | |
++ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S… | |
++ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer… | |
++ } | |
++ | |
++ /* Fill buffer with codepoints. */ | |
++ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r… | |
++ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u; | |
++ mode = glyphs[glyph_idx].mode; | |
++ if (mode & ATTR_WDUMMY) | |
++ hbrunebuffer.runes[rune_idx] = 0x0020; | |
++ } | |
++ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt… | |
++ | |
++ /* Shape the segment. */ | |
++ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t… | |
++ | |
++ /* Get new glyph info. */ | |
++ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun… | |
++ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly… | |
++ | |
++ /* Fill the output. */ | |
++ data->buffer = buffer; | |
++ data->glyphs = info; | |
++ data->positions = pos; | |
++ data->count = glyph_count; | |
++} | |
++ | |
++void hbcleanup(HbTransformData *data) { | |
++ hb_buffer_destroy(data->buffer); | |
++ memset(data, 0, sizeof(HbTransformData)); | |
++} | |
+diff --git a/hb.h b/hb.h | |
+new file mode 100644 | |
+index 0000000..3b0ef44 | |
+--- /dev/null | |
++++ b/hb.h | |
+@@ -0,0 +1,14 @@ | |
++#include <X11/Xft/Xft.h> | |
++#include <hb.h> | |
++#include <hb-ft.h> | |
++ | |
++typedef struct { | |
++ hb_buffer_t *buffer; | |
++ hb_glyph_info_t *glyphs; | |
++ hb_glyph_position_t *positions; | |
++ unsigned int count; | |
++} HbTransformData; | |
++ | |
++void hbunloadfonts(); | |
++void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int); | |
++void hbcleanup(HbTransformData *); | |
+diff --git a/st.c b/st.c | |
+index 2478942..bba90d3 100644 | |
+--- a/st.c | |
++++ b/st.c | |
+@@ -2729,9 +2729,10 @@ draw(void) | |
+ | |
+ drawregion(0, 0, term.col, term.row); | |
+ if (term.scr == 0) | |
+ xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], | |
+- term.ocx, term.ocy, term.line[term.ocy][term.… | |
++ term.ocx, term.ocy, term.line[term.ocy][term.… | |
++ term.line[term.ocy], term.col); | |
+ term.ocx = cx; | |
+ term.ocy = term.c.y; | |
+ xfinishdraw(); | |
+ if (ocx != term.ocx || ocy != term.ocy) | |
+diff --git a/st.h b/st.h | |
+index 818a6f8..4e584b6 100644 | |
+--- a/st.h | |
++++ b/st.h | |
+@@ -10,9 +10,10 @@ | |
+ #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) | |
+ #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) | |
+ #define DEFAULT(a, b) (a) = (a) ? (a) : (b) | |
+ #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)… | |
+-#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f… | |
++#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode … | |
++ (a).fg != (b).fg || \ | |
+ (a).bg != (b).bg) | |
+ #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ | |
+ (t1.tv_nsec-t2.tv_nsec)/1E6) | |
+ #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) | |
+diff --git a/win.h b/win.h | |
+index 6de960d..94679e4 100644 | |
+--- a/win.h | |
++++ b/win.h | |
+@@ -24,9 +24,9 @@ enum win_mode { | |
+ }; | |
+ | |
+ void xbell(void); | |
+ void xclipcopy(void); | |
+-void xdrawcursor(int, int, Glyph, int, int, Glyph); | |
++void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int); | |
+ void xdrawline(Line, int, int, int); | |
+ void xfinishdraw(void); | |
+ void xloadcols(void); | |
+ int xsetcolorname(int, const char *); | |
+diff --git a/x.c b/x.c | |
+index bd23686..2bf3b72 100644 | |
+--- a/x.c | |
++++ b/x.c | |
+@@ -18,8 +18,9 @@ | |
+ char *argv0; | |
+ #include "arg.h" | |
+ #include "st.h" | |
+ #include "win.h" | |
++#include "hb.h" | |
+ | |
+ /* types used in config.h */ | |
+ typedef struct { | |
+ uint mod; | |
+@@ -140,10 +141,11 @@ typedef struct { | |
+ GC gc; | |
+ } DC; | |
+ | |
+ static inline ushort sixd_to_16bit(int); | |
++static void xresetfontsettings(ushort mode, Font **font, int *frcflags); | |
+ static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i… | |
+-static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
++static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
+ static void xdrawglyph(Glyph, int, int); | |
+ static void xclear(int, int, int, int); | |
+ static int xgeommasktogravity(int); | |
+ static int ximopen(Display *); | |
+@@ -756,9 +758,9 @@ xresize(int col, int row) | |
+ XftDrawChange(xw.draw, xw.buf); | |
+ xclear(0, 0, win.w, win.h); | |
+ | |
+ /* resize to new width */ | |
+- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); | |
++ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4); | |
+ } | |
+ | |
+ ushort | |
+ sixd_to_16bit(int x) | |
+@@ -1061,8 +1063,11 @@ xunloadfont(Font *f) | |
+ | |
+ void | |
+ xunloadfonts(void) | |
+ { | |
++ /* Clear Harfbuzz font cache. */ | |
++ hbunloadfonts(); | |
++ | |
+ /* Free the loaded fonts in the font cache. */ | |
+ while (frclen > 0) | |
+ XftFontClose(xw.dpy, frc[--frclen].font); | |
+ | |
+@@ -1184,9 +1189,9 @@ xinit(int cols, int rows) | |
+ XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); | |
+ XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); | |
+ | |
+ /* font spec buffer */ | |
+- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); | |
++ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4); | |
+ | |
+ /* Xft rendering context */ | |
+ xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); | |
+ | |
+@@ -1238,144 +1243,155 @@ xinit(int cols, int rows) | |
+ if (xsel.xtarget == None) | |
+ xsel.xtarget = XA_STRING; | |
+ } | |
+ | |
++void | |
++xresetfontsettings(ushort mode, Font **font, int *frcflags) | |
++{ | |
++ *font = &dc.font; | |
++ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
++ *font = &dc.ibfont; | |
++ *frcflags = FRC_ITALICBOLD; | |
++ } else if (mode & ATTR_ITALIC) { | |
++ *font = &dc.ifont; | |
++ *frcflags = FRC_ITALIC; | |
++ } else if (mode & ATTR_BOLD) { | |
++ *font = &dc.bfont; | |
++ *frcflags = FRC_BOLD; | |
++ } | |
++} | |
++ | |
+ int | |
+ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in… | |
+ { | |
+ float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp,… | |
+- ushort mode, prevmode = USHRT_MAX; | |
++ ushort mode = glyphs[0].mode & ~ATTR_WRAP; | |
+ Font *font = &dc.font; | |
+ int frcflags = FRC_NORMAL; | |
+- float runewidth = win.cw; | |
++ float runewidth = win.cw * ((glyphs[0].mode & ATTR_WIDE) ? 2.0f : 1.0… | |
+ Rune rune; | |
+ FT_UInt glyphidx; | |
+ FcResult fcres; | |
+ FcPattern *fcpattern, *fontpattern; | |
+ FcFontSet *fcsets[] = { NULL }; | |
+ FcCharSet *fccharset; | |
+- int i, f, numspecs = 0; | |
++ int f, code_idx, numspecs = 0; | |
++ float cluster_xp = xp, cluster_yp = yp; | |
++ HbTransformData shaped = { 0 }; | |
+ | |
+- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { | |
+- /* Fetch rune and mode for current glyph. */ | |
+- rune = glyphs[i].u; | |
+- mode = glyphs[i].mode; | |
++ /* Initial values. */ | |
++ xresetfontsettings(mode, &font, &frcflags); | |
+ | |
+- /* Skip dummy wide-character spacing. */ | |
+- if (mode == ATTR_WDUMMY) | |
++ /* Shape the segment. */ | |
++ hbtransform(&shaped, font->match, glyphs, 0, len); | |
++ xp = winx; yp = winy + font->ascent; | |
++ cluster_xp = xp; cluster_yp = yp; | |
++ | |
++ for (code_idx = 0; code_idx < shaped.count; code_idx++) { | |
++ int idx = shaped.glyphs[code_idx].cluster; | |
++ | |
++ if (glyphs[idx].mode & ATTR_WDUMMY) | |
+ continue; | |
+ | |
+- /* Determine font for glyph if different from previous glyph.… | |
+- if (prevmode != mode) { | |
+- prevmode = mode; | |
+- font = &dc.font; | |
+- frcflags = FRC_NORMAL; | |
+- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0… | |
+- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
+- font = &dc.ibfont; | |
+- frcflags = FRC_ITALICBOLD; | |
+- } else if (mode & ATTR_ITALIC) { | |
+- font = &dc.ifont; | |
+- frcflags = FRC_ITALIC; | |
+- } else if (mode & ATTR_BOLD) { | |
+- font = &dc.bfont; | |
+- frcflags = FRC_BOLD; | |
+- } | |
+- yp = winy + font->ascent; | |
++ /* Advance the drawing cursor if we've moved to a new cluster… | |
++ if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluste… | |
++ xp += runewidth; | |
++ cluster_xp = xp; | |
++ cluster_yp = yp; | |
+ } | |
+ | |
+- /* Lookup character index with default font. */ | |
+- glyphidx = XftCharIndex(xw.dpy, font->match, rune); | |
+- if (glyphidx) { | |
++ if (shaped.glyphs[code_idx].codepoint != 0) { | |
++ /* If symbol is found, put it into the specs. */ | |
+ specs[numspecs].font = font->match; | |
+- specs[numspecs].glyph = glyphidx; | |
+- specs[numspecs].x = (short)xp; | |
+- specs[numspecs].y = (short)yp; | |
+- xp += runewidth; | |
++ specs[numspecs].glyph = shaped.glyphs[code_idx].codep… | |
++ specs[numspecs].x = cluster_xp + (short)(shaped.posit… | |
++ specs[numspecs].y = cluster_yp - (short)(shaped.posit… | |
++ cluster_xp += shaped.positions[code_idx].x_advance / … | |
++ cluster_yp += shaped.positions[code_idx].y_advance / … | |
+ numspecs++; | |
+- continue; | |
+- } | |
+- | |
+- /* Fallback on font cache, search the font cache for match. */ | |
+- for (f = 0; f < frclen; f++) { | |
+- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); | |
+- /* Everything correct. */ | |
+- if (glyphidx && frc[f].flags == frcflags) | |
+- break; | |
+- /* We got a default font for a not found glyph. */ | |
+- if (!glyphidx && frc[f].flags == frcflags | |
+- && frc[f].unicodep == rune) { | |
+- break; | |
++ } else { | |
++ /* If it's not found, try to fetch it through the fon… | |
++ rune = glyphs[idx].u; | |
++ for (f = 0; f < frclen; f++) { | |
++ glyphidx = XftCharIndex(xw.dpy, frc[f].font, … | |
++ /* Everything correct. */ | |
++ if (glyphidx && frc[f].flags == frcflags) | |
++ break; | |
++ /* We got a default font for a not found glyp… | |
++ if (!glyphidx && frc[f].flags == frcflags | |
++ && frc[f].unicodep == rune) { | |
++ break; | |
++ } | |
+ } | |
+- } | |
+ | |
+- /* Nothing was found. Use fontconfig to find matching font. */ | |
+- if (f >= frclen) { | |
+- if (!font->set) | |
+- font->set = FcFontSort(0, font->pattern, | |
+- 1, 0, &fcres); | |
+- fcsets[0] = font->set; | |
+- | |
+- /* | |
+- * Nothing was found in the cache. Now use | |
+- * some dozen of Fontconfig calls to get the | |
+- * font for one single character. | |
+- * | |
+- * Xft and fontconfig are design failures. | |
+- */ | |
+- fcpattern = FcPatternDuplicate(font->pattern); | |
+- fccharset = FcCharSetCreate(); | |
+- | |
+- FcCharSetAddChar(fccharset, rune); | |
+- FcPatternAddCharSet(fcpattern, FC_CHARSET, | |
+- fccharset); | |
+- FcPatternAddBool(fcpattern, FC_SCALABLE, 1); | |
+- | |
+- FcConfigSubstitute(0, fcpattern, | |
+- FcMatchPattern); | |
+- FcDefaultSubstitute(fcpattern); | |
+- | |
+- fontpattern = FcFontSetMatch(0, fcsets, 1, | |
+- fcpattern, &fcres); | |
+- | |
+- /* Allocate memory for the new cache entry. */ | |
+- if (frclen >= frccap) { | |
+- frccap += 16; | |
+- frc = xrealloc(frc, frccap * sizeof(Fontcache… | |
++ /* Nothing was found. Use fontconfig to find matching… | |
++ if (f >= frclen) { | |
++ if (!font->set) | |
++ font->set = FcFontSort(0, font->patte… | |
++ 1, 0, &fcres); | |
++ fcsets[0] = font->set; | |
++ | |
++ /* | |
++ * Nothing was found in the cache. Now use | |
++ * some dozen of Fontconfig calls to get the | |
++ * font for one single character. | |
++ * | |
++ * Xft and fontconfig are design failures. | |
++ */ | |
++ fcpattern = FcPatternDuplicate(font->pattern); | |
++ fccharset = FcCharSetCreate(); | |
++ | |
++ FcCharSetAddChar(fccharset, rune); | |
++ FcPatternAddCharSet(fcpattern, FC_CHARSET, | |
++ fccharset); | |
++ FcPatternAddBool(fcpattern, FC_SCALABLE, 1); | |
++ | |
++ FcConfigSubstitute(0, fcpattern, | |
++ FcMatchPattern); | |
++ FcDefaultSubstitute(fcpattern); | |
++ | |
++ fontpattern = FcFontSetMatch(0, fcsets, 1, | |
++ fcpattern, &fcres); | |
++ | |
++ /* Allocate memory for the new cache entry. */ | |
++ if (frclen >= frccap) { | |
++ frccap += 16; | |
++ frc = xrealloc(frc, frccap * sizeof(F… | |
++ } | |
++ | |
++ frc[frclen].font = XftFontOpenPattern(xw.dpy, | |
++ fontpattern); | |
++ if (!frc[frclen].font) | |
++ die("XftFontOpenPattern failed seekin… | |
++ strerror(errno)); | |
++ frc[frclen].flags = frcflags; | |
++ frc[frclen].unicodep = rune; | |
++ | |
++ glyphidx = XftCharIndex(xw.dpy, frc[frclen].f… | |
++ | |
++ f = frclen; | |
++ frclen++; | |
++ | |
++ FcPatternDestroy(fcpattern); | |
++ FcCharSetDestroy(fccharset); | |
+ } | |
+ | |
+- frc[frclen].font = XftFontOpenPattern(xw.dpy, | |
+- fontpattern); | |
+- if (!frc[frclen].font) | |
+- die("XftFontOpenPattern failed seeking fallba… | |
+- strerror(errno)); | |
+- frc[frclen].flags = frcflags; | |
+- frc[frclen].unicodep = rune; | |
+- | |
+- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run… | |
+- | |
+- f = frclen; | |
+- frclen++; | |
+- | |
+- FcPatternDestroy(fcpattern); | |
+- FcCharSetDestroy(fccharset); | |
++ specs[numspecs].font = frc[f].font; | |
++ specs[numspecs].glyph = glyphidx; | |
++ specs[numspecs].x = (short)xp; | |
++ specs[numspecs].y = (short)yp; | |
++ numspecs++; | |
+ } | |
+- | |
+- specs[numspecs].font = frc[f].font; | |
+- specs[numspecs].glyph = glyphidx; | |
+- specs[numspecs].x = (short)xp; | |
+- specs[numspecs].y = (short)yp; | |
+- xp += runewidth; | |
+- numspecs++; | |
+ } | |
+ | |
++ /* Cleanup and get ready for next segment. */ | |
++ hbcleanup(&shaped); | |
+ return numspecs; | |
+ } | |
+ | |
+ void | |
+-xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
++xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
+ { | |
+- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); | |
+ int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, | |
+ width = charlen * win.cw; | |
+ Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; | |
+ XRenderColor colfg, colbg; | |
+@@ -1509,23 +1525,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Gly… | |
+ void | |
+ xdrawglyph(Glyph g, int x, int y) | |
+ { | |
+ int numspecs; | |
+- XftGlyphFontSpec spec; | |
++ XftGlyphFontSpec *specs = xw.specbuf; | |
+ | |
+- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); | |
+- xdrawglyphfontspecs(&spec, g, numspecs, x, y); | |
++ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y); | |
++ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? … | |
+ } | |
+ | |
+ void | |
+-xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) | |
++xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int… | |
+ { | |
+ Color drawcol; | |
+ | |
+ /* remove the old cursor */ | |
+ if (selected(ox, oy)) | |
+ og.mode ^= ATTR_REVERSE; | |
+- xdrawglyph(og, ox, oy); | |
++ | |
++ /* Redraw the line where cursor was previously. | |
++ * It will restore the ligatures broken by the cursor. */ | |
++ xdrawline(line, 0, oy, len); | |
+ | |
+ if (IS_SET(MODE_HIDE)) | |
+ return; | |
+ | |
+@@ -1657,30 +1676,30 @@ xdrawline(Line line, int x1, int y1, int x2) | |
+ int i, x, ox, numspecs; | |
+ Glyph base, new; | |
+ XftGlyphFontSpec *specs = xw.specbuf; | |
+ | |
+- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); | |
+ i = ox = 0; | |
+- for (x = x1; x < x2 && i < numspecs; x++) { | |
++ for (x = x1; x < x2; x++) { | |
+ new = line[x]; | |
+ if (new.mode == ATTR_WDUMMY) | |
+ continue; | |
+ if (selected(x, y1)) | |
+ new.mode ^= ATTR_REVERSE; | |
+- if (i > 0 && ATTRCMP(base, new)) { | |
+- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
+- specs += i; | |
+- numspecs -= i; | |
++ if ((i > 0) && ATTRCMP(base, new)) { | |
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - … | |
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x … | |
+ i = 0; | |
+ } | |
+ if (i == 0) { | |
+ ox = x; | |
+ base = new; | |
+ } | |
+ i++; | |
+ } | |
+- if (i > 0) | |
+- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
++ if (i > 0) { | |
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,… | |
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox); | |
++ } | |
+ } | |
+ | |
+ void | |
+ xfinishdraw(void) | |
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-scrollback-ri… | |
@@ -1,633 +0,0 @@ | |
-diff --git a/Makefile b/Makefile | |
-index 470ac86..38240da 100644 | |
---- a/Makefile | |
-+++ b/Makefile | |
-@@ -4,7 +4,7 @@ | |
- | |
- include config.mk | |
- | |
--SRC = st.c x.c | |
-+SRC = st.c x.c hb.c | |
- OBJ = $(SRC:.c=.o) | |
- | |
- all: st | |
-@@ -22,7 +22,8 @@ config.h: | |
- $(CC) $(STCFLAGS) -c $< | |
- | |
- st.o: config.h st.h win.h | |
--x.o: arg.h config.h st.h win.h | |
-+x.o: arg.h config.h st.h win.h hb.h | |
-+hb.o: st.h | |
- | |
- $(OBJ): config.h config.mk | |
- | |
-diff --git a/config.mk b/config.mk | |
-index 1e306f8..3e13e53 100644 | |
---- a/config.mk | |
-+++ b/config.mk | |
-@@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config | |
- # includes and libs | |
- INCS = -I$(X11INC) \ | |
- `$(PKG_CONFIG) --cflags fontconfig` \ | |
-- `$(PKG_CONFIG) --cflags freetype2` | |
-+ `$(PKG_CONFIG) --cflags freetype2` \ | |
-+ `$(PKG_CONFIG) --cflags harfbuzz` | |
- LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ | |
- `$(PKG_CONFIG) --libs fontconfig` \ | |
-- `$(PKG_CONFIG) --libs freetype2` | |
-+ `$(PKG_CONFIG) --libs freetype2` \ | |
-+ `$(PKG_CONFIG) --libs harfbuzz` | |
- | |
- # flags | |
- STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 | |
-diff --git a/hb.c b/hb.c | |
-new file mode 100644 | |
-index 0000000..99412c8 | |
---- /dev/null | |
-+++ b/hb.c | |
-@@ -0,0 +1,125 @@ | |
-+#include <stdlib.h> | |
-+#include <stdio.h> | |
-+#include <math.h> | |
-+#include <X11/Xft/Xft.h> | |
-+#include <X11/cursorfont.h> | |
-+#include <hb.h> | |
-+#include <hb-ft.h> | |
-+ | |
-+#include "st.h" | |
-+#include "hb.h" | |
-+ | |
-+#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start… | |
-+#define BUFFER_STEP 256 | |
-+ | |
-+hb_font_t *hbfindfont(XftFont *match); | |
-+ | |
-+typedef struct { | |
-+ XftFont *match; | |
-+ hb_font_t *font; | |
-+} HbFontMatch; | |
-+ | |
-+typedef struct { | |
-+ size_t capacity; | |
-+ HbFontMatch *fonts; | |
-+} HbFontCache; | |
-+ | |
-+static HbFontCache hbfontcache = { 0, NULL }; | |
-+ | |
-+typedef struct { | |
-+ size_t capacity; | |
-+ Rune *runes; | |
-+} RuneBuffer; | |
-+ | |
-+static RuneBuffer hbrunebuffer = { 0, NULL }; | |
-+ | |
-+/* | |
-+ * Poplulate the array with a list of font features, wrapped in FEATURE macro, | |
-+ * e. g. | |
-+ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') | |
-+ */ | |
-+hb_feature_t features[] = { }; | |
-+ | |
-+void | |
-+hbunloadfonts() | |
-+{ | |
-+ for (int i = 0; i < hbfontcache.capacity; i++) { | |
-+ hb_font_destroy(hbfontcache.fonts[i].font); | |
-+ XftUnlockFace(hbfontcache.fonts[i].match); | |
-+ } | |
-+ | |
-+ if (hbfontcache.fonts != NULL) { | |
-+ free(hbfontcache.fonts); | |
-+ hbfontcache.fonts = NULL; | |
-+ } | |
-+ hbfontcache.capacity = 0; | |
-+} | |
-+ | |
-+hb_font_t * | |
-+hbfindfont(XftFont *match) | |
-+{ | |
-+ for (int i = 0; i < hbfontcache.capacity; i++) { | |
-+ if (hbfontcache.fonts[i].match == match) | |
-+ return hbfontcache.fonts[i].font; | |
-+ } | |
-+ | |
-+ /* Font not found in cache, caching it now. */ | |
-+ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * … | |
-+ FT_Face face = XftLockFace(match); | |
-+ hb_font_t *font = hb_ft_font_create(face, NULL); | |
-+ if (font == NULL) | |
-+ die("Failed to load Harfbuzz font."); | |
-+ | |
-+ hbfontcache.fonts[hbfontcache.capacity].match = match; | |
-+ hbfontcache.fonts[hbfontcache.capacity].font = font; | |
-+ hbfontcache.capacity += 1; | |
-+ | |
-+ return font; | |
-+} | |
-+ | |
-+void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, … | |
-+ ushort mode = USHRT_MAX; | |
-+ unsigned int glyph_count; | |
-+ int rune_idx, glyph_idx, end = start + length; | |
-+ | |
-+ hb_font_t *font = hbfindfont(xfont); | |
-+ if (font == NULL) | |
-+ return; | |
-+ | |
-+ hb_buffer_t *buffer = hb_buffer_create(); | |
-+ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); | |
-+ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_… | |
-+ | |
-+ /* Resize the buffer if required length is larger. */ | |
-+ if (hbrunebuffer.capacity < length) { | |
-+ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S… | |
-+ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer… | |
-+ } | |
-+ | |
-+ /* Fill buffer with codepoints. */ | |
-+ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r… | |
-+ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u; | |
-+ mode = glyphs[glyph_idx].mode; | |
-+ if (mode & ATTR_WDUMMY) | |
-+ hbrunebuffer.runes[rune_idx] = 0x0020; | |
-+ } | |
-+ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt… | |
-+ | |
-+ /* Shape the segment. */ | |
-+ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t… | |
-+ | |
-+ /* Get new glyph info. */ | |
-+ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun… | |
-+ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly… | |
-+ | |
-+ /* Fill the output. */ | |
-+ data->buffer = buffer; | |
-+ data->glyphs = info; | |
-+ data->positions = pos; | |
-+ data->count = glyph_count; | |
-+} | |
-+ | |
-+void hbcleanup(HbTransformData *data) { | |
-+ hb_buffer_destroy(data->buffer); | |
-+ memset(data, 0, sizeof(HbTransformData)); | |
-+} | |
-diff --git a/hb.h b/hb.h | |
-new file mode 100644 | |
-index 0000000..3b0ef44 | |
---- /dev/null | |
-+++ b/hb.h | |
-@@ -0,0 +1,14 @@ | |
-+#include <X11/Xft/Xft.h> | |
-+#include <hb.h> | |
-+#include <hb-ft.h> | |
-+ | |
-+typedef struct { | |
-+ hb_buffer_t *buffer; | |
-+ hb_glyph_info_t *glyphs; | |
-+ hb_glyph_position_t *positions; | |
-+ unsigned int count; | |
-+} HbTransformData; | |
-+ | |
-+void hbunloadfonts(); | |
-+void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int); | |
-+void hbcleanup(HbTransformData *); | |
-diff --git a/st.c b/st.c | |
-index c44797b..91f54dc 100644 | |
---- a/st.c | |
-+++ b/st.c | |
-@@ -2759,7 +2759,8 @@ draw(void) | |
- drawregion(0, 0, term.col, term.row); | |
- if (TSCREEN.off == 0) | |
- xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx], | |
-- term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]… | |
-+ term.ocx, term.ocy, TLINE(term.ocy)[term.ocx], | |
-+ TLINE(term.ocy), term.col); | |
- term.ocx = cx; | |
- term.ocy = term.c.y; | |
- xfinishdraw(); | |
-diff --git a/st.h b/st.h | |
-index 3cea73b..709a369 100644 | |
---- a/st.h | |
-+++ b/st.h | |
-@@ -11,7 +11,8 @@ | |
- #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) | |
- #define DEFAULT(a, b) (a) = (a) ? (a) : (b) | |
- #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)… | |
--#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f… | |
-+#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode … | |
-+ (a).fg != (b).fg || \ | |
- (a).bg != (b).bg) | |
- #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ | |
- (t1.tv_nsec-t2.tv_nsec)/1E6) | |
-diff --git a/win.h b/win.h | |
-index 6de960d..94679e4 100644 | |
---- a/win.h | |
-+++ b/win.h | |
-@@ -25,7 +25,7 @@ enum win_mode { | |
- | |
- void xbell(void); | |
- void xclipcopy(void); | |
--void xdrawcursor(int, int, Glyph, int, int, Glyph); | |
-+void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int); | |
- void xdrawline(Line, int, int, int); | |
- void xfinishdraw(void); | |
- void xloadcols(void); | |
-diff --git a/x.c b/x.c | |
-index 9891e91..7d42790 100644 | |
---- a/x.c | |
-+++ b/x.c | |
-@@ -19,6 +19,7 @@ char *argv0; | |
- #include "arg.h" | |
- #include "st.h" | |
- #include "win.h" | |
-+#include "hb.h" | |
- | |
- /* types used in config.h */ | |
- typedef struct { | |
-@@ -143,8 +144,9 @@ typedef struct { | |
- } DC; | |
- | |
- static inline ushort sixd_to_16bit(int); | |
-+static void xresetfontsettings(ushort mode, Font **font, int *frcflags); | |
- static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i… | |
--static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
-+static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
- static void xdrawglyph(Glyph, int, int); | |
- static void xclear(int, int, int, int); | |
- static int xgeommasktogravity(int); | |
-@@ -759,7 +761,7 @@ xresize(int col, int row) | |
- xclear(0, 0, win.w, win.h); | |
- | |
- /* resize to new width */ | |
-- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); | |
-+ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4); | |
- } | |
- | |
- ushort | |
-@@ -1064,6 +1066,9 @@ xunloadfont(Font *f) | |
- void | |
- xunloadfonts(void) | |
- { | |
-+ /* Clear Harfbuzz font cache. */ | |
-+ hbunloadfonts(); | |
-+ | |
- /* Free the loaded fonts in the font cache. */ | |
- while (frclen > 0) | |
- XftFontClose(xw.dpy, frc[--frclen].font); | |
-@@ -1187,7 +1192,7 @@ xinit(int cols, int rows) | |
- XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); | |
- | |
- /* font spec buffer */ | |
-- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); | |
-+ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4); | |
- | |
- /* Xft rendering context */ | |
- xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); | |
-@@ -1241,6 +1246,22 @@ xinit(int cols, int rows) | |
- xsel.xtarget = XA_STRING; | |
- } | |
- | |
-+void | |
-+xresetfontsettings(ushort mode, Font **font, int *frcflags) | |
-+{ | |
-+ *font = &dc.font; | |
-+ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
-+ *font = &dc.ibfont; | |
-+ *frcflags = FRC_ITALICBOLD; | |
-+ } else if (mode & ATTR_ITALIC) { | |
-+ *font = &dc.ifont; | |
-+ *frcflags = FRC_ITALIC; | |
-+ } else if (mode & ATTR_BOLD) { | |
-+ *font = &dc.bfont; | |
-+ *frcflags = FRC_BOLD; | |
-+ } | |
-+} | |
-+ | |
- int | |
- xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in… | |
- { | |
-@@ -1255,128 +1276,156 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const G… | |
- FcPattern *fcpattern, *fontpattern; | |
- FcFontSet *fcsets[] = { NULL }; | |
- FcCharSet *fccharset; | |
-- int i, f, numspecs = 0; | |
-+ int i, f, length = 0, start = 0, numspecs = 0; | |
-+ float cluster_xp = xp, cluster_yp = yp; | |
-+ HbTransformData shaped = { 0 }; | |
-+ | |
-+ /* Initial values. */ | |
-+ mode = prevmode = glyphs[0].mode & ~ATTR_WRAP; | |
-+ xresetfontsettings(mode, &font, &frcflags); | |
- | |
- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { | |
-- /* Fetch rune and mode for current glyph. */ | |
-- rune = glyphs[i].u; | |
-- mode = glyphs[i].mode; | |
-+ mode = glyphs[i].mode & ~ATTR_WRAP; | |
- | |
- /* Skip dummy wide-character spacing. */ | |
-- if (mode == ATTR_WDUMMY) | |
-+ if (mode & ATTR_WDUMMY && i < (len - 1)) | |
- continue; | |
- | |
-- /* Determine font for glyph if different from previous glyph.… | |
-- if (prevmode != mode) { | |
-- prevmode = mode; | |
-- font = &dc.font; | |
-- frcflags = FRC_NORMAL; | |
-- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0… | |
-- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
-- font = &dc.ibfont; | |
-- frcflags = FRC_ITALICBOLD; | |
-- } else if (mode & ATTR_ITALIC) { | |
-- font = &dc.ifont; | |
-- frcflags = FRC_ITALIC; | |
-- } else if (mode & ATTR_BOLD) { | |
-- font = &dc.bfont; | |
-- frcflags = FRC_BOLD; | |
-+ if ( | |
-+ prevmode != mode | |
-+ || ATTRCMP(glyphs[start], glyphs[i]) | |
-+ || selected(x + i, y) != selected(x + start, y) | |
-+ || i == (len - 1) | |
-+ ) { | |
-+ /* Handle 1-character wide segments and end of line */ | |
-+ length = i - start; | |
-+ if (i == start) { | |
-+ length = 1; | |
-+ } else if (i == (len - 1)) { | |
-+ length = (i - start + 1); | |
- } | |
-- yp = winy + font->ascent; | |
-- } | |
- | |
-- /* Lookup character index with default font. */ | |
-- glyphidx = XftCharIndex(xw.dpy, font->match, rune); | |
-- if (glyphidx) { | |
-- specs[numspecs].font = font->match; | |
-- specs[numspecs].glyph = glyphidx; | |
-- specs[numspecs].x = (short)xp; | |
-- specs[numspecs].y = (short)yp; | |
-- xp += runewidth; | |
-- numspecs++; | |
-- continue; | |
-- } | |
-- | |
-- /* Fallback on font cache, search the font cache for match. */ | |
-- for (f = 0; f < frclen; f++) { | |
-- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); | |
-- /* Everything correct. */ | |
-- if (glyphidx && frc[f].flags == frcflags) | |
-- break; | |
-- /* We got a default font for a not found glyph. */ | |
-- if (!glyphidx && frc[f].flags == frcflags | |
-- && frc[f].unicodep == rune) { | |
-- break; | |
-+ /* Shape the segment. */ | |
-+ hbtransform(&shaped, font->match, glyphs, start, leng… | |
-+ runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE… | |
-+ cluster_xp = xp; cluster_yp = yp; | |
-+ for (int code_idx = 0; code_idx < shaped.count; code_… | |
-+ int idx = shaped.glyphs[code_idx].cluster; | |
-+ | |
-+ if (glyphs[start + idx].mode & ATTR_WDUMMY) | |
-+ continue; | |
-+ | |
-+ /* Advance the drawing cursor if we've moved … | |
-+ if (code_idx > 0 && idx != shaped.glyphs[code… | |
-+ xp += runewidth; | |
-+ cluster_xp = xp; | |
-+ cluster_yp = yp; | |
-+ runewidth = win.cw * ((glyphs[start +… | |
-+ } | |
-+ | |
-+ if (shaped.glyphs[code_idx].codepoint != 0) { | |
-+ /* If symbol is found, put it into th… | |
-+ specs[numspecs].font = font->match; | |
-+ specs[numspecs].glyph = shaped.glyphs… | |
-+ specs[numspecs].x = cluster_xp + (sho… | |
-+ specs[numspecs].y = cluster_yp - (sho… | |
-+ cluster_xp += shaped.positions[code_i… | |
-+ cluster_yp += shaped.positions[code_i… | |
-+ numspecs++; | |
-+ } else { | |
-+ /* If it's not found, try to fetch it… | |
-+ rune = glyphs[start + idx].u; | |
-+ for (f = 0; f < frclen; f++) { | |
-+ glyphidx = XftCharIndex(xw.dp… | |
-+ /* Everything correct. */ | |
-+ if (glyphidx && frc[f].flags … | |
-+ break; | |
-+ /* We got a default font for … | |
-+ if (!glyphidx && frc[f].flags… | |
-+ && frc[f].uni… | |
-+ break; | |
-+ } | |
-+ } | |
-+ | |
-+ /* Nothing was found. Use fontconfig … | |
-+ if (f >= frclen) { | |
-+ if (!font->set) | |
-+ font->set = FcFontSor… | |
-+ … | |
-+ fcsets[0] = font->set; | |
-+ | |
-+ /* | |
-+ * Nothing was found in the c… | |
-+ * some dozen of Fontconfig c… | |
-+ * font for one single charac… | |
-+ * | |
-+ * Xft and fontconfig are des… | |
-+ */ | |
-+ fcpattern = FcPatternDuplicat… | |
-+ fccharset = FcCharSetCreate(); | |
-+ | |
-+ FcCharSetAddChar(fccharset, r… | |
-+ FcPatternAddCharSet(fcpattern… | |
-+ fccharset); | |
-+ FcPatternAddBool(fcpattern, F… | |
-+ | |
-+ FcConfigSubstitute(0, fcpatte… | |
-+ FcMatchPatter… | |
-+ FcDefaultSubstitute(fcpattern… | |
-+ | |
-+ fontpattern = FcFontSetMatch(… | |
-+ fcpattern, &f… | |
-+ | |
-+ /* Allocate memory for the ne… | |
-+ if (frclen >= frccap) { | |
-+ frccap += 16; | |
-+ frc = xrealloc(frc, f… | |
-+ } | |
-+ | |
-+ frc[frclen].font = XftFontOpe… | |
-+ fontpattern); | |
-+ if (!frc[frclen].font) | |
-+ die("XftFontOpenPatte… | |
-+ strerror(errn… | |
-+ frc[frclen].flags = frcflags; | |
-+ frc[frclen].unicodep = rune; | |
-+ | |
-+ glyphidx = XftCharIndex(xw.dp… | |
-+ | |
-+ f = frclen; | |
-+ frclen++; | |
-+ | |
-+ FcPatternDestroy(fcpattern); | |
-+ FcCharSetDestroy(fccharset); | |
-+ } | |
-+ | |
-+ specs[numspecs].font = frc[f].font; | |
-+ specs[numspecs].glyph = glyphidx; | |
-+ specs[numspecs].x = (short)xp; | |
-+ specs[numspecs].y = (short)yp; | |
-+ numspecs++; | |
-+ } | |
- } | |
-- } | |
- | |
-- /* Nothing was found. Use fontconfig to find matching font. */ | |
-- if (f >= frclen) { | |
-- if (!font->set) | |
-- font->set = FcFontSort(0, font->pattern, | |
-- 1, 0, &fcres); | |
-- fcsets[0] = font->set; | |
-+ /* Cleanup and get ready for next segment. */ | |
-+ hbcleanup(&shaped); | |
-+ start = i; | |
- | |
-- /* | |
-- * Nothing was found in the cache. Now use | |
-- * some dozen of Fontconfig calls to get the | |
-- * font for one single character. | |
-- * | |
-- * Xft and fontconfig are design failures. | |
-- */ | |
-- fcpattern = FcPatternDuplicate(font->pattern); | |
-- fccharset = FcCharSetCreate(); | |
-- | |
-- FcCharSetAddChar(fccharset, rune); | |
-- FcPatternAddCharSet(fcpattern, FC_CHARSET, | |
-- fccharset); | |
-- FcPatternAddBool(fcpattern, FC_SCALABLE, 1); | |
-- | |
-- FcConfigSubstitute(0, fcpattern, | |
-- FcMatchPattern); | |
-- FcDefaultSubstitute(fcpattern); | |
-- | |
-- fontpattern = FcFontSetMatch(0, fcsets, 1, | |
-- fcpattern, &fcres); | |
-- | |
-- /* Allocate memory for the new cache entry. */ | |
-- if (frclen >= frccap) { | |
-- frccap += 16; | |
-- frc = xrealloc(frc, frccap * sizeof(Fontcache… | |
-+ /* Determine font for glyph if different from previou… | |
-+ if (prevmode != mode) { | |
-+ prevmode = mode; | |
-+ xresetfontsettings(mode, &font, &frcflags); | |
-+ yp = winy + font->ascent; | |
- } | |
-- | |
-- frc[frclen].font = XftFontOpenPattern(xw.dpy, | |
-- fontpattern); | |
-- if (!frc[frclen].font) | |
-- die("XftFontOpenPattern failed seeking fallba… | |
-- strerror(errno)); | |
-- frc[frclen].flags = frcflags; | |
-- frc[frclen].unicodep = rune; | |
-- | |
-- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run… | |
-- | |
-- f = frclen; | |
-- frclen++; | |
-- | |
-- FcPatternDestroy(fcpattern); | |
-- FcCharSetDestroy(fccharset); | |
- } | |
-- | |
-- specs[numspecs].font = frc[f].font; | |
-- specs[numspecs].glyph = glyphidx; | |
-- specs[numspecs].x = (short)xp; | |
-- specs[numspecs].y = (short)yp; | |
-- xp += runewidth; | |
-- numspecs++; | |
- } | |
- | |
- return numspecs; | |
- } | |
- | |
- void | |
--xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
-+xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
- { | |
-- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); | |
- int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, | |
- width = charlen * win.cw; | |
- Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; | |
-@@ -1512,21 +1561,24 @@ void | |
- xdrawglyph(Glyph g, int x, int y) | |
- { | |
- int numspecs; | |
-- XftGlyphFontSpec spec; | |
-+ XftGlyphFontSpec *specs = xw.specbuf; | |
- | |
-- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); | |
-- xdrawglyphfontspecs(&spec, g, numspecs, x, y); | |
-+ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y); | |
-+ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? … | |
- } | |
- | |
- void | |
--xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) | |
-+xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int… | |
- { | |
- Color drawcol; | |
- | |
- /* remove the old cursor */ | |
- if (selected(ox, oy)) | |
- og.mode ^= ATTR_REVERSE; | |
-- xdrawglyph(og, ox, oy); | |
-+ | |
-+ /* Redraw the line where cursor was previously. | |
-+ * It will restore the ligatures broken by the cursor. */ | |
-+ xdrawline(line, 0, oy, len); | |
- | |
- if (IS_SET(MODE_HIDE)) | |
- return; | |
-@@ -1654,18 +1706,16 @@ xdrawline(Line line, int x1, int y1, int x2) | |
- Glyph base, new; | |
- XftGlyphFontSpec *specs = xw.specbuf; | |
- | |
-- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); | |
- i = ox = 0; | |
-- for (x = x1; x < x2 && i < numspecs; x++) { | |
-+ for (x = x1; x < x2; x++) { | |
- new = line[x]; | |
- if (new.mode == ATTR_WDUMMY) | |
- continue; | |
- if (selected(x, y1)) | |
- new.mode ^= ATTR_REVERSE; | |
-- if (i > 0 && ATTRCMP(base, new)) { | |
-- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
-- specs += i; | |
-- numspecs -= i; | |
-+ if ((i > 0) && ATTRCMP(base, new)) { | |
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - … | |
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x … | |
- i = 0; | |
- } | |
- if (i == 0) { | |
-@@ -1674,8 +1724,10 @@ xdrawline(Line line, int x1, int y1, int x2) | |
- } | |
- i++; | |
- } | |
-- if (i > 0) | |
-- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
-+ if (i > 0) { | |
-+ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,… | |
-+ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox); | |
-+ } | |
- } | |
- | |
- void | |
diff --git a/st.suckless.org/patches/ligatures/0.9.2/st-ligatures-scrollback-ri… | |
@@ -0,0 +1,650 @@ | |
+diff --git a/Makefile b/Makefile | |
+index 15db421..dfcea0f 100644 | |
+--- a/Makefile | |
++++ b/Makefile | |
+@@ -3,9 +3,9 @@ | |
+ .POSIX: | |
+ | |
+ include config.mk | |
+ | |
+-SRC = st.c x.c | |
++SRC = st.c x.c hb.c | |
+ OBJ = $(SRC:.c=.o) | |
+ | |
+ all: st | |
+ | |
+@@ -15,9 +15,10 @@ config.h: | |
+ .c.o: | |
+ $(CC) $(STCFLAGS) -c $< | |
+ | |
+ st.o: config.h st.h win.h | |
+-x.o: arg.h config.h st.h win.h | |
++x.o: arg.h config.h st.h win.h hb.h | |
++hb.o: st.h | |
+ | |
+ $(OBJ): config.h config.mk | |
+ | |
+ st: $(OBJ) | |
+diff --git a/config.mk b/config.mk | |
+index fdc29a7..6833b3b 100644 | |
+--- a/config.mk | |
++++ b/config.mk | |
+@@ -14,12 +14,14 @@ PKG_CONFIG = pkg-config | |
+ | |
+ # includes and libs | |
+ INCS = -I$(X11INC) \ | |
+ `$(PKG_CONFIG) --cflags fontconfig` \ | |
+- `$(PKG_CONFIG) --cflags freetype2` | |
++ `$(PKG_CONFIG) --cflags freetype2` \ | |
++ `$(PKG_CONFIG) --cflags harfbuzz` | |
+ LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ | |
+ `$(PKG_CONFIG) --libs fontconfig` \ | |
+- `$(PKG_CONFIG) --libs freetype2` | |
++ `$(PKG_CONFIG) --libs freetype2` \ | |
++ `$(PKG_CONFIG) --libs harfbuzz` | |
+ | |
+ # flags | |
+ STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 | |
+ STCFLAGS = $(INCS) $(STCPPFLAGS) $(CPPFLAGS) $(CFLAGS) | |
+@@ -28,9 +30,10 @@ STLDFLAGS = $(LIBS) $(LDFLAGS) | |
+ # OpenBSD: | |
+ #CPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 -D_BSD_SOURCE | |
+ #LIBS = -L$(X11LIB) -lm -lX11 -lutil -lXft \ | |
+ # `$(PKG_CONFIG) --libs fontconfig` \ | |
+-# `$(PKG_CONFIG) --libs freetype2` | |
++# `$(PKG_CONFIG) --libs freetype2` \ | |
++# `$(PKG_CONFIG) --libs harfbuzz` | |
+ #MANPREFIX = ${PREFIX}/man | |
+ | |
+ # compiler and linker | |
+ # CC = c99 | |
+diff --git a/hb.c b/hb.c | |
+new file mode 100644 | |
+index 0000000..99412c8 | |
+--- /dev/null | |
++++ b/hb.c | |
+@@ -0,0 +1,125 @@ | |
++#include <stdlib.h> | |
++#include <stdio.h> | |
++#include <math.h> | |
++#include <X11/Xft/Xft.h> | |
++#include <X11/cursorfont.h> | |
++#include <hb.h> | |
++#include <hb-ft.h> | |
++ | |
++#include "st.h" | |
++#include "hb.h" | |
++ | |
++#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start… | |
++#define BUFFER_STEP 256 | |
++ | |
++hb_font_t *hbfindfont(XftFont *match); | |
++ | |
++typedef struct { | |
++ XftFont *match; | |
++ hb_font_t *font; | |
++} HbFontMatch; | |
++ | |
++typedef struct { | |
++ size_t capacity; | |
++ HbFontMatch *fonts; | |
++} HbFontCache; | |
++ | |
++static HbFontCache hbfontcache = { 0, NULL }; | |
++ | |
++typedef struct { | |
++ size_t capacity; | |
++ Rune *runes; | |
++} RuneBuffer; | |
++ | |
++static RuneBuffer hbrunebuffer = { 0, NULL }; | |
++ | |
++/* | |
++ * Poplulate the array with a list of font features, wrapped in FEATURE macro, | |
++ * e. g. | |
++ * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') | |
++ */ | |
++hb_feature_t features[] = { }; | |
++ | |
++void | |
++hbunloadfonts() | |
++{ | |
++ for (int i = 0; i < hbfontcache.capacity; i++) { | |
++ hb_font_destroy(hbfontcache.fonts[i].font); | |
++ XftUnlockFace(hbfontcache.fonts[i].match); | |
++ } | |
++ | |
++ if (hbfontcache.fonts != NULL) { | |
++ free(hbfontcache.fonts); | |
++ hbfontcache.fonts = NULL; | |
++ } | |
++ hbfontcache.capacity = 0; | |
++} | |
++ | |
++hb_font_t * | |
++hbfindfont(XftFont *match) | |
++{ | |
++ for (int i = 0; i < hbfontcache.capacity; i++) { | |
++ if (hbfontcache.fonts[i].match == match) | |
++ return hbfontcache.fonts[i].font; | |
++ } | |
++ | |
++ /* Font not found in cache, caching it now. */ | |
++ hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * … | |
++ FT_Face face = XftLockFace(match); | |
++ hb_font_t *font = hb_ft_font_create(face, NULL); | |
++ if (font == NULL) | |
++ die("Failed to load Harfbuzz font."); | |
++ | |
++ hbfontcache.fonts[hbfontcache.capacity].match = match; | |
++ hbfontcache.fonts[hbfontcache.capacity].font = font; | |
++ hbfontcache.capacity += 1; | |
++ | |
++ return font; | |
++} | |
++ | |
++void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, … | |
++ ushort mode = USHRT_MAX; | |
++ unsigned int glyph_count; | |
++ int rune_idx, glyph_idx, end = start + length; | |
++ | |
++ hb_font_t *font = hbfindfont(xfont); | |
++ if (font == NULL) | |
++ return; | |
++ | |
++ hb_buffer_t *buffer = hb_buffer_create(); | |
++ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); | |
++ hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_… | |
++ | |
++ /* Resize the buffer if required length is larger. */ | |
++ if (hbrunebuffer.capacity < length) { | |
++ hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_S… | |
++ hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer… | |
++ } | |
++ | |
++ /* Fill buffer with codepoints. */ | |
++ for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, r… | |
++ hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u; | |
++ mode = glyphs[glyph_idx].mode; | |
++ if (mode & ATTR_WDUMMY) | |
++ hbrunebuffer.runes[rune_idx] = 0x0020; | |
++ } | |
++ hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, lengt… | |
++ | |
++ /* Shape the segment. */ | |
++ hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t… | |
++ | |
++ /* Get new glyph info. */ | |
++ hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_coun… | |
++ hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &gly… | |
++ | |
++ /* Fill the output. */ | |
++ data->buffer = buffer; | |
++ data->glyphs = info; | |
++ data->positions = pos; | |
++ data->count = glyph_count; | |
++} | |
++ | |
++void hbcleanup(HbTransformData *data) { | |
++ hb_buffer_destroy(data->buffer); | |
++ memset(data, 0, sizeof(HbTransformData)); | |
++} | |
+diff --git a/hb.h b/hb.h | |
+new file mode 100644 | |
+index 0000000..3b0ef44 | |
+--- /dev/null | |
++++ b/hb.h | |
+@@ -0,0 +1,14 @@ | |
++#include <X11/Xft/Xft.h> | |
++#include <hb.h> | |
++#include <hb-ft.h> | |
++ | |
++typedef struct { | |
++ hb_buffer_t *buffer; | |
++ hb_glyph_info_t *glyphs; | |
++ hb_glyph_position_t *positions; | |
++ unsigned int count; | |
++} HbTransformData; | |
++ | |
++void hbunloadfonts(); | |
++void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int); | |
++void hbcleanup(HbTransformData *); | |
+diff --git a/st.c b/st.c | |
+index d9b163e..fbca4ba 100644 | |
+--- a/st.c | |
++++ b/st.c | |
+@@ -2777,9 +2777,10 @@ draw(void) | |
+ | |
+ drawregion(0, 0, term.col, term.row); | |
+ if (TSCREEN.off == 0) | |
+ xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx], | |
+- term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]… | |
++ term.ocx, term.ocy, TLINE(term.ocy)[term.ocx], | |
++ TLINE(term.ocy), term.col); | |
+ term.ocx = cx; | |
+ term.ocy = term.c.y; | |
+ xfinishdraw(); | |
+ if (ocx != term.ocx || ocy != term.ocy) | |
+diff --git a/st.h b/st.h | |
+index 3cea73b..709a369 100644 | |
+--- a/st.h | |
++++ b/st.h | |
+@@ -10,9 +10,10 @@ | |
+ #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) | |
+ #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) | |
+ #define DEFAULT(a, b) (a) = (a) ? (a) : (b) | |
+ #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b)… | |
+-#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).f… | |
++#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode … | |
++ (a).fg != (b).fg || \ | |
+ (a).bg != (b).bg) | |
+ #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ | |
+ (t1.tv_nsec-t2.tv_nsec)/1E6) | |
+ #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) | |
+diff --git a/win.h b/win.h | |
+index 6de960d..94679e4 100644 | |
+--- a/win.h | |
++++ b/win.h | |
+@@ -24,9 +24,9 @@ enum win_mode { | |
+ }; | |
+ | |
+ void xbell(void); | |
+ void xclipcopy(void); | |
+-void xdrawcursor(int, int, Glyph, int, int, Glyph); | |
++void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int); | |
+ void xdrawline(Line, int, int, int); | |
+ void xfinishdraw(void); | |
+ void xloadcols(void); | |
+ int xsetcolorname(int, const char *); | |
+diff --git a/x.c b/x.c | |
+index 25785a6..16d7a3a 100644 | |
+--- a/x.c | |
++++ b/x.c | |
+@@ -18,8 +18,9 @@ | |
+ char *argv0; | |
+ #include "arg.h" | |
+ #include "st.h" | |
+ #include "win.h" | |
++#include "hb.h" | |
+ | |
+ /* types used in config.h */ | |
+ typedef struct { | |
+ uint mod; | |
+@@ -142,10 +143,11 @@ typedef struct { | |
+ GC gc; | |
+ } DC; | |
+ | |
+ static inline ushort sixd_to_16bit(int); | |
++static void xresetfontsettings(ushort mode, Font **font, int *frcflags); | |
+ static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, i… | |
+-static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
++static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, in… | |
+ static void xdrawglyph(Glyph, int, int); | |
+ static void xclear(int, int, int, int); | |
+ static int xgeommasktogravity(int); | |
+ static int ximopen(Display *); | |
+@@ -758,9 +760,9 @@ xresize(int col, int row) | |
+ XftDrawChange(xw.draw, xw.buf); | |
+ xclear(0, 0, win.w, win.h); | |
+ | |
+ /* resize to new width */ | |
+- xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); | |
++ xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4); | |
+ } | |
+ | |
+ ushort | |
+ sixd_to_16bit(int x) | |
+@@ -1063,8 +1065,11 @@ xunloadfont(Font *f) | |
+ | |
+ void | |
+ xunloadfonts(void) | |
+ { | |
++ /* Clear Harfbuzz font cache. */ | |
++ hbunloadfonts(); | |
++ | |
+ /* Free the loaded fonts in the font cache. */ | |
+ while (frclen > 0) | |
+ XftFontClose(xw.dpy, frc[--frclen].font); | |
+ | |
+@@ -1186,9 +1191,9 @@ xinit(int cols, int rows) | |
+ XSetForeground(xw.dpy, dc.gc, dc.col[defaultbg].pixel); | |
+ XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); | |
+ | |
+ /* font spec buffer */ | |
+- xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); | |
++ xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4); | |
+ | |
+ /* Xft rendering context */ | |
+ xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); | |
+ | |
+@@ -1240,144 +1245,155 @@ xinit(int cols, int rows) | |
+ if (xsel.xtarget == None) | |
+ xsel.xtarget = XA_STRING; | |
+ } | |
+ | |
++void | |
++xresetfontsettings(ushort mode, Font **font, int *frcflags) | |
++{ | |
++ *font = &dc.font; | |
++ if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
++ *font = &dc.ibfont; | |
++ *frcflags = FRC_ITALICBOLD; | |
++ } else if (mode & ATTR_ITALIC) { | |
++ *font = &dc.ifont; | |
++ *frcflags = FRC_ITALIC; | |
++ } else if (mode & ATTR_BOLD) { | |
++ *font = &dc.bfont; | |
++ *frcflags = FRC_BOLD; | |
++ } | |
++} | |
++ | |
+ int | |
+ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, in… | |
+ { | |
+ float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp,… | |
+- ushort mode, prevmode = USHRT_MAX; | |
++ ushort mode = glyphs[0].mode & ~ATTR_WRAP; | |
+ Font *font = &dc.font; | |
+ int frcflags = FRC_NORMAL; | |
+- float runewidth = win.cw; | |
++ float runewidth = win.cw * ((glyphs[0].mode & ATTR_WIDE) ? 2.0f : 1.0… | |
+ Rune rune; | |
+ FT_UInt glyphidx; | |
+ FcResult fcres; | |
+ FcPattern *fcpattern, *fontpattern; | |
+ FcFontSet *fcsets[] = { NULL }; | |
+ FcCharSet *fccharset; | |
+- int i, f, numspecs = 0; | |
++ int f, code_idx, numspecs = 0; | |
++ float cluster_xp = xp, cluster_yp = yp; | |
++ HbTransformData shaped = { 0 }; | |
+ | |
+- for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { | |
+- /* Fetch rune and mode for current glyph. */ | |
+- rune = glyphs[i].u; | |
+- mode = glyphs[i].mode; | |
++ /* Initial values. */ | |
++ xresetfontsettings(mode, &font, &frcflags); | |
+ | |
+- /* Skip dummy wide-character spacing. */ | |
+- if (mode == ATTR_WDUMMY) | |
++ /* Shape the segment. */ | |
++ hbtransform(&shaped, font->match, glyphs, 0, len); | |
++ xp = winx; yp = winy + font->ascent; | |
++ cluster_xp = xp; cluster_yp = yp; | |
++ | |
++ for (code_idx = 0; code_idx < shaped.count; code_idx++) { | |
++ int idx = shaped.glyphs[code_idx].cluster; | |
++ | |
++ if (glyphs[idx].mode & ATTR_WDUMMY) | |
+ continue; | |
+ | |
+- /* Determine font for glyph if different from previous glyph.… | |
+- if (prevmode != mode) { | |
+- prevmode = mode; | |
+- font = &dc.font; | |
+- frcflags = FRC_NORMAL; | |
+- runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0… | |
+- if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { | |
+- font = &dc.ibfont; | |
+- frcflags = FRC_ITALICBOLD; | |
+- } else if (mode & ATTR_ITALIC) { | |
+- font = &dc.ifont; | |
+- frcflags = FRC_ITALIC; | |
+- } else if (mode & ATTR_BOLD) { | |
+- font = &dc.bfont; | |
+- frcflags = FRC_BOLD; | |
+- } | |
+- yp = winy + font->ascent; | |
++ /* Advance the drawing cursor if we've moved to a new cluster… | |
++ if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluste… | |
++ xp += runewidth; | |
++ cluster_xp = xp; | |
++ cluster_yp = yp; | |
+ } | |
+ | |
+- /* Lookup character index with default font. */ | |
+- glyphidx = XftCharIndex(xw.dpy, font->match, rune); | |
+- if (glyphidx) { | |
++ if (shaped.glyphs[code_idx].codepoint != 0) { | |
++ /* If symbol is found, put it into the specs. */ | |
+ specs[numspecs].font = font->match; | |
+- specs[numspecs].glyph = glyphidx; | |
+- specs[numspecs].x = (short)xp; | |
+- specs[numspecs].y = (short)yp; | |
+- xp += runewidth; | |
++ specs[numspecs].glyph = shaped.glyphs[code_idx].codep… | |
++ specs[numspecs].x = cluster_xp + (short)(shaped.posit… | |
++ specs[numspecs].y = cluster_yp - (short)(shaped.posit… | |
++ cluster_xp += shaped.positions[code_idx].x_advance / … | |
++ cluster_yp += shaped.positions[code_idx].y_advance / … | |
+ numspecs++; | |
+- continue; | |
+- } | |
+- | |
+- /* Fallback on font cache, search the font cache for match. */ | |
+- for (f = 0; f < frclen; f++) { | |
+- glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); | |
+- /* Everything correct. */ | |
+- if (glyphidx && frc[f].flags == frcflags) | |
+- break; | |
+- /* We got a default font for a not found glyph. */ | |
+- if (!glyphidx && frc[f].flags == frcflags | |
+- && frc[f].unicodep == rune) { | |
+- break; | |
++ } else { | |
++ /* If it's not found, try to fetch it through the fon… | |
++ rune = glyphs[idx].u; | |
++ for (f = 0; f < frclen; f++) { | |
++ glyphidx = XftCharIndex(xw.dpy, frc[f].font, … | |
++ /* Everything correct. */ | |
++ if (glyphidx && frc[f].flags == frcflags) | |
++ break; | |
++ /* We got a default font for a not found glyp… | |
++ if (!glyphidx && frc[f].flags == frcflags | |
++ && frc[f].unicodep == rune) { | |
++ break; | |
++ } | |
+ } | |
+- } | |
+ | |
+- /* Nothing was found. Use fontconfig to find matching font. */ | |
+- if (f >= frclen) { | |
+- if (!font->set) | |
+- font->set = FcFontSort(0, font->pattern, | |
+- 1, 0, &fcres); | |
+- fcsets[0] = font->set; | |
+- | |
+- /* | |
+- * Nothing was found in the cache. Now use | |
+- * some dozen of Fontconfig calls to get the | |
+- * font for one single character. | |
+- * | |
+- * Xft and fontconfig are design failures. | |
+- */ | |
+- fcpattern = FcPatternDuplicate(font->pattern); | |
+- fccharset = FcCharSetCreate(); | |
+- | |
+- FcCharSetAddChar(fccharset, rune); | |
+- FcPatternAddCharSet(fcpattern, FC_CHARSET, | |
+- fccharset); | |
+- FcPatternAddBool(fcpattern, FC_SCALABLE, 1); | |
+- | |
+- FcConfigSubstitute(0, fcpattern, | |
+- FcMatchPattern); | |
+- FcDefaultSubstitute(fcpattern); | |
+- | |
+- fontpattern = FcFontSetMatch(0, fcsets, 1, | |
+- fcpattern, &fcres); | |
+- | |
+- /* Allocate memory for the new cache entry. */ | |
+- if (frclen >= frccap) { | |
+- frccap += 16; | |
+- frc = xrealloc(frc, frccap * sizeof(Fontcache… | |
++ /* Nothing was found. Use fontconfig to find matching… | |
++ if (f >= frclen) { | |
++ if (!font->set) | |
++ font->set = FcFontSort(0, font->patte… | |
++ 1, 0, &fcres); | |
++ fcsets[0] = font->set; | |
++ | |
++ /* | |
++ * Nothing was found in the cache. Now use | |
++ * some dozen of Fontconfig calls to get the | |
++ * font for one single character. | |
++ * | |
++ * Xft and fontconfig are design failures. | |
++ */ | |
++ fcpattern = FcPatternDuplicate(font->pattern); | |
++ fccharset = FcCharSetCreate(); | |
++ | |
++ FcCharSetAddChar(fccharset, rune); | |
++ FcPatternAddCharSet(fcpattern, FC_CHARSET, | |
++ fccharset); | |
++ FcPatternAddBool(fcpattern, FC_SCALABLE, 1); | |
++ | |
++ FcConfigSubstitute(0, fcpattern, | |
++ FcMatchPattern); | |
++ FcDefaultSubstitute(fcpattern); | |
++ | |
++ fontpattern = FcFontSetMatch(0, fcsets, 1, | |
++ fcpattern, &fcres); | |
++ | |
++ /* Allocate memory for the new cache entry. */ | |
++ if (frclen >= frccap) { | |
++ frccap += 16; | |
++ frc = xrealloc(frc, frccap * sizeof(F… | |
++ } | |
++ | |
++ frc[frclen].font = XftFontOpenPattern(xw.dpy, | |
++ fontpattern); | |
++ if (!frc[frclen].font) | |
++ die("XftFontOpenPattern failed seekin… | |
++ strerror(errno)); | |
++ frc[frclen].flags = frcflags; | |
++ frc[frclen].unicodep = rune; | |
++ | |
++ glyphidx = XftCharIndex(xw.dpy, frc[frclen].f… | |
++ | |
++ f = frclen; | |
++ frclen++; | |
++ | |
++ FcPatternDestroy(fcpattern); | |
++ FcCharSetDestroy(fccharset); | |
+ } | |
+ | |
+- frc[frclen].font = XftFontOpenPattern(xw.dpy, | |
+- fontpattern); | |
+- if (!frc[frclen].font) | |
+- die("XftFontOpenPattern failed seeking fallba… | |
+- strerror(errno)); | |
+- frc[frclen].flags = frcflags; | |
+- frc[frclen].unicodep = rune; | |
+- | |
+- glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, run… | |
+- | |
+- f = frclen; | |
+- frclen++; | |
+- | |
+- FcPatternDestroy(fcpattern); | |
+- FcCharSetDestroy(fccharset); | |
++ specs[numspecs].font = frc[f].font; | |
++ specs[numspecs].glyph = glyphidx; | |
++ specs[numspecs].x = (short)xp; | |
++ specs[numspecs].y = (short)yp; | |
++ numspecs++; | |
+ } | |
+- | |
+- specs[numspecs].font = frc[f].font; | |
+- specs[numspecs].glyph = glyphidx; | |
+- specs[numspecs].x = (short)xp; | |
+- specs[numspecs].y = (short)yp; | |
+- xp += runewidth; | |
+- numspecs++; | |
+ } | |
+ | |
++ /* Cleanup and get ready for next segment. */ | |
++ hbcleanup(&shaped); | |
+ return numspecs; | |
+ } | |
+ | |
+ void | |
+-xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
++xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x… | |
+ { | |
+- int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1); | |
+ int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, | |
+ width = charlen * win.cw; | |
+ Color *fg, *bg, *temp, revfg, revbg, truefg, truebg; | |
+ XRenderColor colfg, colbg; | |
+@@ -1511,23 +1527,26 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Gly… | |
+ void | |
+ xdrawglyph(Glyph g, int x, int y) | |
+ { | |
+ int numspecs; | |
+- XftGlyphFontSpec spec; | |
++ XftGlyphFontSpec *specs = xw.specbuf; | |
+ | |
+- numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y); | |
+- xdrawglyphfontspecs(&spec, g, numspecs, x, y); | |
++ numspecs = xmakeglyphfontspecs(specs, &g, 1, x, y); | |
++ xdrawglyphfontspecs(specs, g, numspecs, x, y, (g.mode & ATTR_WIDE) ? … | |
+ } | |
+ | |
+ void | |
+-xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) | |
++xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int… | |
+ { | |
+ Color drawcol; | |
+ | |
+ /* remove the old cursor */ | |
+ if (selected(ox, oy)) | |
+ og.mode ^= ATTR_REVERSE; | |
+- xdrawglyph(og, ox, oy); | |
++ | |
++ /* Redraw the line where cursor was previously. | |
++ * It will restore the ligatures broken by the cursor. */ | |
++ xdrawline(line, 0, oy, len); | |
+ | |
+ if (IS_SET(MODE_HIDE)) | |
+ return; | |
+ | |
+@@ -1659,30 +1678,30 @@ xdrawline(Line line, int x1, int y1, int x2) | |
+ int i, x, ox, numspecs; | |
+ Glyph base, new; | |
+ XftGlyphFontSpec *specs = xw.specbuf; | |
+ | |
+- numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); | |
+ i = ox = 0; | |
+- for (x = x1; x < x2 && i < numspecs; x++) { | |
++ for (x = x1; x < x2; x++) { | |
+ new = line[x]; | |
+ if (new.mode == ATTR_WDUMMY) | |
+ continue; | |
+ if (selected(x, y1)) | |
+ new.mode ^= ATTR_REVERSE; | |
+- if (i > 0 && ATTRCMP(base, new)) { | |
+- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
+- specs += i; | |
+- numspecs -= i; | |
++ if ((i > 0) && ATTRCMP(base, new)) { | |
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x - … | |
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x … | |
+ i = 0; | |
+ } | |
+ if (i == 0) { | |
+ ox = x; | |
+ base = new; | |
+ } | |
+ i++; | |
+ } | |
+- if (i > 0) | |
+- xdrawglyphfontspecs(specs, base, i, ox, y1); | |
++ if (i > 0) { | |
++ numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox,… | |
++ xdrawglyphfontspecs(specs, base, numspecs, ox, y1, x2 - ox); | |
++ } | |
+ } | |
+ | |
+ void | |
+ xfinishdraw(void) | |
diff --git a/st.suckless.org/patches/ligatures/index.md b/st.suckless.org/patch… | |
@@ -28,13 +28,13 @@ Boxdraw | |
Download | |
-------- | |
**0.9.2**: | |
-* [st-ligatures-0.9.2](0.9.2/st-ligatures-20240427-0.9.2.diff) | |
-* [st-ligatures-scrollback-0.9.2](0.9.2/st-ligatures-scrollback-20240427-0.9.2… | |
-* [st-ligatures-scrollback-ringbuffer-0.9.2](0.9.2/st-ligatures-scrollback-rin… | |
-* [st-ligatures-alpha-0.9.2](0.9.2/st-ligatures-alpha-20240427-0.9.2.diff) | |
-* [st-ligatures-alpha-scrollback-0.9.2](0.9.2/st-ligatures-alpha-scrollback-20… | |
-* [st-ligatures-alpha-scrollback-ringbuffer-0.9.2](0.9.2/st-ligatures-alpha-sc… | |
-* [st-ligatures-boxdraw-0.9.2](0.9.2/st-ligatures-boxdraw-20240427-0.9.2.diff) | |
+* [st-ligatures-0.9.2](0.9.2/st-ligatures-20241226-0.9.2.diff) | |
+* [st-ligatures-scrollback-0.9.2](0.9.2/st-ligatures-scrollback-20241226-0.9.2… | |
+* [st-ligatures-scrollback-ringbuffer-0.9.2](0.9.2/st-ligatures-scrollback-rin… | |
+* [st-ligatures-alpha-0.9.2](0.9.2/st-ligatures-alpha-20241226-0.9.2.diff) | |
+* [st-ligatures-alpha-scrollback-0.9.2](0.9.2/st-ligatures-alpha-scrollback-20… | |
+* [st-ligatures-alpha-scrollback-ringbuffer-0.9.2](0.9.2/st-ligatures-alpha-sc… | |
+* [st-ligatures-boxdraw-0.9.2](0.9.2/st-ligatures-boxdraw-20241226-0.9.2.diff) | |
**0.9**: | |
* [st-ligatures-0.9](0.9/st-ligatures-20240105-0.9.diff) |