| st-ligatures-alpha-20210824-0.8.4.diff - sites - public wiki contents of suckle… | |
| git clone git://git.suckless.org/sites | |
| Log | |
| Files | |
| Refs | |
| --- | |
| st-ligatures-alpha-20210824-0.8.4.diff (8235B) | |
| --- | |
| 1 diff --git a/Makefile b/Makefile | |
| 2 index 470ac86..38240da 100644 | |
| 3 --- a/Makefile | |
| 4 +++ b/Makefile | |
| 5 @@ -4,7 +4,7 @@ | |
| 6 | |
| 7 include config.mk | |
| 8 | |
| 9 -SRC = st.c x.c | |
| 10 +SRC = st.c x.c hb.c | |
| 11 OBJ = $(SRC:.c=.o) | |
| 12 | |
| 13 all: options st | |
| 14 @@ -22,7 +22,8 @@ config.h: | |
| 15 $(CC) $(STCFLAGS) -c $< | |
| 16 | |
| 17 st.o: config.h st.h win.h | |
| 18 -x.o: arg.h config.h st.h win.h | |
| 19 +x.o: arg.h config.h st.h win.h hb.h | |
| 20 +hb.o: st.h | |
| 21 | |
| 22 $(OBJ): config.h config.mk | |
| 23 | |
| 24 diff --git a/config.mk b/config.mk | |
| 25 index aaa54ff..1741840 100644 | |
| 26 --- a/config.mk | |
| 27 +++ b/config.mk | |
| 28 @@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config | |
| 29 # includes and libs | |
| 30 INCS = -I$(X11INC) \ | |
| 31 `$(PKG_CONFIG) --cflags fontconfig` \ | |
| 32 - `$(PKG_CONFIG) --cflags freetype2` | |
| 33 + `$(PKG_CONFIG) --cflags freetype2` \ | |
| 34 + `$(PKG_CONFIG) --cflags harfbuzz` | |
| 35 LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender\ | |
| 36 `$(PKG_CONFIG) --libs fontconfig` \ | |
| 37 - `$(PKG_CONFIG) --libs freetype2` | |
| 38 + `$(PKG_CONFIG) --libs freetype2` \ | |
| 39 + `$(PKG_CONFIG) --libs harfbuzz` | |
| 40 | |
| 41 # flags | |
| 42 STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 | |
| 43 diff --git a/hb.c b/hb.c | |
| 44 new file mode 100644 | |
| 45 index 0000000..f9c4f76 | |
| 46 --- /dev/null | |
| 47 +++ b/hb.c | |
| 48 @@ -0,0 +1,145 @@ | |
| 49 +#include <stdlib.h> | |
| 50 +#include <stdio.h> | |
| 51 +#include <math.h> | |
| 52 +#include <X11/Xft/Xft.h> | |
| 53 +#include <hb.h> | |
| 54 +#include <hb-ft.h> | |
| 55 + | |
| 56 +#include "st.h" | |
| 57 + | |
| 58 +#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, … | |
| 59 + | |
| 60 +void hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoi… | |
| 61 +hb_font_t *hbfindfont(XftFont *match); | |
| 62 + | |
| 63 +typedef struct { | |
| 64 + XftFont *match; | |
| 65 + hb_font_t *font; | |
| 66 +} HbFontMatch; | |
| 67 + | |
| 68 +static int hbfontslen = 0; | |
| 69 +static HbFontMatch *hbfontcache = NULL; | |
| 70 + | |
| 71 +/* | |
| 72 + * Poplulate the array with a list of font features, wrapped in FEATURE… | |
| 73 + * e. g. | |
| 74 + * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') | |
| 75 + */ | |
| 76 +hb_feature_t features[] = { }; | |
| 77 + | |
| 78 +void | |
| 79 +hbunloadfonts() | |
| 80 +{ | |
| 81 + for (int i = 0; i < hbfontslen; i++) { | |
| 82 + hb_font_destroy(hbfontcache[i].font); | |
| 83 + XftUnlockFace(hbfontcache[i].match); | |
| 84 + } | |
| 85 + | |
| 86 + if (hbfontcache != NULL) { | |
| 87 + free(hbfontcache); | |
| 88 + hbfontcache = NULL; | |
| 89 + } | |
| 90 + hbfontslen = 0; | |
| 91 +} | |
| 92 + | |
| 93 +hb_font_t * | |
| 94 +hbfindfont(XftFont *match) | |
| 95 +{ | |
| 96 + for (int i = 0; i < hbfontslen; i++) { | |
| 97 + if (hbfontcache[i].match == match) | |
| 98 + return hbfontcache[i].font; | |
| 99 + } | |
| 100 + | |
| 101 + /* Font not found in cache, caching it now. */ | |
| 102 + hbfontcache = realloc(hbfontcache, sizeof(HbFontMatch) * (hbfon… | |
| 103 + FT_Face face = XftLockFace(match); | |
| 104 + hb_font_t *font = hb_ft_font_create(face, NULL); | |
| 105 + if (font == NULL) | |
| 106 + die("Failed to load Harfbuzz font."); | |
| 107 + | |
| 108 + hbfontcache[hbfontslen].match = match; | |
| 109 + hbfontcache[hbfontslen].font = font; | |
| 110 + hbfontslen += 1; | |
| 111 + | |
| 112 + return font; | |
| 113 +} | |
| 114 + | |
| 115 +void | |
| 116 +hbtransform(XftGlyphFontSpec *specs, const Glyph *glyphs, size_t len, i… | |
| 117 +{ | |
| 118 + int start = 0, length = 1, gstart = 0; | |
| 119 + hb_codepoint_t *codepoints = calloc((unsigned int)len, sizeof(h… | |
| 120 + | |
| 121 + for (int idx = 1, specidx = 1; idx < len; idx++) { | |
| 122 + if (glyphs[idx].mode & ATTR_WDUMMY) { | |
| 123 + length += 1; | |
| 124 + continue; | |
| 125 + } | |
| 126 + | |
| 127 + if (specs[specidx].font != specs[start].font || ATTRCMP… | |
| 128 + hbtransformsegment(specs[start].font, glyphs, c… | |
| 129 + | |
| 130 + /* Reset the sequence. */ | |
| 131 + length = 1; | |
| 132 + start = specidx; | |
| 133 + gstart = idx; | |
| 134 + } else { | |
| 135 + length += 1; | |
| 136 + } | |
| 137 + | |
| 138 + specidx++; | |
| 139 + } | |
| 140 + | |
| 141 + /* EOL. */ | |
| 142 + hbtransformsegment(specs[start].font, glyphs, codepoints, gstar… | |
| 143 + | |
| 144 + /* Apply the transformation to glyph specs. */ | |
| 145 + for (int i = 0, specidx = 0; i < len; i++) { | |
| 146 + if (glyphs[i].mode & ATTR_WDUMMY) | |
| 147 + continue; | |
| 148 + | |
| 149 + if (codepoints[i] != specs[specidx].glyph) | |
| 150 + ((Glyph *)glyphs)[i].mode |= ATTR_LIGA; | |
| 151 + | |
| 152 + specs[specidx++].glyph = codepoints[i]; | |
| 153 + } | |
| 154 + | |
| 155 + free(codepoints); | |
| 156 +} | |
| 157 + | |
| 158 +void | |
| 159 +hbtransformsegment(XftFont *xfont, const Glyph *string, hb_codepoint_t … | |
| 160 +{ | |
| 161 + hb_font_t *font = hbfindfont(xfont); | |
| 162 + if (font == NULL) | |
| 163 + return; | |
| 164 + | |
| 165 + Rune rune; | |
| 166 + ushort mode = USHRT_MAX; | |
| 167 + hb_buffer_t *buffer = hb_buffer_create(); | |
| 168 + hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); | |
| 169 + | |
| 170 + /* Fill buffer with codepoints. */ | |
| 171 + for (int i = start; i < (start+length); i++) { | |
| 172 + rune = string[i].u; | |
| 173 + mode = string[i].mode; | |
| 174 + if (mode & ATTR_WDUMMY) | |
| 175 + rune = 0x0020; | |
| 176 + hb_buffer_add_codepoints(buffer, &rune, 1, 0, 1); | |
| 177 + } | |
| 178 + | |
| 179 + /* Shape the segment. */ | |
| 180 + hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_fea… | |
| 181 + | |
| 182 + /* Get new glyph info. */ | |
| 183 + hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, NULL); | |
| 184 + | |
| 185 + /* Write new codepoints. */ | |
| 186 + for (int i = 0; i < length; i++) { | |
| 187 + hb_codepoint_t gid = info[i].codepoint; | |
| 188 + codepoints[start+i] = gid; | |
| 189 + } | |
| 190 + | |
| 191 + /* Cleanup. */ | |
| 192 + hb_buffer_destroy(buffer); | |
| 193 +} | |
| 194 diff --git a/hb.h b/hb.h | |
| 195 new file mode 100644 | |
| 196 index 0000000..07888df | |
| 197 --- /dev/null | |
| 198 +++ b/hb.h | |
| 199 @@ -0,0 +1,6 @@ | |
| 200 +#include <X11/Xft/Xft.h> | |
| 201 +#include <hb.h> | |
| 202 +#include <hb-ft.h> | |
| 203 + | |
| 204 +void hbunloadfonts(); | |
| 205 +void hbtransform(XftGlyphFontSpec *, const Glyph *, size_t, int, int); | |
| 206 diff --git a/st.c b/st.c | |
| 207 index 76b7e0d..e60bf58 100644 | |
| 208 --- a/st.c | |
| 209 +++ b/st.c | |
| 210 @@ -2581,7 +2581,8 @@ draw(void) | |
| 211 | |
| 212 drawregion(0, 0, term.col, term.row); | |
| 213 xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], | |
| 214 - term.ocx, term.ocy, term.line[term.ocy][term.oc… | |
| 215 + term.ocx, term.ocy, term.line[term.ocy][term.oc… | |
| 216 + term.line[term.ocy], term.col); | |
| 217 term.ocx = cx; | |
| 218 term.ocy = term.c.y; | |
| 219 xfinishdraw(); | |
| 220 diff --git a/st.h b/st.h | |
| 221 index 9f11a6a..4b72307 100644 | |
| 222 --- a/st.h | |
| 223 +++ b/st.h | |
| 224 @@ -11,7 +11,8 @@ | |
| 225 #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) | |
| 226 #define DEFAULT(a, b) (a) = (a) ? (a) : (b) | |
| 227 #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b)… | |
| 228 -#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg !=… | |
| 229 +#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP) & (~ATTR… | |
| 230 + (a).fg != (b).fg || \ | |
| 231 (a).bg != (b).bg) | |
| 232 #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ | |
| 233 (t1.tv_nsec-t2.tv_nsec)/1E6) | |
| 234 @@ -33,6 +34,7 @@ enum glyph_attribute { | |
| 235 ATTR_WRAP = 1 << 8, | |
| 236 ATTR_WIDE = 1 << 9, | |
| 237 ATTR_WDUMMY = 1 << 10, | |
| 238 + ATTR_LIGA = 1 << 11, | |
| 239 ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, | |
| 240 }; | |
| 241 | |
| 242 diff --git a/win.h b/win.h | |
| 243 index a6ef1b9..bc0d180 100644 | |
| 244 --- a/win.h | |
| 245 +++ b/win.h | |
| 246 @@ -25,7 +25,7 @@ enum win_mode { | |
| 247 | |
| 248 void xbell(void); | |
| 249 void xclipcopy(void); | |
| 250 -void xdrawcursor(int, int, Glyph, int, int, Glyph); | |
| 251 +void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int); | |
| 252 void xdrawline(Line, int, int, int); | |
| 253 void xfinishdraw(void); | |
| 254 void xloadcols(void); | |
| 255 diff --git a/x.c b/x.c | |
| 256 index 73545d8..9e980e3 100644 | |
| 257 --- a/x.c | |
| 258 +++ b/x.c | |
| 259 @@ -19,6 +19,7 @@ char *argv0; | |
| 260 #include "arg.h" | |
| 261 #include "st.h" | |
| 262 #include "win.h" | |
| 263 +#include "hb.h" | |
| 264 | |
| 265 /* types used in config.h */ | |
| 266 typedef struct { | |
| 267 @@ -1040,6 +1041,9 @@ xunloadfont(Font *f) | |
| 268 void | |
| 269 xunloadfonts(void) | |
| 270 { | |
| 271 + /* Clear Harfbuzz font cache. */ | |
| 272 + hbunloadfonts(); | |
| 273 + | |
| 274 /* Free the loaded fonts in the font cache. */ | |
| 275 while (frclen > 0) | |
| 276 XftFontClose(xw.dpy, frc[--frclen].font); | |
| 277 @@ -1246,7 +1250,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const… | |
| 278 mode = glyphs[i].mode; | |
| 279 | |
| 280 /* Skip dummy wide-character spacing. */ | |
| 281 - if (mode == ATTR_WDUMMY) | |
| 282 + if (mode & ATTR_WDUMMY) | |
| 283 continue; | |
| 284 | |
| 285 /* Determine font for glyph if different from previous … | |
| 286 @@ -1353,6 +1357,9 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const… | |
| 287 numspecs++; | |
| 288 } | |
| 289 | |
| 290 + /* Harfbuzz transformation for ligatures. */ | |
| 291 + hbtransform(specs, glyphs, len, x, y); | |
| 292 + | |
| 293 return numspecs; | |
| 294 } | |
| 295 | |
| 296 @@ -1502,14 +1509,17 @@ xdrawglyph(Glyph g, int x, int y) | |
| 297 } | |
| 298 | |
| 299 void | |
| 300 -xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) | |
| 301 +xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line lin… | |
| 302 { | |
| 303 Color drawcol; | |
| 304 | |
| 305 /* remove the old cursor */ | |
| 306 if (selected(ox, oy)) | |
| 307 og.mode ^= ATTR_REVERSE; | |
| 308 - xdrawglyph(og, ox, oy); | |
| 309 + | |
| 310 + /* Redraw the line where cursor was previously. | |
| 311 + * It will restore the ligatures broken by the cursor. */ | |
| 312 + xdrawline(line, 0, oy, len); | |
| 313 | |
| 314 if (IS_SET(MODE_HIDE)) | |
| 315 return; |