fbpad: support multiple fonts
authorAli Gholami Rudi <ali@rudi.ir>
Fri, 27 Apr 2012 14:29:58 +0000 (27 18:59 +0430)
committerAli Gholami Rudi <ali@rudi.ir>
Wed, 2 May 2012 13:57:01 +0000 (2 18:27 +0430)
The new "m-f x" command changes the fonts used in a terminal
where x denotes the fontset.  The fonts in each fontset are
defined in config.h: FR0, FI0, and FB0 are the regular,
italic, and bold font in fontset 0.

Makefile
README
config.h
fbpad.c
font.c
font.h
pad.c
pad.h
term.c

index 875b077..bd19e08 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@ CFLAGS = -Wall -O2
 LDFLAGS =
 
 all: fbpad
-%.o: %.c
+%.o: %.c config.h
        $(CC) -c $(CFLAGS) $<
 fbpad: fbpad.o term.o pad.o draw.o font.o scrsnap.o
        $(CC) -o $@ $^ $(LDFLAGS)
diff --git a/README b/README
index c16fbae..aefb810 100644 (file)
--- a/README
+++ b/README
@@ -21,13 +21,14 @@ m-tab               show next terminal
 m-s            create a text screenshot (SCRSHOT)
 m-y            redraw terminal
 c-m-q          quit fbpad
+m-f x          use font x in this terminal
 ==============  =======================================
 
 SETTING UP
 ==========
 
 You can edit config.h to configure fbpad.  To get fbpad running you need
-to make sure TINYFONT points to a valid tinyfont file and SHELL is the
+to make sure F* point to a valid tinyfont file and SHELL is the
 shell you want to execute.  Also the type of fbval_t should match
 the framebuffer depth: unsigned char for 8-bit, short for 16-bit and int
 for 24/32-bit framebuffers.  Once these are set, you should be able to
index dc64c08..3b226fc 100644 (file)
--- a/config.h
+++ b/config.h
@@ -4,7 +4,12 @@
 #define MAIL           "mailx"
 #define EDITOR         "vi"
 
-#define TINYFONT       "/path/to/tinyfont/file.tf"
+/* fontsets; tinyfont files for regular, italic, and bold fonts */
+#define F0             {"/path/to/font.tf", NULL, NULL}
+#define F1             {}
+#define F2             {}
+#define F3             {}
+#define F4             {}
 
 #define FGCOLOR                0
 #define BGCOLOR                7
diff --git a/fbpad.c b/fbpad.c
index d54b591..a33b820 100644 (file)
--- a/fbpad.c
+++ b/fbpad.c
 
 static char tags[] = TAGS;
 static struct term terms[NTERMS];
-static int tops[NTAGS];        /* top terms of tags */
-static int ctag;       /* current tag */
-static int ltag;       /* the last tag */
+static int tops[NTAGS];                /* top terms of tags */
+static int ctag;               /* current tag */
+static int ltag;               /* the last tag */
+static int fonts[NTERMS];      /* term fonts */
+static int setfont;            /* the next character is font */
 static int exitit;
 static int hidden;
 
@@ -59,6 +61,8 @@ static void term_switch(int oidx, int nidx, int show, int save, int load)
        term_save(&terms[oidx]);
        if (show && load && TERMOPEN(nidx) && TERMSNAP(nidx))
                flags = scr_load(&terms[nidx]) ? TERM_REDRAW : TERM_VISIBLE;
+       if (show)
+               pad_font(fonts[nidx]);
        term_load(&terms[nidx], flags);
 }
 
@@ -138,6 +142,14 @@ static void showtags(void)
 static void directkey(void)
 {
        int c = readchar();
+       if (setfont && c != -1) {
+               setfont = 0;
+               if (c != ESC && isdigit(c)) {
+                       fonts[cterm()] = c - '0';
+                       term_switch(cterm(), cterm(), 1, 0, 1);
+               }
+               return;
+       }
        if (c == ESC) {
                switch ((c = readchar())) {
                case 'c':
@@ -171,6 +183,9 @@ static void directkey(void)
                case 'y':
                        term_switch(cterm(), cterm(), 1, 0, 1);
                        return;
+               case 'f':
+                       setfont = 1;
+                       return;
                default:
                        if (strchr(tags, c)) {
                                showtag(strchr(tags, c) - tags);
diff --git a/font.c b/font.c
index c0acadc..6164134 100644 (file)
--- a/font.c
+++ b/font.c
@@ -7,11 +7,13 @@
 #include "font.h"
 #include "util.h"
 
-static int fd;
-static int rows;
-static int cols;
-static int n;
-static int *glyphs;
+struct font {
+       int fd;
+       int rows;
+       int cols;
+       int n;
+       int *glyphs;
+};
 
 /*
  * tinyfont format:
@@ -32,33 +34,35 @@ struct tinyfont {
        int rows, cols;
 };
 
-int font_init(void)
+struct font *font_open(char *path)
 {
+       struct font *font;
        struct tinyfont head;
-       fd = open(TINYFONT, O_RDONLY);
-       if (fd == -1)
-               return 1;
-       fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
-       if (read(fd, &head, sizeof(head)) != sizeof(head))
-               return 1;
-       n = head.n;
-       rows = head.rows;
-       cols = head.cols;
-       glyphs = malloc(n * sizeof(int));
-       if (read(fd, glyphs, n * sizeof(int)) != n * sizeof(int))
-               return 1;
-       return 0;
+       font = malloc(sizeof(*font));
+       font->fd = open(path, O_RDONLY);
+       if (font->fd == -1)
+               return NULL;
+       fcntl(font->fd, F_SETFD, fcntl(font->fd, F_GETFD) | FD_CLOEXEC);
+       if (read(font->fd, &head, sizeof(head)) != sizeof(head))
+               return NULL;
+       font->n = head.n;
+       font->rows = head.rows;
+       font->cols = head.cols;
+       font->glyphs = malloc(font->n * sizeof(int));
+       if (read(font->fd, font->glyphs, font->n * sizeof(int)) != font->n * sizeof(int))
+               return NULL;
+       return font;
 }
 
-static int find_glyph(int c)
+static int find_glyph(struct font *font, int c)
 {
        int l = 0;
-       int h = n;
+       int h = font->n;
        while (l < h) {
                int m = (l + h) / 2;
-               if (glyphs[m] == c)
+               if (font->glyphs[m] == c)
                        return m;
-               if (c < glyphs[m])
+               if (c < font->glyphs[m])
                        h = m;
                else
                        l = m + 1;
@@ -66,28 +70,30 @@ static int find_glyph(int c)
        return -1;
 }
 
-int font_bitmap(void *dst, int c)
+int font_bitmap(struct font *font, void *dst, int c)
 {
-       int i = find_glyph(c);
+       int i = find_glyph(font, c);
        if (i < 0)
                return 1;
-       lseek(fd, sizeof(struct tinyfont) + n * sizeof(int) + i * rows * cols, 0);
-       read(fd, dst, rows * cols);
+       lseek(font->fd, sizeof(struct tinyfont) + font->n * sizeof(int) +
+                                       i * font->rows * font->cols, 0);
+       read(font->fd, dst, font->rows * font->cols);
        return 0;
 }
 
-void font_free(void)
+void font_free(struct font *font)
 {
-       free(glyphs);
-       close(fd);
+       free(font->glyphs);
+       close(font->fd);
+       free(font);
 }
 
-int font_rows(void)
+int font_rows(struct font *font)
 {
-       return rows;
+       return font->rows;
 }
 
-int font_cols(void)
+int font_cols(struct font *font)
 {
-       return cols;
+       return font->cols;
 }
diff --git a/font.h b/font.h
index 1d875c5..c69d5ca 100644 (file)
--- a/font.h
+++ b/font.h
@@ -1,7 +1,7 @@
 #define MAXDOTS                (1 << 10)
 
-int font_init(void);
-void font_free(void);
-int font_rows(void);
-int font_cols(void);
-int font_bitmap(void *dst, int c);
+struct font *font_open(char *path);
+void font_free(struct font *font);
+int font_rows(struct font *font);
+int font_cols(struct font *font);
+int font_bitmap(struct font *font, void *dst, int c);
diff --git a/pad.c b/pad.c
index eb022ca..9dc003e 100644 (file)
--- a/pad.c
+++ b/pad.c
@@ -8,33 +8,64 @@
 #include "util.h"
 #include "pad.h"
 
+#define NCACHE         (1 << 11)
+#define MAXFBWIDTH     (1 << 12)
+
 static unsigned int cd[] = {
        COLOR0, COLOR1, COLOR2, COLOR3,
        COLOR4, COLOR5, COLOR6, COLOR7,
        COLOR8, COLOR9, COLOR10, COLOR11,
        COLOR12, COLOR13, COLOR14, COLOR15};
 static int rows, cols;
+static int fnrows, fncols;
+static struct font *fonts[32];
+static int nfonts;
+static int font;
+
+static int fnsets[16][3];
+static char *fnpaths[][3] = {F0, F1, F2, F3, F4};
+
+static int fontadd(char *path)
+{
+       fonts[nfonts] = font_open(path);
+       if (!fonts[nfonts]) {
+               fprintf(stderr, "pad: bad font <%s>\n", path);
+               return -1;
+       }
+       return nfonts++;
+}
 
 int pad_init(void)
 {
+       int i, j;
        if (fb_init())
                return 1;
        if (sizeof(fbval_t) != FBM_BPP(fb_mode())) {
                fprintf(stderr, "pad_init: fbval_t doesn't match fb depth\n");
                return 1;
        }
-       if (font_init()) {
-               fprintf(stderr, "pad_init: loading font failed\n");
-               return 1;
+       for (i = 0; i < ARRAY_SIZE(fnpaths); i++) {
+               for (j = 0; j < 3; j++) {
+                       int fn = -1;
+                       if (fnpaths[i][j])
+                               if ((fn = fontadd(fnpaths[i][j])) < 0)
+                                       return 1;
+                       if (fn < 0 && j > 0)
+                               fn = fnsets[i][0];
+                       if (fn < 0 && i > 0)
+                               fn = fnsets[0][j];
+                       fnsets[i][j] = fn;
+               }
        }
-       rows = fb_rows() / font_rows();
-       cols = fb_cols() / font_cols();
+       pad_font(0);
        return 0;
 }
 
 void pad_free(void)
 {
-       font_free();
+       int i;
+       for (i = 0; i < nfonts; i++)
+               font_free(fonts[i]);
        fb_free();
 }
 
@@ -43,7 +74,7 @@ void pad_free(void)
 #define CB(a)          ((a) & 0x0000ff)
 #define COLORMERGE(f, b, c)            ((b) + (((f) - (b)) * (c) >> 8u))
 
-static unsigned mixed_color(int fg, int bg, unsigned char val)
+static unsigned mixed_color(int fg, int bg, unsigned val)
 {
        unsigned int fore = cd[fg], back = cd[bg];
        unsigned char r = COLORMERGE(CR(fore), CR(back), val);
@@ -57,42 +88,64 @@ static unsigned color2fb(int c)
        return FB_VAL(CR(cd[c]), CG(cd[c]), CB(cd[c]));
 }
 
-#define NCACHE         (1 << 11)
-static fbval_t cache[NCACHE * MAXDOTS];
+static fbval_t cache[NCACHE][MAXDOTS];
 static struct glyph {
        int c;
+       int fn;
        short fg, bg;
-} cacheid[NCACHE];
+} glyphs[NCACHE];
+
+static struct glyph *glyph_entry(int c, int fn, int fg, int bg)
+{
+       return &glyphs[(c ^ (fg << 7) ^ (bg << 6) ^ (fn << 8)) & (NCACHE - 1)];
+}
+
+static fbval_t *glyph_cache(int c, int fn, short fg, short bg)
+{
+       struct glyph *g = glyph_entry(c, fn, fg, bg);
+       if (g->c == c && g->fn == fn && g->fg == fg && g->bg == bg)
+               return cache[g - glyphs];
+       return NULL;
+}
+
+static fbval_t *glyph_add(int c, int fn, short fg, short bg)
+{
+       struct glyph *g = glyph_entry(c, fn, fg, bg);
+       g->c = c;
+       g->fg = fg;
+       g->bg = bg;
+       g->fn = fn;
+       return cache[g - glyphs];
+}
 
-static int glyph_hash(int c, int fg, int bg)
+static void bmp2fb(fbval_t *d, char *s, int fg, int bg, int nr, int nc)
 {
-       return (c ^ (fg << 7) ^ (bg << 6)) & (NCACHE - 1);
+       int i, j;
+       for (i = 0; i < fnrows; i++) {
+               for (j = 0; j < fncols; j++) {
+                       unsigned v = i < nr && j < nc ?
+                               (unsigned char) s[i * nc + j] : 0;
+                       d[i * fncols + j] = mixed_color(fg, bg, v);
+               }
+       }
 }
 
-static fbval_t *bitmap(int c, short fg, short bg)
+static fbval_t *ch2fb(int fn, int c, short fg, short bg)
 {
-       unsigned char bits[MAXDOTS];
+       char bits[MAXDOTS];
        fbval_t *fbbits;
-       struct glyph *glyph;
-       int i;
-       int nbits = font_rows() * font_cols();
        if (c < 0 || (c < 256 && (!isprint(c) || isspace(c))))
                return NULL;
-       glyph = &cacheid[glyph_hash(c, fg, bg)];
-       fbbits = &cache[glyph_hash(c, fg, bg) * MAXDOTS];
-       if (glyph->c == c && glyph->fg == fg && glyph->bg == bg)
+       if ((fbbits = glyph_cache(c, fn, fg, bg)))
                return fbbits;
-       if (font_bitmap(bits, c))
+       if (font_bitmap(fonts[fn], bits, c))
                return NULL;
-       glyph->c = c;
-       glyph->fg = fg;
-       glyph->bg = bg;
-       for (i = 0; i < nbits; i++)
-               fbbits[i] = mixed_color(fg, bg, bits[i]);
+       fbbits = glyph_add(c, fn, fg, bg);
+       bmp2fb(fbbits, bits, FN_C(fg), FN_C(bg),
+               font_rows(fonts[fn]), font_cols(fonts[fn]));
        return fbbits;
 }
 
-#define MAXFBWIDTH     (1 << 12)
 static void fb_box(int sr, int sc, int er, int ec, fbval_t val)
 {
        static fbval_t line[MAXFBWIDTH];
@@ -104,30 +157,40 @@ static void fb_box(int sr, int sc, int er, int ec, fbval_t val)
                fb_set(i, sc, line, cn);
 }
 
+static int fnsel(int fg, int bg)
+{
+       if ((fg | bg) & FN_B)
+               return fnsets[font][2];
+       if ((fg | bg) & FN_I)
+               return fnsets[font][1];
+       return fnsets[font][0];
+}
+
 void pad_put(int ch, int r, int c, int fg, int bg)
 {
-       int sr = font_rows() * r;
-       int sc = font_cols() * c;
-       int frows = font_rows(), fcols = font_cols();
+       int sr = fnrows * r;
+       int sc = fncols * c;
        int i;
-       fbval_t *bits = bitmap(ch, fg, bg);
+       fbval_t *bits = ch2fb(fnsel(fg, bg), ch, fg, bg);
+       if (!bits)
+               bits = ch2fb(0, ch, fg, bg);
        if (!bits)
-               fb_box(sr, sc, sr + frows, sc + fcols, color2fb(bg));
+               fb_box(sr, sc, sr + fnrows, sc + fncols, color2fb(FN_C(bg)));
        else
-               for (i = 0; i < frows; i++)
-                       fb_set(sr + i, sc, bits + (i * fcols), fcols);
+               for (i = 0; i < fnrows; i++)
+                       fb_set(sr + i, sc, bits + (i * fncols), fncols);
 }
 
 void pad_blank(int c)
 {
-       fb_box(0, 0, fb_rows(), fb_cols(), color2fb(c));
+       fb_box(0, 0, fb_rows(), fb_cols(), color2fb(FN_C(c)));
 }
 
 void pad_blankrow(int r, int bg)
 {
-       int sr = r * font_rows();
-       int er = r == rows - 1 ? fb_rows() : (r + 1) * font_rows();
-       fb_box(sr, 0, er, fb_cols(), color2fb(bg));
+       int sr = r * fnrows;
+       int er = r == rows - 1 ? fb_rows() : (r + 1) * fnrows;
+       fb_box(sr, 0, er, fb_cols(), color2fb(FN_C(bg)));
 }
 
 int pad_rows(void)
@@ -139,3 +202,12 @@ int pad_cols(void)
 {
        return cols;
 }
+
+void pad_font(int i)
+{
+       font = i < ARRAY_SIZE(fnpaths) ? i : 0;
+       fnrows = font_rows(fonts[fnsets[0][0]]);
+       fncols = font_cols(fonts[fnsets[0][0]]);
+       rows = fb_rows() / fnrows;
+       cols = fb_cols() / fncols;
+}
diff --git a/pad.h b/pad.h
index 9b3d960..814b6d0 100644 (file)
--- a/pad.h
+++ b/pad.h
@@ -1,4 +1,7 @@
 #define MAXCHARS       (1 << 15)
+#define FN_I           0x10
+#define FN_B           0x20
+#define FN_C(fg)       (((fg) & FN_B ? (fg) + 8 : (fg)) & 0x0f)
 
 int pad_init(void);
 void pad_free(void);
@@ -7,3 +10,4 @@ int pad_rows(void);
 int pad_cols(void);
 void pad_blank(int c);
 void pad_blankrow(int r, int bg);
+void pad_font(int n);
diff --git a/term.c b/term.c
index 66415f1..5e5e9dd 100644 (file)
--- a/term.c
+++ b/term.c
 #define MODE_AUTOCR            0x08
 #define MODE_DEFAULT           (MODE_CURSOR | MODE_WRAP)
 #define ATTR_BOLD              0x10
-#define ATTR_REV               0x20
-#define ATTR_ALL               (ATTR_BOLD | ATTR_REV)
-#define MODE_INSERT            0x40
-#define MODE_WRAPREADY         0x80
+#define ATTR_ITALIC            0x20
+#define ATTR_REV               0x40
+#define ATTR_ALL               (ATTR_BOLD | ATTR_ITALIC | ATTR_REV)
+#define MODE_INSERT            0x100
+#define MODE_WRAPREADY         0x200
 
 #define BIT_SET(i, b, val)     ((val) ? ((i) | (b)) : ((i) & ~(b)))
 #define OFFSET(r, c)           ((r) * pad_cols() + (c))
@@ -46,7 +47,11 @@ static int lazy;
 static char fgcolor(void)
 {
        int c = mode & ATTR_REV ? bg : fg;
-       return mode & ATTR_BOLD ? c | 0x08 : c;
+       if (mode & ATTR_BOLD)
+               c |= FN_B;
+       if (mode & ATTR_ITALIC)
+               c |= FN_I;
+       return c;
 }
 
 static char bgcolor(void)
@@ -491,6 +496,9 @@ static void setattr(int m)
        case 1:
                mode |= ATTR_BOLD;
                break;
+       case 3:
+               mode |= ATTR_ITALIC;
+               break;
        case 7:
                mode |= ATTR_REV;
                break;