]> git.armaanb.net Git - st.git/blobdiff - st.c
Revert "Optimize memory footprint of line buffers"
[st.git] / st.c
diff --git a/st.c b/st.c
index 4add638d0340d76f9334e796a117420201b229f5..3dd5caf876d91a104e79234ed9c5114a97a8620c 100644 (file)
--- a/st.c
+++ b/st.c
@@ -58,7 +58,6 @@ char *argv0;
 #define ESC_ARG_SIZ   16
 #define STR_BUF_SIZ   ESC_BUF_SIZ
 #define STR_ARG_SIZ   ESC_ARG_SIZ
-#define DRAW_BUF_SIZ  20*1024
 #define XK_ANY_MOD    UINT_MAX
 #define XK_NO_MOD     0
 #define XK_SWITCH_MOD (1<<13)
@@ -72,7 +71,7 @@ char *argv0;
 #define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177')
 #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f))
 #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c))
-#define ISDELIM(u) (BETWEEN(u, 0, 127) && strchr(worddelimiters, u) != NULL)
+#define ISDELIM(u) (utf8strchr(worddelimiters, u) != NULL)
 #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 || (a).bg != (b).bg)
 #define IS_SET(flag) ((term.mode & (flag)) != 0)
@@ -87,18 +86,19 @@ char *argv0;
 
 
 enum glyph_attribute {
-       ATTR_NULL      = 0,
-       ATTR_BOLD      = 1 << 0,
-       ATTR_FAINT     = 1 << 1,
-       ATTR_ITALIC    = 1 << 2,
-       ATTR_UNDERLINE = 1 << 3,
-       ATTR_BLINK     = 1 << 4,
-       ATTR_REVERSE   = 1 << 5,
-       ATTR_INVISIBLE = 1 << 6,
-       ATTR_STRUCK    = 1 << 7,
-       ATTR_WRAP      = 1 << 8,
-       ATTR_WIDE      = 1 << 9,
-       ATTR_WDUMMY    = 1 << 10,
+       ATTR_NULL       = 0,
+       ATTR_BOLD       = 1 << 0,
+       ATTR_FAINT      = 1 << 1,
+       ATTR_ITALIC     = 1 << 2,
+       ATTR_UNDERLINE  = 1 << 3,
+       ATTR_BLINK      = 1 << 4,
+       ATTR_REVERSE    = 1 << 5,
+       ATTR_INVISIBLE  = 1 << 6,
+       ATTR_STRUCK     = 1 << 7,
+       ATTR_WRAP       = 1 << 8,
+       ATTR_WIDE       = 1 << 9,
+       ATTR_WDUMMY     = 1 << 10,
+       ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
 };
 
 enum cursor_movement {
@@ -191,8 +191,8 @@ typedef XftColor Color;
 typedef struct {
        Rune u;           /* character code */
        ushort mode;      /* attribute flags */
-       ushort fg;        /* foreground  */
-       ushort bg;        /* background  */
+       uint32_t fg;      /* foreground  */
+       uint32_t bg;      /* background  */
 } Glyph;
 
 typedef Glyph *Line;
@@ -232,6 +232,7 @@ typedef struct {
        Line *line;   /* screen */
        Line *alt;    /* alternate screen */
        bool *dirty;  /* dirtyness of lines */
+       XftGlyphFontSpec *specbuf; /* font spec buffer used for rendering */
        TCursor c;    /* cursor */
        int top;      /* top    scroll limit */
        int bot;      /* bottom scroll limit */
@@ -418,7 +419,8 @@ static void ttywrite(const char *, size_t);
 static void tstrsequence(uchar);
 
 static inline ushort sixd_to_16bit(int);
-static void xdraws(char *, Glyph, int, int, int, int);
+static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int);
+static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int);
 static void xdrawglyph(Glyph, int, int);
 static void xhints(void);
 static void xclear(int, int, int, int);
@@ -471,6 +473,7 @@ static size_t utf8decode(char *, Rune *, size_t);
 static Rune utf8decodebyte(char, size_t *);
 static size_t utf8encode(Rune, char *);
 static char utf8encodebyte(Rune, size_t);
+static char *utf8strchr(char *s, Rune u);
 static size_t utf8validate(Rune *, size_t);
 
 static ssize_t xwrite(int, const char *, size_t);
@@ -638,6 +641,21 @@ utf8encodebyte(Rune u, size_t i) {
        return utfbyte[i] | (u & ~utfmask[i]);
 }
 
+char *
+utf8strchr(char *s, Rune u) {
+       Rune r;
+       size_t i, j, len;
+
+       len = strlen(s);
+       for(i = 0, j = 0; i < len; i += j) {
+               if(!(j = utf8decode(&s[i], &r, len - i)))
+                       break;
+               if(r == u)
+                       return &(s[i]);
+       }
+       return NULL;
+}
+
 size_t
 utf8validate(Rune *u, size_t i) {
        if(!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
@@ -1067,6 +1085,7 @@ void
 selclear(XEvent *e) {
        if(sel.ob.x == -1)
                return;
+       sel.mode = SEL_IDLE;
        sel.ob.x = -1;
        tsetdirt(sel.nb.y, sel.ne.y);
 }
@@ -2819,6 +2838,9 @@ tresize(int col, int row) {
                free(term.alt[i]);
        }
 
+       /* resize to new width */
+       term.specbuf = xrealloc(term.specbuf, col * sizeof(XftGlyphFontSpec));
+
        /* resize to new height */
        term.line = xrealloc(term.line, row * sizeof(Line));
        term.alt  = xrealloc(term.alt,  row * sizeof(Line));
@@ -3257,38 +3279,155 @@ xinit(void) {
        XSync(xw.dpy, False);
 }
 
-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, xp, i;
-       int frcflags, charexists;
-       int u8fl, u8fblen, u8cblen, doesexist;
-       char *u8c, *u8fs;
-       Rune unicodep;
+int
+xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
+{
+       float winx = borderpx + x * xw.cw, winy = borderpx + y * xw.ch, xp, yp;
+       ushort mode, prevmode = USHRT_MAX;
        Font *font = &dc.font;
+       int frcflags = FRC_NORMAL;
+       float runewidth = xw.cw;
+       Rune rune;
+       FT_UInt glyphidx;
        FcResult fcres;
        FcPattern *fcpattern, *fontpattern;
        FcFontSet *fcsets[] = { NULL };
        FcCharSet *fccharset;
+       int i, f, numspecs = 0;
+
+       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)
+                       continue;
+
+               /* Determine font for glyph if different from previous glyph. */
+               if(prevmode != mode) {
+                       prevmode = mode;
+                       font = &dc.font;
+                       frcflags = FRC_NORMAL;
+                       runewidth = xw.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;
+                       }
+                       yp = winy + font->ascent;
+               }
+
+               /* 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;
+                       }
+               }
+
+               /* Nothing was found. Use fontconfig to find matching font. */
+               if(f >= frclen) {
+                       if(!font->set)
+                               font->set = FcFontSort(0, font->pattern,
+                                                      FcTrue, 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,
+                                       FcTrue);
+
+                       FcConfigSubstitute(0, fcpattern,
+                                       FcMatchPattern);
+                       FcDefaultSubstitute(fcpattern);
+
+                       fontpattern = FcFontSetMatch(0, fcsets, 1,
+                                       fcpattern, &fcres);
+
+                       /*
+                        * Overwrite or create the new cache entry.
+                        */
+                       if(frclen >= LEN(frc)) {
+                               frclen = LEN(frc) - 1;
+                               XftFontClose(xw.dpy, frc[frclen].font);
+                               frc[frclen].unicodep = 0;
+                       }
+
+                       frc[frclen].font = XftFontOpenPattern(xw.dpy,
+                                       fontpattern);
+                       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)(winy + frc[f].font->ascent);
+               xp += runewidth;
+               numspecs++;
+       }
+
+       return numspecs;
+}
+
+void
+xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y) {
+       int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
+       int winx = borderpx + x * xw.cw, winy = borderpx + y * xw.ch,
+           width = charlen * xw.cw;
        Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
        XRenderColor colfg, colbg;
        XRectangle r;
-       int oneatatime;
 
-       frcflags = FRC_NORMAL;
-
-       if(base.mode & ATTR_ITALIC) {
-               if(base.fg == defaultfg)
+       /* Determine foreground and background colors based on mode. */
+       if(base.fg == defaultfg) {
+               if(base.mode & ATTR_ITALIC)
                        base.fg = defaultitalic;
-               font = &dc.ifont;
-               frcflags = FRC_ITALIC;
-       } else if((base.mode & ATTR_ITALIC) && (base.mode & ATTR_BOLD)) {
-               if(base.fg == defaultfg)
+               else if((base.mode & ATTR_ITALIC) && (base.mode & ATTR_BOLD))
                        base.fg = defaultitalic;
-               font = &dc.ibfont;
-               frcflags = FRC_ITALICBOLD;
-       } else if(base.mode & ATTR_UNDERLINE) {
-               if(base.fg == defaultfg)
+               else if(base.mode & ATTR_UNDERLINE)
                        base.fg = defaultunderline;
        }
 
@@ -3314,22 +3453,9 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
                bg = &dc.col[base.bg];
        }
 
-       if(base.mode & ATTR_BOLD) {
-               /*
-                * change basic system colors [0-7]
-                * to bright system colors [8-15]
-                */
-               if(BETWEEN(base.fg, 0, 7) && !(base.mode & ATTR_FAINT))
-                       fg = &dc.col[base.fg + 8];
-
-               if(base.mode & ATTR_ITALIC) {
-                       font = &dc.ibfont;
-                       frcflags = FRC_ITALICBOLD;
-               } else {
-                       font = &dc.bfont;
-                       frcflags = FRC_BOLD;
-               }
-       }
+       /* Change basic system colors [0-7] to bright system colors [8-15] */
+       if((base.mode & ATTR_BOLD_FAINT) == ATTR_BOLD && BETWEEN(base.fg, 0, 7))
+               fg = &dc.col[base.fg + 8];
 
        if(IS_SET(MODE_REVERSE)) {
                if(fg == &dc.col[defaultfg]) {
@@ -3363,7 +3489,7 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
                bg = temp;
        }
 
-       if(base.mode & ATTR_FAINT && !(base.mode & ATTR_BOLD)) {
+       if((base.mode & ATTR_BOLD_FAINT) == ATTR_FAINT) {
                colfg.red = fg->color.red / 2;
                colfg.green = fg->color.green / 2;
                colfg.blue = fg->color.blue / 2;
@@ -3401,136 +3527,17 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
        r.width = width;
        XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1);
 
-       for(xp = winx; bytelen > 0;) {
-               /*
-                * Search for the range in the to be printed string of glyphs
-                * that are in the main font. Then print that range. If
-                * some glyph is found that is not in the font, do the
-                * fallback dance.
-                */
-               u8fs = s;
-               u8fblen = 0;
-               u8fl = 0;
-               oneatatime = font->width != xw.cw;
-               for(;;) {
-                       u8c = s;
-                       u8cblen = utf8decode(s, &unicodep, UTF_SIZ);
-                       s += u8cblen;
-                       bytelen -= u8cblen;
-
-                       doesexist = XftCharExists(xw.dpy, font->match, unicodep);
-                       if(doesexist) {
-                                       u8fl++;
-                                       u8fblen += u8cblen;
-                                       if(!oneatatime && bytelen > 0)
-                                                       continue;
-                       }
-
-                       if(u8fl > 0) {
-                               XftDrawStringUtf8(xw.draw, fg,
-                                               font->match, xp,
-                                               winy + font->ascent,
-                                               (FcChar8 *)u8fs,
-                                               u8fblen);
-                               xp += xw.cw * u8fl;
-                       }
-                       break;
-               }
-               if(doesexist) {
-                       if(oneatatime)
-                               continue;
-                       break;
-               }
-
-               /* Search the font cache. */
-               for(i = 0; i < frclen; i++) {
-                       charexists = XftCharExists(xw.dpy, frc[i].font, unicodep);
-                       /* Everything correct. */
-                       if(charexists && frc[i].flags == frcflags)
-                               break;
-                       /* We got a default font for a not found glyph. */
-                       if(!charexists && frc[i].flags == frcflags \
-                                       && frc[i].unicodep == unicodep) {
-                               break;
-                       }
-               }
-
-               /* Nothing was found. */
-               if(i >= frclen) {
-                       if(!font->set)
-                               font->set = FcFontSort(0, font->pattern,
-                                                      FcTrue, 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, unicodep);
-                       FcPatternAddCharSet(fcpattern, FC_CHARSET,
-                                       fccharset);
-                       FcPatternAddBool(fcpattern, FC_SCALABLE,
-                                       FcTrue);
-
-                       FcConfigSubstitute(0, fcpattern,
-                                       FcMatchPattern);
-                       FcDefaultSubstitute(fcpattern);
-
-                       fontpattern = FcFontSetMatch(0, fcsets, 1,
-                                       fcpattern, &fcres);
-
-                       /*
-                        * Overwrite or create the new cache entry.
-                        */
-                       if(frclen >= LEN(frc)) {
-                               frclen = LEN(frc) - 1;
-                               XftFontClose(xw.dpy, frc[frclen].font);
-                               frc[frclen].unicodep = 0;
-                       }
-
-                       frc[frclen].font = XftFontOpenPattern(xw.dpy,
-                                       fontpattern);
-                       frc[frclen].flags = frcflags;
-                       frc[frclen].unicodep = unicodep;
-
-                       i = frclen;
-                       frclen++;
-
-                       FcPatternDestroy(fcpattern);
-                       FcCharSetDestroy(fccharset);
-               }
-
-               XftDrawStringUtf8(xw.draw, fg, frc[i].font,
-                               xp, winy + frc[i].font->ascent,
-                               (FcChar8 *)u8c, u8cblen);
-
-               xp += xw.cw * wcwidth(unicodep);
-       }
-
-       /*
-        * This is how the loop above actually should be. Why does the
-        * application have to care about font details?
-        *
-        * I have to repeat: Xft and Fontconfig are design failures.
-        */
-       /*
-       XftDrawStringUtf8(xw.draw, fg, font->set, winx,
-                       winy + font->ascent, (FcChar8 *)s, bytelen);
-       */
+       /* Render the glyphs. */
+       XftDrawGlyphFontSpec(xw.draw, fg, specs, len);
 
+       /* Render underline and strikethrough. */
        if(base.mode & ATTR_UNDERLINE) {
-               XftDrawRect(xw.draw, fg, winx, winy + font->ascent + 1,
+               XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1,
                                width, 1);
        }
 
        if(base.mode & ATTR_STRUCK) {
-               XftDrawRect(xw.draw, fg, winx, winy + 2 * font->ascent / 3,
+               XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3,
                                width, 1);
        }
 
@@ -3540,11 +3547,10 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
 
 void
 xdrawglyph(Glyph g, int x, int y) {
-       static char buf[UTF_SIZ];
-       size_t len = utf8encode(g.u, buf);
-       int width = g.mode & ATTR_WIDE ? 2 : 1;
-
-       xdraws(buf, g, x, y, width, len);
+       int numspecs;
+       XftGlyphFontSpec spec;
+       numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
+       xdrawglyphfontspecs(&spec, g, numspecs, x, y);
 }
 
 void
@@ -3658,9 +3664,9 @@ draw(void) {
 
 void
 drawregion(int x1, int y1, int x2, int y2) {
-       int ic, ib, x, y, ox;
+       int i, x, y, ox, numspecs;
        Glyph base, new;
-       char buf[DRAW_BUF_SIZ];
+       XftGlyphFontSpec* specs;
        bool ena_sel = sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN);
 
        if(!(xw.state & WIN_VISIBLE))
@@ -3672,29 +3678,31 @@ drawregion(int x1, int y1, int x2, int y2) {
 
                xtermclear(0, y, term.col, y);
                term.dirty[y] = 0;
-               base = term.line[y][0];
-               ic = ib = ox = 0;
-               for(x = x1; x < x2; x++) {
+
+               specs = term.specbuf;
+               numspecs = xmakeglyphfontspecs(specs, &term.line[y][x1], x2 - x1, x1, y);
+
+               i = ox = 0;
+               for(x = x1; x < x2 && i < numspecs; x++) {
                        new = term.line[y][x];
                        if(new.mode == ATTR_WDUMMY)
                                continue;
                        if(ena_sel && selected(x, y))
                                new.mode ^= ATTR_REVERSE;
-                       if(ib > 0 && (ATTRCMP(base, new)
-                                       || ib >= DRAW_BUF_SIZ-UTF_SIZ)) {
-                               xdraws(buf, base, ox, y, ic, ib);
-                               ic = ib = 0;
+                       if(i > 0 && ATTRCMP(base, new)) {
+                               xdrawglyphfontspecs(specs, base, i, ox, y);
+                               specs += i;
+                               numspecs -= i;
+                               i = 0;
                        }
-                       if(ib == 0) {
+                       if(i == 0) {
                                ox = x;
                                base = new;
                        }
-
-                       ib += utf8encode(new.u, buf+ib);
-                       ic += (new.mode & ATTR_WIDE)? 2 : 1;
+                       i++;
                }
-               if(ib > 0)
-                       xdraws(buf, base, ox, y, ic, ib);
+               if(i > 0)
+                       xdrawglyphfontspecs(specs, base, i, ox, y);
        }
        xdrawcursor();
 }
@@ -4019,7 +4027,7 @@ main(int argc, char *argv[]) {
                opt_class = EARGF(usage());
                break;
        case 'e':
-               if(argc > 1)
+               if(argc > 0)
                        --argc, ++argv;
                goto run;
        case 'f':