diff options
| -rw-r--r-- | st.c | 383 | 
1 files changed, 187 insertions, 196 deletions
@@ -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) @@ -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 { @@ -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); @@ -2819,6 +2821,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 +3262,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 +3436,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 +3472,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 +3510,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 +3530,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 +3647,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 +3661,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][0], 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();  }  |