From 9c44229c626ea7351a7809540435f40cffb624bc Mon Sep 17 00:00:00 2001 From: Christoph Lohmann <20h@r-36.net> Date: Fri, 28 Dec 2012 23:52:04 +0100 Subject: [PATCH] Adding fallback support to st. --- st.c | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 188 insertions(+), 13 deletions(-) diff --git a/st.c b/st.c index 8074df9..a440959 100644 --- a/st.c +++ b/st.c @@ -275,7 +275,9 @@ typedef struct { int descent; short lbearing; short rbearing; - XftFont *set; + XftFont *match; + FcFontSet *set; + FcPattern *pattern; } Font; /* Drawing Context */ @@ -338,10 +340,13 @@ static void xclear(int, int, int, int); static void xdrawcursor(void); static void xinit(void); static void xloadcols(void); +static int xloadfont(Font *, FcPattern *); +static void xloadfonts(char *, int); static void xresettitle(void); static void xseturgency(int); static void xsetsel(char*); static void xtermclear(int, int, int, int); +static void xunloadfonts(void); static void xresize(int, int); static void expose(XEvent *); @@ -350,7 +355,7 @@ static void unmap(XEvent *); static char *kmap(KeySym, uint); static void kpress(XEvent *); static void cmessage(XEvent *); -static void cresize(int width, int height); +static void cresize(int, int); static void resize(XEvent *); static void focus(XEvent *); static void brelease(XEvent *); @@ -373,7 +378,7 @@ static int isfullutf8(char *, int); static ssize_t xwrite(int, char *, size_t); static void *xmalloc(size_t); static void *xrealloc(void *, size_t); -static void *xcalloc(size_t nmemb, size_t size); +static void *xcalloc(size_t, size_t); static void (*handler[LASTEvent])(XEvent *) = { [KeyPress] = kpress, @@ -412,6 +417,28 @@ static char *opt_font = NULL; static char *usedfont = NULL; static int usedfontsize = 0; +/* Font Ring Cache */ +enum { + FRC_NORMAL, + FRC_ITALIC, + FRC_BOLD, + FRC_ITALICBOLD +}; + +typedef struct { + XftFont *font; + long c; + int flags; +} Fontcache; + +/* + * Fontcache is a ring buffer, with frccur as current position and frclen as + * the current length of used elements. + */ + +static Fontcache frc[256]; +static int frccur = 0, frclen = 0; + ssize_t xwrite(int fd, char *s, size_t len) { size_t aux = len; @@ -2282,20 +2309,28 @@ xloadfont(Font *f, FcPattern *pattern) { FcPattern *match; FcResult result; - match = XftFontMatch(xw.dpy, xw.scr, pattern, &result); + match = FcFontMatch(NULL, pattern, &result); if(!match) return 1; - if(!(f->set = XftFontOpenPattern(xw.dpy, match))) { + + if(!(f->set = FcFontSort(0, match, FcTrue, 0, &result))) { + FcPatternDestroy(match); + return 1; + } + + if(!(f->match = XftFontOpenPattern(xw.dpy, match))) { FcPatternDestroy(match); return 1; } - f->ascent = f->set->ascent; - f->descent = f->set->descent; + f->pattern = FcPatternDuplicate(pattern); + + f->ascent = f->match->ascent; + f->descent = f->match->descent; f->lbearing = 0; - f->rbearing = f->set->max_advance_width; + f->rbearing = f->match->max_advance_width; - f->height = f->set->height; + f->height = f->match->height; f->width = f->lbearing + f->rbearing; return 0; @@ -2334,6 +2369,9 @@ xloadfonts(char *fontstr, int fontsize) { } } + FcConfigSubstitute(0, pattern, FcMatchPattern); + FcDefaultSubstitute(pattern); + if(xloadfont(&dc.font, pattern)) die("st: can't open font %s\n", fontstr); @@ -2358,9 +2396,41 @@ xloadfonts(char *fontstr, int fontsize) { FcPatternDestroy(pattern); } +void +xunloadfonts(void) +{ + int i, ip; + + /* + * Free the loaded fonts in the font cache. This is done backwards + * from the frccur. + */ + for (i = 0, ip = frccur; i < frclen; i++, ip--) { + if (ip <= 0) + ip = LEN(frc) - 1; + XftFontClose(xw.dpy, frc[ip].font); + } + frccur = 0; + frclen = 0; + + XftFontClose(xw.dpy, dc.font.match); + FcPatternDestroy(dc.font.pattern); + FcFontSetDestroy(dc.font.set); + XftFontClose(xw.dpy, dc.bfont.match); + FcPatternDestroy(dc.bfont.pattern); + FcFontSetDestroy(dc.bfont.set); + XftFontClose(xw.dpy, dc.ifont.match); + FcPatternDestroy(dc.ifont.pattern); + FcFontSetDestroy(dc.ifont.set); + XftFontClose(xw.dpy, dc.ibfont.match); + FcPatternDestroy(dc.ibfont.pattern); + FcFontSetDestroy(dc.ibfont.set); +} + void xzoom(const Arg *arg) { + xunloadfonts(); xloadfonts(usedfont, usedfontsize + arg->i); cresize(0, 0); draw(); @@ -2379,6 +2449,9 @@ xinit(void) { xw.vis = XDefaultVisual(xw.dpy, xw.scr); /* font */ + if (!FcInit()) + die("Could not init fontconfig.\n"); + usedfont = (opt_font == NULL)? font : opt_font; xloadfonts(usedfont, 0); @@ -2459,13 +2532,22 @@ xinit(void) { void xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { int winx = borderpx + x * xw.cw, winy = borderpx + y * xw.ch, - width = charlen * xw.cw; + width = charlen * xw.cw, u8clen, xp, i, frp, frcflags; + long u8char; + char *u8c; Font *font = &dc.font; + XftFont *sfont; + FcResult fcres; + FcPattern *fcpattern, *fontpattern; + FcFontSet *fcsets[] = { NULL }; + FcCharSet *fccharset; XGlyphInfo extents; Colour *fg = &dc.col[base.fg], *bg = &dc.col[base.bg], *temp, revfg, revbg; XRenderColor colfg, colbg; + frcflags = FRC_NORMAL; + if(base.mode & ATTR_BOLD) { if(BETWEEN(base.fg, 0, 7)) { /* basic system colors */ @@ -2484,12 +2566,17 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { * 252 - 255 – brightest colors in greyscale */ font = &dc.bfont; + frcflags = FRC_BOLD; } - if(base.mode & ATTR_ITALIC) + if(base.mode & ATTR_ITALIC) { font = &dc.ifont; - if((base.mode & ATTR_ITALIC) && (base.mode & ATTR_BOLD)) + frcflags = FRC_ITALIC; + } + if((base.mode & ATTR_ITALIC) && (base.mode & ATTR_BOLD)) { font = &dc.ibfont; + frcflags = FRC_ITALICBOLD; + } if(IS_SET(MODE_REVERSE)) { if(fg == &dc.col[defaultfg]) { @@ -2521,7 +2608,8 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { bg = temp; } - XftTextExtentsUtf8(xw.dpy, font->set, (FcChar8 *)s, bytelen, + /* Width of the whole string that should be printed. */ + XftTextExtentsUtf8(xw.dpy, font->match, (FcChar8 *)s, bytelen, &extents); width = extents.xOff; @@ -2539,9 +2627,96 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { if(y == term.row-1) xclear(winx, winy + xw.ch, winx + width, xw.h); + /* Clean up the region we want to draw to. */ XftDrawRect(xw.draw, bg, winx, winy, width, xw.ch); + + /* + * Step through all UTF-8 characters one by one and search in the font + * cache ring buffer, whether there was some font found to display the + * unicode value of that UTF-8 character. + */ + fcsets[0] = font->set; + for (xp = winx; bytelen > 0; ) { + u8c = s; + u8clen = utf8decode(s, &u8char); + s += u8clen; + bytelen -= u8clen; + + sfont = font->match; + /* + * Only check the font cache or load new fonts, if the + * characters is not represented in main font. + */ + if (!XftCharExists(xw.dpy, font->match, u8char)) { + frp = frccur; + /* Search the font cache. */ + for (i = 0; i < frclen; i++, frp--) { + if (frp <= 0) + frp = LEN(frc) - 1; + + if (frc[frp].c == u8char + && frc[frp].flags == frcflags) { + break; + } + } + if (i >= frclen) { + /* + * Nothing was found in the cache. Now use + * some dozen of Fontconfig calls to get the + * font for one single character. + */ + fcpattern = FcPatternDuplicate(font->pattern); + fccharset = FcCharSetCreate(); + + FcCharSetAddChar(fccharset, u8char); + FcPatternAddCharSet(fcpattern, FC_CHARSET, + fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, + FcTrue); + + FcConfigSubstitute(0, fcpattern, + FcMatchPattern); + FcDefaultSubstitute(fcpattern); + + fontpattern = FcFontSetMatch(0, fcsets, + FcTrue, fcpattern, &fcres); + + frccur++; + frclen++; + if (frccur >= LEN(frc)) + frccur = 0; + if (frclen >= LEN(frc)) { + frclen = LEN(frc); + XftFontClose(xw.dpy, frc[frccur].font); + } + + /* + * Overwrite or create the new cache entry + * entry. + */ + frc[frccur].font = XftFontOpenPattern(xw.dpy, + fontpattern); + frc[frccur].c = u8char; + frc[frccur].flags = frcflags; + + FcPatternDestroy(fcpattern); + FcCharSetDestroy(fccharset); + + frp = frccur; + } + sfont = frc[frp].font; + } + + XftDrawStringUtf8(xw.draw, fg, sfont, xp, winy + sfont->ascent, + (FcChar8 *)u8c, u8clen); + + xp += font->width; + } + + /* XftDrawStringUtf8(xw.draw, fg, font->set, winx, winy + font->ascent, (FcChar8 *)s, bytelen); + */ if(base.mode & ATTR_UNDERLINE) { XftDrawRect(xw.draw, fg, winx, winy + font->ascent + 1,