123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541 |
- 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: options 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..59b9200
- --- /dev/null
- +++ b/hb.c
- @@ -0,0 +1,107 @@
- +#include <stdlib.h>
- +#include <stdio.h>
- +#include <math.h>
- +#include <X11/Xft/Xft.h>
- +#include <X11/cursorfont.h>
- +#include <hb.h>
- +#include <hb-ft.h>
- +
- +#include "st.h"
- +#include "hb.h"
- +
- +#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start = HB_FEATURE_GLOBAL_START, .end = HB_FEATURE_GLOBAL_END }
- +
- +hb_font_t *hbfindfont(XftFont *match);
- +
- +typedef struct {
- + XftFont *match;
- + hb_font_t *font;
- +} HbFontMatch;
- +
- +static int hbfontslen = 0;
- +static HbFontMatch *hbfontcache = 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 < hbfontslen; i++) {
- + hb_font_destroy(hbfontcache[i].font);
- + XftUnlockFace(hbfontcache[i].match);
- + }
- +
- + if (hbfontcache != NULL) {
- + free(hbfontcache);
- + hbfontcache = NULL;
- + }
- + hbfontslen = 0;
- +}
- +
- +hb_font_t *
- +hbfindfont(XftFont *match)
- +{
- + for (int i = 0; i < hbfontslen; i++) {
- + if (hbfontcache[i].match == match)
- + return hbfontcache[i].font;
- + }
- +
- + /* Font not found in cache, caching it now. */
- + hbfontcache = realloc(hbfontcache, sizeof(HbFontMatch) * (hbfontslen + 1));
- + FT_Face face = XftLockFace(match);
- + hb_font_t *font = hb_ft_font_create(face, NULL);
- + if (font == NULL)
- + die("Failed to load Harfbuzz font.");
- +
- + hbfontcache[hbfontslen].match = match;
- + hbfontcache[hbfontslen].font = font;
- + hbfontslen += 1;
- +
- + return font;
- +}
- +
- +void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, int start, int length) {
- + Rune rune;
- + ushort mode = USHRT_MAX;
- + unsigned int glyph_count;
- + int i, end = start + length;
- +
- + 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);
- +
- + /* Fill buffer with codepoints. */
- + for (i = start; i < end; i++) {
- + rune = glyphs[i].u;
- + mode = glyphs[i].mode;
- + if (mode & ATTR_WDUMMY)
- + rune = 0x0020;
- + hb_buffer_add_codepoints(buffer, &rune, 1, 0, 1);
- + }
- +
- + /* Shape the segment. */
- + hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t));
- +
- + /* Get new glyph info. */
- + hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_count);
- + hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &glyph_count);
- +
- + /** Fill the output. */
- + data->buffer = buffer;
- + data->glyphs = info;
- + data->positions = pos;
- + data->count = glyph_count;
- +}
- +
- +void hbcleanup(HbTransformData *data) {
- + hb_buffer_destroy(data->buffer);
- + memset(data, 0, sizeof(HbTransformData));
- +}
- diff --git a/hb.h b/hb.h
- new file mode 100644
- index 0000000..88de9bd
- --- /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) : (x)
- -#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \
- +#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode & (~ATTR_WRAP)) || \
- + (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..440bd2a 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,6 +142,7 @@ 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, int);
- static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int);
- static void xdrawglyph(Glyph, int, int);
- @@ -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);
- @@ -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, int x, int y)
- {
- @@ -1255,124 +1276,145 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
- FcPattern *fcpattern, *fontpattern;
- FcFontSet *fcsets[] = { NULL };
- FcCharSet *fccharset;
- - int i, f, numspecs = 0;
- + int i, f, length = 0, start = 0, numspecs = 0;
- + HbTransformData shaped = { 0 };
- +
- + /* Initial values. */
- + mode = prevmode = glyphs[0].mode;
- + 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;
-
- /* Skip dummy wide-character spacing. */
- - if (mode == ATTR_WDUMMY)
- + if (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.0f);
- - 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, length);
- + for (int code_idx = 0; code_idx < shaped.count; code_idx++) {
- + rune = glyphs[start + code_idx].u;
- + runewidth = win.cw * ((glyphs[start + code_idx].mode & ATTR_WIDE) ? 2.0f : 1.0f);
- +
- + if (glyphs[start + code_idx].mode & ATTR_WDUMMY)
- + continue;
- +
- + if (glyphs[start + code_idx].mode & ATTR_BOXDRAW) {
- + /* minor shoehorning: boxdraw uses only this ushort */
- + specs[numspecs].font = font->match;
- + specs[numspecs].glyph = boxdrawindex(&glyphs[start + code_idx]);
- + specs[numspecs].x = xp;
- + specs[numspecs].y = yp;
- + xp += runewidth;
- + numspecs++;
- + } else if (shaped.glyphs[code_idx].codepoint != 0) {
- + /* If symbol is found, put it into the specs. */
- + specs[numspecs].font = font->match;
- + specs[numspecs].glyph = shaped.glyphs[code_idx].codepoint;
- + specs[numspecs].x = xp + (short)shaped.positions[code_idx].x_offset;
- + specs[numspecs].y = yp + (short)shaped.positions[code_idx].y_offset;
- + xp += runewidth;
- + numspecs++;
- + } else {
- + /* If it's not found, try to fetch it through the font cache. */
- + for (f = 0; f < frclen; f++) {
- + glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune);
- + /* Everything correct. */
- + if (glyphidx && frc[f].flags == frcflags)
- + break;
- + /* We got a default font for a not found glyph. */
- + if (!glyphidx && frc[f].flags == frcflags
- + && frc[f].unicodep == rune) {
- + break;
- + }
- + }
- +
- + /* Nothing was found. Use fontconfig to find matching font. */
- + if (f >= frclen) {
- + if (!font->set)
- + font->set = FcFontSort(0, font->pattern,
- + 1, 0, &fcres);
- + fcsets[0] = font->set;
- +
- + /*
- + * Nothing was found in the cache. Now use
- + * some dozen of Fontconfig calls to get the
- + * font for one single character.
- + *
- + * Xft and fontconfig are design failures.
- + */
- + fcpattern = FcPatternDuplicate(font->pattern);
- + fccharset = FcCharSetCreate();
- +
- + FcCharSetAddChar(fccharset, rune);
- + FcPatternAddCharSet(fcpattern, FC_CHARSET,
- + fccharset);
- + FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
- +
- + FcConfigSubstitute(0, fcpattern,
- + FcMatchPattern);
- + FcDefaultSubstitute(fcpattern);
- +
- + fontpattern = FcFontSetMatch(0, fcsets, 1,
- + fcpattern, &fcres);
- +
- + /* Allocate memory for the new cache entry. */
- + if (frclen >= frccap) {
- + frccap += 16;
- + frc = xrealloc(frc, frccap * sizeof(Fontcache));
- + }
- +
- + frc[frclen].font = XftFontOpenPattern(xw.dpy,
- + fontpattern);
- + if (!frc[frclen].font)
- + die("XftFontOpenPattern failed seeking fallback font: %s\n",
- + strerror(errno));
- + frc[frclen].flags = frcflags;
- + frc[frclen].unicodep = rune;
- +
- + glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
- +
- + f = frclen;
- + frclen++;
- +
- + FcPatternDestroy(fcpattern);
- + FcCharSetDestroy(fccharset);
- + }
- +
- + specs[numspecs].font = frc[f].font;
- + specs[numspecs].glyph = glyphidx;
- + specs[numspecs].x = (short)xp;
- + specs[numspecs].y = (short)yp;
- + xp += runewidth;
- + numspecs++;
- + }
- }
- - }
-
- - /* Nothing was found. Use fontconfig to find matching font. */
- - if (f >= frclen) {
- - if (!font->set)
- - font->set = FcFontSort(0, font->pattern,
- - 1, 0, &fcres);
- - fcsets[0] = font->set;
- + /* Cleanup and get ready for next segment. */
- + hbcleanup(&shaped);
- + start = i;
-
- - /*
- - * Nothing was found in the cache. Now use
- - * 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 previous glyph. */
- + 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 fallback font: %s\n",
- - strerror(errno));
- - frc[frclen].flags = frcflags;
- - frc[frclen].unicodep = rune;
- -
- - glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune);
- -
- - f = frclen;
- - frclen++;
- -
- - FcPatternDestroy(fcpattern);
- - FcCharSetDestroy(fccharset);
- }
- -
- - specs[numspecs].font = frc[f].font;
- - specs[numspecs].glyph = glyphidx;
- - specs[numspecs].x = (short)xp;
- - specs[numspecs].y = (short)yp;
- - xp += runewidth;
- - numspecs++;
- }
-
- return numspecs;
- @@ -1528,14 +1570,17 @@ xdrawglyph(Glyph g, int x, int y)
- }
-
- 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 len)
- {
- 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;
|