This adds support for specifying fonts as an array. This allows the user
to explicitly configure fonts that should be used as fallback fonts when
a glyph is not found in the previous font. Although it is possible to
define fallback fonts with fontconfig's fonts.conf, fonts.conf is a
complicated mess of XML whose configuration can also be further
complicated by interaction distro-specific and font-specific files, and
this patch brings st in line with dwm and dmenu which have supported
specifying fonts as an array for a number of years.
---
 config.def.h |  10 ++++-
 x.c          | 120 +++++++++++++++++++++++++++++++++------------------
 2 files changed, 85 insertions(+), 45 deletions(-)

diff --git a/config.def.h b/config.def.h
index 2cd740a..1da0d69 100644
--- a/config.def.h
+++ b/config.def.h
@@ -3,9 +3,15 @@
 /*
  * appearance
  *
- * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html
+ * fonts: see http://freedesktop.org/software/fontconfig/fontconfig-user.html
  */
-static char *font = "Liberation 
Mono:pixelsize=12:antialias=true:autohint=true";
+static char *fonts[] = {
+    "Liberation Mono:pixelsize=12:antialias=true:autohint=true",
+
+    /* CJK Fonts */
+    "VL Gothic:pixelsize=12:antialias=true:autohint=true",
+    "WenQuanYi Micro Hei:pixelsize=12:antialias=true:autohint=true",
+};
 static int borderpx = 2;
 
 /*
diff --git a/x.c b/x.c
index bd23686..b8e6187 100644
--- a/x.c
+++ b/x.c
@@ -132,11 +132,18 @@ typedef struct {
        FcPattern *pattern;
 } Font;
 
+typedef struct {
+       Font font;
+       Font bfont;
+       Font ifont;
+       Font ibfont;
+} FontFamily;
+
 /* Drawing Context */
 typedef struct {
        Color *col;
        size_t collen;
-       Font font, bfont, ifont, ibfont;
+       FontFamily fontfamily;
        GC gc;
 } DC;
 
@@ -221,6 +228,10 @@ static XWindow xw;
 static XSelection xsel;
 static TermWindow win;
 
+static FontFamily EmptyFontFamily;
+static FontFamily fontfamilies[16];
+static int fontfamiliescount = 0;
+
 /* Font Ring Cache */
 enum {
        FRC_NORMAL,
@@ -985,6 +996,11 @@ xloadfonts(const char *fontstr, double fontsize)
 {
        FcPattern *pattern;
        double fontval;
+       FontFamily fontfamily = EmptyFontFamily;
+
+       if (fontfamiliescount >= LEN(fontfamilies)) {
+               die("Font family array is full.\n");
+       }
 
        if (fontstr[0] == '-')
                pattern = XftXlfdParse(fontstr, False, False);
@@ -1017,37 +1033,43 @@ xloadfonts(const char *fontstr, double fontsize)
                defaultfontsize = usedfontsize;
        }
 
-       if (xloadfont(&dc.font, pattern))
+       if (xloadfont(&fontfamily.font, pattern))
                die("can't open font %s\n", fontstr);
 
        if (usedfontsize < 0) {
-               FcPatternGetDouble(dc.font.match->pattern,
+               FcPatternGetDouble(fontfamily.font.match->pattern,
                                   FC_PIXEL_SIZE, 0, &fontval);
                usedfontsize = fontval;
                if (fontsize == 0)
                        defaultfontsize = fontval;
        }
 
-       /* Setting character width and height. */
-       win.cw = ceilf(dc.font.width * cwscale);
-       win.ch = ceilf(dc.font.height * chscale);
-
        FcPatternDel(pattern, FC_SLANT);
        FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
-       if (xloadfont(&dc.ifont, pattern))
+       if (xloadfont(&fontfamily.ifont, pattern))
                die("can't open font %s\n", fontstr);
 
        FcPatternDel(pattern, FC_WEIGHT);
        FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
-       if (xloadfont(&dc.ibfont, pattern))
+       if (xloadfont(&fontfamily.ibfont, pattern))
                die("can't open font %s\n", fontstr);
 
        FcPatternDel(pattern, FC_SLANT);
        FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN);
-       if (xloadfont(&dc.bfont, pattern))
+       if (xloadfont(&fontfamily.bfont, pattern))
                die("can't open font %s\n", fontstr);
 
        FcPatternDestroy(pattern);
+
+       /* Setting character width and height. */
+       if (!fontfamiliescount) {
+               win.cw = ceilf(fontfamily.font.width * cwscale);
+               win.ch = ceilf(fontfamily.font.height * chscale);
+               dc.fontfamily = fontfamily;
+               usedfont = (char *) fontstr;
+       }
+
+       fontfamilies[fontfamiliescount++] = fontfamily;
 }
 
 void
@@ -1066,10 +1088,13 @@ xunloadfonts(void)
        while (frclen > 0)
                XftFontClose(xw.dpy, frc[--frclen].font);
 
-       xunloadfont(&dc.font);
-       xunloadfont(&dc.bfont);
-       xunloadfont(&dc.ifont);
-       xunloadfont(&dc.ibfont);
+       while (fontfamiliescount > 0) {
+               fontfamiliescount--;
+               xunloadfont(&fontfamilies[fontfamiliescount].font);
+               xunloadfont(&fontfamilies[fontfamiliescount].bfont);
+               xunloadfont(&fontfamilies[fontfamiliescount].ifont);
+               xunloadfont(&fontfamilies[fontfamiliescount].ibfont);
+       }
 }
 
 int
@@ -1134,6 +1159,7 @@ xinit(int cols, int rows)
        Window parent;
        pid_t thispid = getpid();
        XColor xmousefg, xmousebg;
+       int i;
 
        if (!(xw.dpy = XOpenDisplay(NULL)))
                die("can't open display\n");
@@ -1144,8 +1170,13 @@ xinit(int cols, int rows)
        if (!FcInit())
                die("could not init fontconfig.\n");
 
-       usedfont = (opt_font == NULL)? font : opt_font;
-       xloadfonts(usedfont, 0);
+       if (opt_font) {
+               xloadfonts(opt_font, 0);
+       }
+
+       for (i = 0; i < LEN(fonts); i++) {
+               xloadfonts(fonts[i], 0);
+       }
 
        /* colors */
        xw.cmap = XDefaultColormap(xw.dpy, xw.scr);
@@ -1242,9 +1273,9 @@ xinit(int cols, int rows)
 int
 xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int 
x, int y)
 {
-       float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, 
yp;
-       ushort mode, prevmode = USHRT_MAX;
-       Font *font = &dc.font;
+       float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp;
+       ushort mode;
+       Font *font, *reference = NULL;
        int frcflags = FRC_NORMAL;
        float runewidth = win.cw;
        Rune rune;
@@ -1255,7 +1286,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph 
*glyphs, int len, int x
        FcCharSet *fccharset;
        int i, f, numspecs = 0;
 
-       for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) {
+       for (i = 0, xp = winx; i < len; ++i) {
                /* Fetch rune and mode for current glyph. */
                rune = glyphs[i].u;
                mode = glyphs[i].mode;
@@ -1264,32 +1295,33 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const 
Glyph *glyphs, int len, int x
                if (mode == ATTR_WDUMMY)
                        continue;
 
-               /* Determine font for glyph if different from previous glyph. */
-               if (prevmode != mode) {
-                       prevmode = mode;
-                       font = &dc.font;
+               /* Lookup character index within user-defined fonts. */
+               for (glyphidx = 0, f = 0; f < fontfamiliescount && !glyphidx; 
f++) {
+                       font = &fontfamilies[f].font;
                        frcflags = FRC_NORMAL;
                        runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f);
                        if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) {
-                               font = &dc.ibfont;
+                               font = &fontfamilies[f].ibfont;
                                frcflags = FRC_ITALICBOLD;
                        } else if (mode & ATTR_ITALIC) {
-                               font = &dc.ifont;
+                               font = &fontfamilies[f].ifont;
                                frcflags = FRC_ITALIC;
                        } else if (mode & ATTR_BOLD) {
-                               font = &dc.bfont;
+                               font = &fontfamilies[f].bfont;
                                frcflags = FRC_BOLD;
                        }
-                       yp = winy + font->ascent;
+
+                       if (!f) {
+                               reference = font;
+                       }
+                       glyphidx = XftCharIndex(xw.dpy, font->match, rune);
                }
 
-               /* 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;
+                       specs[numspecs].y = (short) (winy + font->ascent);
                        xp += runewidth;
                        numspecs++;
                        continue;
@@ -1308,12 +1340,14 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const 
Glyph *glyphs, int len, int x
                        }
                }
 
-               /* Nothing was found. Use fontconfig to find matching font. */
+               /* Nothing was found. Use fontconfig to find matching font 
using the
+                * default font as the reference font.
+                */
                if (f >= frclen) {
-                       if (!font->set)
-                               font->set = FcFontSort(0, font->pattern,
-                                                      1, 0, &fcres);
-                       fcsets[0] = font->set;
+                       if (!reference->set)
+                               reference->set = FcFontSort(0, 
reference->pattern,
+                                                           1, 0, &fcres);
+                       fcsets[0] = reference->set;
 
                        /*
                         * Nothing was found in the cache. Now use
@@ -1322,7 +1356,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph 
*glyphs, int len, int x
                         *
                         * Xft and fontconfig are design failures.
                         */
-                       fcpattern = FcPatternDuplicate(font->pattern);
+                       fcpattern = FcPatternDuplicate(reference->pattern);
                        fccharset = FcCharSetCreate();
 
                        FcCharSetAddChar(fccharset, rune);
@@ -1363,7 +1397,7 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph 
*glyphs, int len, int x
                specs[numspecs].font = frc[f].font;
                specs[numspecs].glyph = glyphidx;
                specs[numspecs].x = (short)xp;
-               specs[numspecs].y = (short)yp;
+               specs[numspecs].y = (short) (winy + frc[f].font->ascent);
                xp += runewidth;
                numspecs++;
        }
@@ -1383,10 +1417,10 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, 
Glyph base, int len, int x, i
 
        /* Fallback on color display for attributes not supported by the font */
        if (base.mode & ATTR_ITALIC && base.mode & ATTR_BOLD) {
-               if (dc.ibfont.badslant || dc.ibfont.badweight)
+               if (dc.fontfamily.ibfont.badslant || 
dc.fontfamily.ibfont.badweight)
                        base.fg = defaultattr;
-       } else if ((base.mode & ATTR_ITALIC && dc.ifont.badslant) ||
-           (base.mode & ATTR_BOLD && dc.bfont.badweight)) {
+       } else if ((base.mode & ATTR_ITALIC && dc.fontfamily.ifont.badslant) ||
+           (base.mode & ATTR_BOLD && dc.fontfamily.bfont.badweight)) {
                base.fg = defaultattr;
        }
 
@@ -1493,12 +1527,12 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, 
Glyph base, int len, int x, i
 
        /* Render underline and strikethrough. */
        if (base.mode & ATTR_UNDERLINE) {
-               XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent * chscale 
+ 1,
+               XftDrawRect(xw.draw, fg, winx, winy + dc.fontfamily.font.ascent 
* chscale + 1,
                                width, 1);
        }
 
        if (base.mode & ATTR_STRUCK) {
-               XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent * 
chscale / 3,
+               XftDrawRect(xw.draw, fg, winx, winy + 2 * 
dc.fontfamily.font.ascent * chscale / 3,
                                width, 1);
        }
 
-- 
2.39.2


Reply via email to