diff options
| author | Tim Keller <tjkeller.xyz> | 2025-01-08 21:41:30 -0600 | 
|---|---|---|
| committer | Tim Keller <tjkeller.xyz> | 2025-01-08 21:41:30 -0600 | 
| commit | d8e0ba487eca7164aed1d1fde1258558a6e44368 (patch) | |
| tree | f9bc22b98f3450252a52c79a6bdeca2fe5b814b8 | |
| parent | 6009e6e25bdff9548f085e9ae562b1ca305d3a0b (diff) | |
| download | st-tj-0.9.2.0.tar.xz st-tj-0.9.2.0.zip  | |
add patches and configtj-0.9.2.00.9.2.0
| -rw-r--r-- | .gitignore | 3 | ||||
| -rw-r--r-- | Makefile | 6 | ||||
| -rw-r--r-- | PATCHES | 8 | ||||
| -rw-r--r-- | boxdraw.c | 194 | ||||
| -rw-r--r-- | boxdraw_data.h | 214 | ||||
| -rw-r--r-- | config.def.h | 97 | ||||
| -rw-r--r-- | config.mk | 1 | ||||
| -rw-r--r-- | st.c | 1176 | ||||
| -rw-r--r-- | st.desktop | 12 | ||||
| -rw-r--r-- | st.h | 40 | ||||
| -rw-r--r-- | st.info | 5 | ||||
| -rw-r--r-- | win.h | 4 | ||||
| -rw-r--r-- | x.c | 504 | 
13 files changed, 1889 insertions, 375 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..51447da --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +config.h +st @@ -4,7 +4,7 @@  include config.mk -SRC = st.c x.c +SRC = st.c x.c boxdraw.c  OBJ = $(SRC:.c=.o)  all: st @@ -17,6 +17,7 @@ config.h:  st.o: config.h st.h win.h  x.o: arg.h config.h st.h win.h +boxdraw.o: config.h st.h boxdraw_data.h  $(OBJ): config.h config.mk @@ -43,9 +44,12 @@ install: st  	chmod 644 $(DESTDIR)$(MANPREFIX)/man1/st.1  	tic -sx st.info  	@echo Please see the README file regarding the terminfo entry of st. +	mkdir -p $(DESTDIR)$(APPPREFIX) +	cp -f st.desktop $(DESTDIR)$(APPPREFIX)  uninstall:  	rm -f $(DESTDIR)$(PREFIX)/bin/st +	rm -f $(DESTDIR)$(APPPREFIX)/st.desktop  	rm -f $(DESTDIR)$(MANPREFIX)/man1/st.1  .PHONY: all clean dist install uninstall @@ -0,0 +1,8 @@ +st-anygeometry-0.8.1.diff +st-boxdraw_v2-0.8.5.diff +st-clipboard-20180309-c5ba9c0.diff +st-csi_22_23-0.8.5.diff +st-desktopentry-0.8.5.diff +st-scrollback-0.9.2.diff +st-scrollback-reflow-0.9.2.diff +st-undercurl-0.9-20240103.diff diff --git a/boxdraw.c b/boxdraw.c new file mode 100644 index 0000000..28a92d0 --- /dev/null +++ b/boxdraw.c @@ -0,0 +1,194 @@ +/* + * Copyright 2018 Avi Halachmi (:avih) avihpit@yahoo.com https://github.com/avih + * MIT/X Consortium License + */ + +#include <X11/Xft/Xft.h> +#include "st.h" +#include "boxdraw_data.h" + +/* Rounded non-negative integers division of n / d  */ +#define DIV(n, d) (((n) + (d) / 2) / (d)) + +static Display *xdpy; +static Colormap xcmap; +static XftDraw *xd; +static Visual *xvis; + +static void drawbox(int, int, int, int, XftColor *, XftColor *, ushort); +static void drawboxlines(int, int, int, int, XftColor *, ushort); + +/* public API */ + +void +boxdraw_xinit(Display *dpy, Colormap cmap, XftDraw *draw, Visual *vis) +{ +	xdpy = dpy; xcmap = cmap; xd = draw, xvis = vis; +} + +int +isboxdraw(Rune u) +{ +	Rune block = u & ~0xff; +	return (boxdraw && block == 0x2500 && boxdata[(uint8_t)u]) || +	       (boxdraw_braille && block == 0x2800); +} + +/* the "index" is actually the entire shape data encoded as ushort */ +ushort +boxdrawindex(const Glyph *g) +{ +	if (boxdraw_braille && (g->u & ~0xff) == 0x2800) +		return BRL | (uint8_t)g->u; +	if (boxdraw_bold && (g->mode & ATTR_BOLD)) +		return BDB | boxdata[(uint8_t)g->u]; +	return boxdata[(uint8_t)g->u]; +} + +void +drawboxes(int x, int y, int cw, int ch, XftColor *fg, XftColor *bg, +          const XftGlyphFontSpec *specs, int len) +{ +	for ( ; len-- > 0; x += cw, specs++) +		drawbox(x, y, cw, ch, fg, bg, (ushort)specs->glyph); +} + +/* implementation */ + +void +drawbox(int x, int y, int w, int h, XftColor *fg, XftColor *bg, ushort bd) +{ +	ushort cat = bd & ~(BDB | 0xff);  /* mask out bold and data */ +	if (bd & (BDL | BDA)) { +		/* lines (light/double/heavy/arcs) */ +		drawboxlines(x, y, w, h, fg, bd); + +	} else if (cat == BBD) { +		/* lower (8-X)/8 block */ +		int d = DIV((uint8_t)bd * h, 8); +		XftDrawRect(xd, fg, x, y + d, w, h - d); + +	} else if (cat == BBU) { +		/* upper X/8 block */ +		XftDrawRect(xd, fg, x, y, w, DIV((uint8_t)bd * h, 8)); + +	} else if (cat == BBL) { +		/* left X/8 block */ +		XftDrawRect(xd, fg, x, y, DIV((uint8_t)bd * w, 8), h); + +	} else if (cat == BBR) { +		/* right (8-X)/8 block */ +		int d = DIV((uint8_t)bd * w, 8); +		XftDrawRect(xd, fg, x + d, y, w - d, h); + +	} else if (cat == BBQ) { +		/* Quadrants */ +		int w2 = DIV(w, 2), h2 = DIV(h, 2); +		if (bd & TL) +			XftDrawRect(xd, fg, x, y, w2, h2); +		if (bd & TR) +			XftDrawRect(xd, fg, x + w2, y, w - w2, h2); +		if (bd & BL) +			XftDrawRect(xd, fg, x, y + h2, w2, h - h2); +		if (bd & BR) +			XftDrawRect(xd, fg, x + w2, y + h2, w - w2, h - h2); + +	} else if (bd & BBS) { +		/* Shades - data is 1/2/3 for 25%/50%/75% alpha, respectively */ +		int d = (uint8_t)bd; +		XftColor xfc; +		XRenderColor xrc = { .alpha = 0xffff }; + +		xrc.red = DIV(fg->color.red * d + bg->color.red * (4 - d), 4); +		xrc.green = DIV(fg->color.green * d + bg->color.green * (4 - d), 4); +		xrc.blue = DIV(fg->color.blue * d + bg->color.blue * (4 - d), 4); + +		XftColorAllocValue(xdpy, xvis, xcmap, &xrc, &xfc); +		XftDrawRect(xd, &xfc, x, y, w, h); +		XftColorFree(xdpy, xvis, xcmap, &xfc); + +	} else if (cat == BRL) { +		/* braille, each data bit corresponds to one dot at 2x4 grid */ +		int w1 = DIV(w, 2); +		int h1 = DIV(h, 4), h2 = DIV(h, 2), h3 = DIV(3 * h, 4); + +		if (bd & 1)   XftDrawRect(xd, fg, x, y, w1, h1); +		if (bd & 2)   XftDrawRect(xd, fg, x, y + h1, w1, h2 - h1); +		if (bd & 4)   XftDrawRect(xd, fg, x, y + h2, w1, h3 - h2); +		if (bd & 8)   XftDrawRect(xd, fg, x + w1, y, w - w1, h1); +		if (bd & 16)  XftDrawRect(xd, fg, x + w1, y + h1, w - w1, h2 - h1); +		if (bd & 32)  XftDrawRect(xd, fg, x + w1, y + h2, w - w1, h3 - h2); +		if (bd & 64)  XftDrawRect(xd, fg, x, y + h3, w1, h - h3); +		if (bd & 128) XftDrawRect(xd, fg, x + w1, y + h3, w - w1, h - h3); + +	} +} + +void +drawboxlines(int x, int y, int w, int h, XftColor *fg, ushort bd) +{ +	/* s: stem thickness. width/8 roughly matches underscore thickness. */ +	/* We draw bold as 1.5 * normal-stem and at least 1px thicker.      */ +	/* doubles draw at least 3px, even when w or h < 3. bold needs 6px. */ +	int mwh = MIN(w, h); +	int base_s = MAX(1, DIV(mwh, 8)); +	int bold = (bd & BDB) && mwh >= 6;  /* possibly ignore boldness */ +	int s = bold ? MAX(base_s + 1, DIV(3 * base_s, 2)) : base_s; +	int w2 = DIV(w - s, 2), h2 = DIV(h - s, 2); +	/* the s-by-s square (x + w2, y + h2, s, s) is the center texel.    */ +	/* The base length (per direction till edge) includes this square.  */ + +	int light = bd & (LL | LU | LR | LD); +	int double_ = bd & (DL | DU | DR | DD); + +	if (light) { +		/* d: additional (negative) length to not-draw the center   */ +		/* texel - at arcs and avoid drawing inside (some) doubles  */ +		int arc = bd & BDA; +		int multi_light = light & (light - 1); +		int multi_double = double_ & (double_ - 1); +		/* light crosses double only at DH+LV, DV+LH (ref. shapes)  */ +		int d = arc || (multi_double && !multi_light) ? -s : 0; + +		if (bd & LL) +			XftDrawRect(xd, fg, x, y + h2, w2 + s + d, s); +		if (bd & LU) +			XftDrawRect(xd, fg, x + w2, y, s, h2 + s + d); +		if (bd & LR) +			XftDrawRect(xd, fg, x + w2 - d, y + h2, w - w2 + d, s); +		if (bd & LD) +			XftDrawRect(xd, fg, x + w2, y + h2 - d, s, h - h2 + d); +	} + +	/* double lines - also align with light to form heavy when combined */ +	if (double_) { +		/* +		* going clockwise, for each double-ray: p is additional length +		* to the single-ray nearer to the previous direction, and n to +		* the next. p and n adjust from the base length to lengths +		* which consider other doubles - shorter to avoid intersections +		* (p, n), or longer to draw the far-corner texel (n). +		*/ +		int dl = bd & DL, du = bd & DU, dr = bd & DR, dd = bd & DD; +		if (dl) { +			int p = dd ? -s : 0, n = du ? -s : dd ? s : 0; +			XftDrawRect(xd, fg, x, y + h2 + s, w2 + s + p, s); +			XftDrawRect(xd, fg, x, y + h2 - s, w2 + s + n, s); +		} +		if (du) { +			int p = dl ? -s : 0, n = dr ? -s : dl ? s : 0; +			XftDrawRect(xd, fg, x + w2 - s, y, s, h2 + s + p); +			XftDrawRect(xd, fg, x + w2 + s, y, s, h2 + s + n); +		} +		if (dr) { +			int p = du ? -s : 0, n = dd ? -s : du ? s : 0; +			XftDrawRect(xd, fg, x + w2 - p, y + h2 - s, w - w2 + p, s); +			XftDrawRect(xd, fg, x + w2 - n, y + h2 + s, w - w2 + n, s); +		} +		if (dd) { +			int p = dr ? -s : 0, n = dl ? -s : dr ? s : 0; +			XftDrawRect(xd, fg, x + w2 + s, y + h2 - p, s, h - h2 + p); +			XftDrawRect(xd, fg, x + w2 - s, y + h2 - n, s, h - h2 + n); +		} +	} +} diff --git a/boxdraw_data.h b/boxdraw_data.h new file mode 100644 index 0000000..7890500 --- /dev/null +++ b/boxdraw_data.h @@ -0,0 +1,214 @@ +/* + * Copyright 2018 Avi Halachmi (:avih) avihpit@yahoo.com https://github.com/avih + * MIT/X Consortium License + */ + +/* + * U+25XX codepoints data + * + * References: + *   http://www.unicode.org/charts/PDF/U2500.pdf + *   http://www.unicode.org/charts/PDF/U2580.pdf + * + * Test page: + *   https://github.com/GNOME/vte/blob/master/doc/boxes.txt + */ + +/* Each shape is encoded as 16-bits. Higher bits are category, lower are data */ +/* Categories (mutually exclusive except BDB): */ +/* For convenience, BDL/BDA/BBS/BDB are 1 bit each, the rest are enums */ +#define BDL (1<<8)   /* Box Draw Lines (light/double/heavy) */ +#define BDA (1<<9)   /* Box Draw Arc (light) */ + +#define BBD (1<<10)  /* Box Block Down (lower) X/8 */ +#define BBL (2<<10)  /* Box Block Left X/8 */ +#define BBU (3<<10)  /* Box Block Upper X/8 */ +#define BBR (4<<10)  /* Box Block Right X/8 */ +#define BBQ (5<<10)  /* Box Block Quadrants */ +#define BRL (6<<10)  /* Box Braille (data is lower byte of U28XX) */ + +#define BBS (1<<14)  /* Box Block Shades */ +#define BDB (1<<15)  /* Box Draw is Bold */ + +/* (BDL/BDA) Light/Double/Heavy x Left/Up/Right/Down/Horizontal/Vertical      */ +/* Heavy is light+double (literally drawing light+double align to form heavy) */ +#define LL (1<<0) +#define LU (1<<1) +#define LR (1<<2) +#define LD (1<<3) +#define LH (LL+LR) +#define LV (LU+LD) + +#define DL (1<<4) +#define DU (1<<5) +#define DR (1<<6) +#define DD (1<<7) +#define DH (DL+DR) +#define DV (DU+DD) + +#define HL (LL+DL) +#define HU (LU+DU) +#define HR (LR+DR) +#define HD (LD+DD) +#define HH (HL+HR) +#define HV (HU+HD) + +/* (BBQ) Quadrants Top/Bottom x Left/Right */ +#define TL (1<<0) +#define TR (1<<1) +#define BL (1<<2) +#define BR (1<<3) + +/* Data for U+2500 - U+259F except dashes/diagonals */ +static const unsigned short boxdata[256] = { +	/* light lines */ +	[0x00] = BDL + LH,       /* light horizontal */ +	[0x02] = BDL + LV,       /* light vertical */ +	[0x0c] = BDL + LD + LR,  /* light down and right */ +	[0x10] = BDL + LD + LL,  /* light down and left */ +	[0x14] = BDL + LU + LR,  /* light up and right */ +	[0x18] = BDL + LU + LL,  /* light up and left */ +	[0x1c] = BDL + LV + LR,  /* light vertical and right */ +	[0x24] = BDL + LV + LL,  /* light vertical and left */ +	[0x2c] = BDL + LH + LD,  /* light horizontal and down */ +	[0x34] = BDL + LH + LU,  /* light horizontal and up */ +	[0x3c] = BDL + LV + LH,  /* light vertical and horizontal */ +	[0x74] = BDL + LL,       /* light left */ +	[0x75] = BDL + LU,       /* light up */ +	[0x76] = BDL + LR,       /* light right */ +	[0x77] = BDL + LD,       /* light down */ + +	/* heavy [+light] lines */ +	[0x01] = BDL + HH, +	[0x03] = BDL + HV, +	[0x0d] = BDL + HR + LD, +	[0x0e] = BDL + HD + LR, +	[0x0f] = BDL + HD + HR, +	[0x11] = BDL + HL + LD, +	[0x12] = BDL + HD + LL, +	[0x13] = BDL + HD + HL, +	[0x15] = BDL + HR + LU, +	[0x16] = BDL + HU + LR, +	[0x17] = BDL + HU + HR, +	[0x19] = BDL + HL + LU, +	[0x1a] = BDL + HU + LL, +	[0x1b] = BDL + HU + HL, +	[0x1d] = BDL + HR + LV, +	[0x1e] = BDL + HU + LD + LR, +	[0x1f] = BDL + HD + LR + LU, +	[0x20] = BDL + HV + LR, +	[0x21] = BDL + HU + HR + LD, +	[0x22] = BDL + HD + HR + LU, +	[0x23] = BDL + HV + HR, +	[0x25] = BDL + HL + LV, +	[0x26] = BDL + HU + LD + LL, +	[0x27] = BDL + HD + LU + LL, +	[0x28] = BDL + HV + LL, +	[0x29] = BDL + HU + HL + LD, +	[0x2a] = BDL + HD + HL + LU, +	[0x2b] = BDL + HV + HL, +	[0x2d] = BDL + HL + LD + LR, +	[0x2e] = BDL + HR + LL + LD, +	[0x2f] = BDL + HH + LD, +	[0x30] = BDL + HD + LH, +	[0x31] = BDL + HD + HL + LR, +	[0x32] = BDL + HR + HD + LL, +	[0x33] = BDL + HH + HD, +	[0x35] = BDL + HL + LU + LR, +	[0x36] = BDL + HR + LU + LL, +	[0x37] = BDL + HH + LU, +	[0x38] = BDL + HU + LH, +	[0x39] = BDL + HU + HL + LR, +	[0x3a] = BDL + HU + HR + LL, +	[0x3b] = BDL + HH + HU, +	[0x3d] = BDL + HL + LV + LR, +	[0x3e] = BDL + HR + LV + LL, +	[0x3f] = BDL + HH + LV, +	[0x40] = BDL + HU + LH + LD, +	[0x41] = BDL + HD + LH + LU, +	[0x42] = BDL + HV + LH, +	[0x43] = BDL + HU + HL + LD + LR, +	[0x44] = BDL + HU + HR + LD + LL, +	[0x45] = BDL + HD + HL + LU + LR, +	[0x46] = BDL + HD + HR + LU + LL, +	[0x47] = BDL + HH + HU + LD, +	[0x48] = BDL + HH + HD + LU, +	[0x49] = BDL + HV + HL + LR, +	[0x4a] = BDL + HV + HR + LL, +	[0x4b] = BDL + HV + HH, +	[0x78] = BDL + HL, +	[0x79] = BDL + HU, +	[0x7a] = BDL + HR, +	[0x7b] = BDL + HD, +	[0x7c] = BDL + HR + LL, +	[0x7d] = BDL + HD + LU, +	[0x7e] = BDL + HL + LR, +	[0x7f] = BDL + HU + LD, + +	/* double [+light] lines */ +	[0x50] = BDL + DH, +	[0x51] = BDL + DV, +	[0x52] = BDL + DR + LD, +	[0x53] = BDL + DD + LR, +	[0x54] = BDL + DR + DD, +	[0x55] = BDL + DL + LD, +	[0x56] = BDL + DD + LL, +	[0x57] = BDL + DL + DD, +	[0x58] = BDL + DR + LU, +	[0x59] = BDL + DU + LR, +	[0x5a] = BDL + DU + DR, +	[0x5b] = BDL + DL + LU, +	[0x5c] = BDL + DU + LL, +	[0x5d] = BDL + DL + DU, +	[0x5e] = BDL + DR + LV, +	[0x5f] = BDL + DV + LR, +	[0x60] = BDL + DV + DR, +	[0x61] = BDL + DL + LV, +	[0x62] = BDL + DV + LL, +	[0x63] = BDL + DV + DL, +	[0x64] = BDL + DH + LD, +	[0x65] = BDL + DD + LH, +	[0x66] = BDL + DD + DH, +	[0x67] = BDL + DH + LU, +	[0x68] = BDL + DU + LH, +	[0x69] = BDL + DH + DU, +	[0x6a] = BDL + DH + LV, +	[0x6b] = BDL + DV + LH, +	[0x6c] = BDL + DH + DV, + +	/* (light) arcs */ +	[0x6d] = BDA + LD + LR, +	[0x6e] = BDA + LD + LL, +	[0x6f] = BDA + LU + LL, +	[0x70] = BDA + LU + LR, + +	/* Lower (Down) X/8 block (data is 8 - X) */ +	[0x81] = BBD + 7, [0x82] = BBD + 6, [0x83] = BBD + 5, [0x84] = BBD + 4, +	[0x85] = BBD + 3, [0x86] = BBD + 2, [0x87] = BBD + 1, [0x88] = BBD + 0, + +	/* Left X/8 block (data is X) */ +	[0x89] = BBL + 7, [0x8a] = BBL + 6, [0x8b] = BBL + 5, [0x8c] = BBL + 4, +	[0x8d] = BBL + 3, [0x8e] = BBL + 2, [0x8f] = BBL + 1, + +	/* upper 1/2 (4/8), 1/8 block (X), right 1/2, 1/8 block (8-X) */ +	[0x80] = BBU + 4, [0x94] = BBU + 1, +	[0x90] = BBR + 4, [0x95] = BBR + 7, + +	/* Quadrants */ +	[0x96] = BBQ + BL, +	[0x97] = BBQ + BR, +	[0x98] = BBQ + TL, +	[0x99] = BBQ + TL + BL + BR, +	[0x9a] = BBQ + TL + BR, +	[0x9b] = BBQ + TL + TR + BL, +	[0x9c] = BBQ + TL + TR + BR, +	[0x9d] = BBQ + TR, +	[0x9e] = BBQ + BL + TR, +	[0x9f] = BBQ + BL + TR + BR, + +	/* Shades, data is an alpha value in 25% units (1/4, 1/2, 3/4) */ +	[0x91] = BBS + 1, [0x92] = BBS + 2, [0x93] = BBS + 3, + +	/* U+2504 - U+250B, U+254C - U+254F: unsupported (dashes) */ +	/* U+2571 - U+2573: unsupported (diagonals) */ +}; diff --git a/config.def.h b/config.def.h index 2cd740a..c535ee0 100644 --- a/config.def.h +++ b/config.def.h @@ -5,7 +5,7 @@   *   * font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html   */ -static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohint=true"; +static char *font = "JetBrainsMonoNL Nerd Font Mono:pixelsize=12:antialias=true:autohint=true";  static int borderpx = 2;  /* @@ -68,6 +68,18 @@ static unsigned int blinktimeout = 800;  static unsigned int cursorthickness = 2;  /* + * 1: render most of the lines/blocks characters without using the font for + *    perfect alignment between cells (U2500 - U259F except dashes/diagonals). + *    Bold affects lines thickness if boxdraw_bold is not 0. Italic is ignored. + * 0: disable (render all U25XX glyphs normally from the font). + */ +const int boxdraw = 1; +const int boxdraw_bold = 1; + +/* braille (U28XX):  1: render as adjacent "pixels",  0: use font */ +const int boxdraw_braille = 1; + +/*   * bell volume. It must be a value between -100 and 100. Use 0 for disabling   * it   */ @@ -91,37 +103,35 @@ char *termname = "st-256color";   *   *	stty tabs   */ -unsigned int tabspaces = 8; +unsigned int tabspaces = 8; // TODO change to 4  /* Terminal colors (16 first used in escape sequence) */  static const char *colorname[] = {  	/* 8 normal colors */ -	"black", -	"red3", -	"green3", -	"yellow3", -	"blue2", -	"magenta3", -	"cyan3", -	"gray90", +	"#101010", +	"#ed1207", +	"#2bb500", +	"#fc9700", +	"#1578c1", +	"#b14ff7", +	"#00c4a3", +	"white",  	/* 8 bright colors */  	"gray50", -	"red", -	"green", -	"yellow", -	"#5c5cff", -	"magenta", -	"cyan", +	"#ed1207", +	"#2bb500", +	"#fc9700", +	"#1578c1", +	"#b14ff7", +	"#00c4a3",  	"white",  	[255] = 0, -	/* more colors can be added after 255 to use with DefaultXX */ +	/* more colors can be added after 255 to use with defaultxx */  	"#cccccc",  	"#555555", -	"gray90", /* default foreground colour */ -	"black", /* default background colour */  }; @@ -129,8 +139,8 @@ static const char *colorname[] = {   * Default colors (colorname index)   * foreground, background, cursor, reverse cursor   */ -unsigned int defaultfg = 258; -unsigned int defaultbg = 259; +unsigned int defaultfg = 7; +unsigned int defaultbg = 0;  unsigned int defaultcs = 256;  static unsigned int defaultrcs = 257; @@ -144,6 +154,12 @@ static unsigned int defaultrcs = 257;  static unsigned int cursorshape = 2;  /* + * Whether to use pixel geometry or cell geometry + */ + +static Geometry geometry = CellGeometry; + +/*   * Default columns and rows numbers   */ @@ -151,6 +167,13 @@ static unsigned int cols = 80;  static unsigned int rows = 24;  /* + * Default width and height (including borders!) + */ + +static unsigned int width = 564; +static unsigned int height = 364; + +/*   * Default colour and shape of the mouse cursor   */  static unsigned int mouseshape = XC_xterm; @@ -176,6 +199,10 @@ static uint forcemousemod = ShiftMask;   */  static MouseShortcut mshortcuts[] = {  	/* mask                 button   function        argument       release */ +	{ ControlMask,          Button4, zoom,           {.f = +1},     0 }, +	{ ControlMask,          Button5, zoom,           {.f = -1},     0 }, +	{ XK_ANY_MOD,           Button4, kscrollup,      {.i = 3},      0 }, +	{ XK_ANY_MOD,           Button5, kscrolldown,    {.i = 3},      0 },  	{ XK_ANY_MOD,           Button2, selpaste,       {.i = 0},      1 },  	{ ShiftMask,            Button4, ttysend,        {.s = "\033[5;2~"} },  	{ XK_ANY_MOD,           Button4, ttysend,        {.s = "\031"} }, @@ -195,12 +222,16 @@ static Shortcut shortcuts[] = {  	{ XK_ANY_MOD,           XK_Print,       printsel,       {.i =  0} },  	{ TERMMOD,              XK_Prior,       zoom,           {.f = +1} },  	{ TERMMOD,              XK_Next,        zoom,           {.f = -1} }, +	{ ControlMask,          XK_equal,       zoom,           {.f = +1} }, +	{ ControlMask,          XK_minus,       zoom,           {.f = -1} },  	{ TERMMOD,              XK_Home,        zoomreset,      {.f =  0} },  	{ TERMMOD,              XK_C,           clipcopy,       {.i =  0} },  	{ TERMMOD,              XK_V,           clippaste,      {.i =  0} },  	{ TERMMOD,              XK_Y,           selpaste,       {.i =  0} },  	{ ShiftMask,            XK_Insert,      selpaste,       {.i =  0} },  	{ TERMMOD,              XK_Num_Lock,    numlock,        {.i =  0} }, +	{ ShiftMask,            XK_Page_Up,     kscrollup,      {.i = -1} }, +    { ShiftMask,            XK_Page_Down,   kscrolldown,    {.i = -1} },  };  /* @@ -472,3 +503,27 @@ static char ascii_printable[] =  	" !\"#$%&'()*+,-./0123456789:;<=>?"  	"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"  	"`abcdefghijklmnopqrstuvwxyz{|}~"; + +/** + * Undercurl style. Set UNDERCURL_STYLE to one of the available styles. + * + * Curly: Dunno how to draw it *shrug* + *  _   _   _   _ + * ( ) ( ) ( ) ( ) + *	 (_) (_) (_) (_) + * + * Spiky: + * /\  /\   /\	/\ + *   \/  \/	  \/ + * + * Capped: + *	_     _     _ + * / \   / \   / \ + *    \_/   \_/ + */ +// Available styles +#define UNDERCURL_CURLY 0 +#define UNDERCURL_SPIKY 1 +#define UNDERCURL_CAPPED 2 +// Active style +#define UNDERCURL_STYLE UNDERCURL_SPIKY @@ -5,6 +5,7 @@ VERSION = 0.9.2  # paths  PREFIX = /usr/local +APPPREFIX = $(PREFIX)/share/applications  MANPREFIX = $(PREFIX)/share/man  X11INC = /usr/X11R6/include @@ -33,8 +33,11 @@  #define UTF_SIZ       4  #define ESC_BUF_SIZ   (128*UTF_SIZ)  #define ESC_ARG_SIZ   16 +#define CAR_PER_ARG   4  #define STR_BUF_SIZ   ESC_BUF_SIZ  #define STR_ARG_SIZ   ESC_ARG_SIZ +#define HISTSIZE      2000 +#define RESIZEBUFFER  1000  /* macros */  #define IS_SET(flag)		((term.mode & (flag)) != 0) @@ -42,6 +45,21 @@  #define ISCONTROLC1(c)		(BETWEEN(c, 0x80, 0x9f))  #define ISCONTROL(c)		(ISCONTROLC0(c) || ISCONTROLC1(c))  #define ISDELIM(u)		(u && wcschr(worddelimiters, u)) +#define TLINE(y) ( \ +	(y) < term.scr ? term.hist[(term.histi + (y) - term.scr + 1 + HISTSIZE) % HISTSIZE] \ +	               : term.line[(y) - term.scr] \ +) + +#define TLINEABS(y) ( \ +	(y) < 0 ? term.hist[(term.histi + (y) + 1 + HISTSIZE) % HISTSIZE] : term.line[(y)] \ +) + +#define UPDATEWRAPNEXT(alt, col) do { \ +	if ((term.c.state & CURSOR_WRAPNEXT) && term.c.x + term.wrapcwidth[alt] < col) { \ +		term.c.x += term.wrapcwidth[alt]; \ +		term.c.state &= ~CURSOR_WRAPNEXT; \ +	} \ +} while (0);  enum term_mode {  	MODE_WRAP        = 1 << 0, @@ -53,6 +71,12 @@ enum term_mode {  	MODE_UTF8        = 1 << 6,  }; +enum scroll_mode { +	SCROLL_RESIZE = -1, +	SCROLL_NOSAVEHIST = 0, +	SCROLL_SAVEHIST = 1 +}; +  enum cursor_movement {  	CURSOR_SAVE,  	CURSOR_LOAD @@ -114,7 +138,11 @@ typedef struct {  	int row;      /* nb row */  	int col;      /* nb col */  	Line *line;   /* screen */ -	Line *alt;    /* alternate screen */ +	Line hist[HISTSIZE]; /* history buffer */ +	int histi;           /* history index */ +	int histf;           /* nb history available */ +	int scr;             /* scroll back */ +	int wrapcwidth[2];   /* used in updating WRAPNEXT when resizing */  	int *dirty;   /* dirtyness of lines */  	TCursor c;    /* cursor */  	int ocx;      /* old cursor col */ @@ -139,6 +167,7 @@ typedef struct {  	int arg[ESC_ARG_SIZ];  	int narg;              /* nb of args */  	char mode[2]; +	int carg[ESC_ARG_SIZ][CAR_PER_ARG]; /* colon args */  } CSIEscape;  /* STR Escape sequence structs */ @@ -159,6 +188,7 @@ static void ttywriteraw(const char *, size_t);  static void csidump(void);  static void csihandle(void); +static void readcolonargs(char **, int, int[][CAR_PER_ARG]);  static void csiparse(void);  static void csireset(void);  static void osc_color_response(int, int, int); @@ -172,26 +202,37 @@ static void tprinter(char *, size_t);  static void tdumpsel(void);  static void tdumpline(int);  static void tdump(void); -static void tclearregion(int, int, int, int); +static void tclearregion(int, int, int, int, int);  static void tcursor(int); +static void tclearglyph(Glyph *, int); +static void tresetcursor(void);  static void tdeletechar(int);  static void tdeleteline(int);  static void tinsertblank(int);  static void tinsertblankline(int); -static int tlinelen(int); +static int tlinelen(Line len); +static int tiswrapped(Line line); +static char *tgetglyphs(char *, const Glyph *, const Glyph *); +static size_t tgetline(char *, const Glyph *);  static void tmoveto(int, int);  static void tmoveato(int, int);  static void tnewline(int);  static void tputtab(int);  static void tputc(Rune);  static void treset(void); -static void tscrollup(int, int); +static void tscrollup(int, int, int, int);  static void tscrolldown(int, int); +static void treflow(int, int); +static void rscrolldown(int); +static void tresizedef(int, int); +static void tresizealt(int, int);  static void tsetattr(const int *, int);  static void tsetchar(Rune, const Glyph *, int, int);  static void tsetdirt(int, int);  static void tsetscroll(int, int);  static void tswapscreen(void); +static void tloaddefscreen(int, int); +static void tloadaltscreen(int, int);  static void tsetmode(int, int, const int *, int);  static int twrite(const char *, int, int);  static void tfulldirt(void); @@ -205,7 +246,10 @@ static void tstrsequence(uchar);  static void drawregion(int, int, int, int);  static void selnormalize(void); -static void selscroll(int, int); +static void selscroll(int, int, int); +static void selmove(int); +static void selremove(void); +static int regionselected(int, int, int, int);  static void selsnap(int *, int *, int);  static size_t utf8decode(const char *, Rune *, size_t); @@ -405,17 +449,46 @@ selinit(void)  }  int -tlinelen(int y) +tlinelen(Line line)  { -	int i = term.col; +	int i = term.col - 1; -	if (term.line[y][i - 1].mode & ATTR_WRAP) -		return i; +	for (; i >= 0 && !(line[i].mode & (ATTR_SET | ATTR_WRAP)); i--); +	return i + 1; +} -	while (i > 0 && term.line[y][i - 1].u == ' ') -		--i; +int +tiswrapped(Line line) +{ +	int len = tlinelen(line); -	return i; +	return len > 0 && (line[len - 1].mode & ATTR_WRAP); +} + +char * +tgetglyphs(char *buf, const Glyph *gp, const Glyph *lgp) +{ +	while (gp <= lgp) +		if (gp->mode & ATTR_WDUMMY) { +			gp++; +		} else { +			buf += utf8encode((gp++)->u, buf); +		} +	return buf; +} + +size_t +tgetline(char *buf, const Glyph *fgp) +{ +	char *ptr; +	const Glyph *lgp = &fgp[term.col - 1]; + +	while (lgp > fgp && !(lgp->mode & (ATTR_SET | ATTR_WRAP))) +		lgp--; +	ptr = tgetglyphs(buf, fgp, lgp); +	if (!(lgp->mode & ATTR_WRAP)) +		*(ptr++) = '\n'; +	return ptr - buf;  }  void @@ -455,10 +528,11 @@ selextend(int col, int row, int type, int done)  	sel.oe.x = col;  	sel.oe.y = row; -	selnormalize();  	sel.type = type; +	selnormalize(); -	if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY) +	if (oldey != sel.oe.y || oldex != sel.oe.x || +	    oldtype != sel.type || sel.mode == SEL_EMPTY)  		tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey));  	sel.mode = done ? SEL_IDLE : SEL_READY; @@ -467,61 +541,69 @@ selextend(int col, int row, int type, int done)  void  selnormalize(void)  { -	int i; +    int i; -	if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) { -		sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x; -		sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x; -	} else { -		sel.nb.x = MIN(sel.ob.x, sel.oe.x); -		sel.ne.x = MAX(sel.ob.x, sel.oe.x); -	} -	sel.nb.y = MIN(sel.ob.y, sel.oe.y); -	sel.ne.y = MAX(sel.ob.y, sel.oe.y); +    if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) { +        sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x; +        sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x; +    } else { +        sel.nb.x = MIN(sel.ob.x, sel.oe.x); +        sel.ne.x = MAX(sel.ob.x, sel.oe.x); +    } +    sel.nb.y = MIN(sel.ob.y, sel.oe.y); +    sel.ne.y = MAX(sel.ob.y, sel.oe.y); -	selsnap(&sel.nb.x, &sel.nb.y, -1); -	selsnap(&sel.ne.x, &sel.ne.y, +1); +    selsnap(&sel.nb.x, &sel.nb.y, -1); +    selsnap(&sel.ne.x, &sel.ne.y, +1); -	/* expand selection over line breaks */ -	if (sel.type == SEL_RECTANGULAR) -		return; -	i = tlinelen(sel.nb.y); -	if (i < sel.nb.x) -		sel.nb.x = i; -	if (tlinelen(sel.ne.y) <= sel.ne.x) -		sel.ne.x = term.col - 1; +    /* expand selection over line breaks */ +    if (sel.type == SEL_RECTANGULAR) +        return; + +    i = tlinelen(TLINE(sel.nb.y)); +    if (sel.nb.x > i) +        sel.nb.x = i; +    if (sel.ne.x >= tlinelen(TLINE(sel.ne.y))) +        sel.ne.x = term.col - 1;  } -int -selected(int x, int y) +    int +regionselected(int x1, int y1, int x2, int y2)  { -	if (sel.mode == SEL_EMPTY || sel.ob.x == -1 || -			sel.alt != IS_SET(MODE_ALTSCREEN)) -		return 0; +    if (sel.ob.x == -1 || sel.mode == SEL_EMPTY || +            sel.alt != IS_SET(MODE_ALTSCREEN) || sel.nb.y > y2 || sel.ne.y < y1) +        return 0; -	if (sel.type == SEL_RECTANGULAR) -		return BETWEEN(y, sel.nb.y, sel.ne.y) -		    && BETWEEN(x, sel.nb.x, sel.ne.x); +    return (sel.type == SEL_RECTANGULAR) ? sel.nb.x <= x2 && sel.ne.x >= x1 +        : (sel.nb.y != y2 || sel.nb.x <= x2) && +        (sel.ne.y != y1 || sel.ne.x >= x1); +} -	return BETWEEN(y, sel.nb.y, sel.ne.y) -	    && (y != sel.nb.y || x >= sel.nb.x) -	    && (y != sel.ne.y || x <= sel.ne.x); +    int +selected(int x, int y) +{ +    return regionselected(x, y, x, y);  } +  void  selsnap(int *x, int *y, int direction)  {  	int newx, newy, xt, yt; +	int rtop = 0, rbot = term.row - 1;  	int delim, prevdelim;  	const Glyph *gp, *prevgp; +	if (!IS_SET(MODE_ALTSCREEN)) +		rtop += -term.histf + term.scr, rbot += term.scr; +  	switch (sel.snap) {  	case SNAP_WORD:  		/*  		 * Snap around if the word wraps around at the end or  		 * beginning of a line.  		 */ -		prevgp = &term.line[*y][*x]; +		prevgp = &TLINE(*y)[*x];  		prevdelim = ISDELIM(prevgp->u);  		for (;;) {  			newx = *x + direction; @@ -529,24 +611,24 @@ selsnap(int *x, int *y, int direction)  			if (!BETWEEN(newx, 0, term.col - 1)) {  				newy += direction;  				newx = (newx + term.col) % term.col; -				if (!BETWEEN(newy, 0, term.row - 1)) +				if (!BETWEEN(newy, rtop, rbot))  					break;  				if (direction > 0)  					yt = *y, xt = *x;  				else  					yt = newy, xt = newx; -				if (!(term.line[yt][xt].mode & ATTR_WRAP)) +				if (!(TLINE(yt)[xt].mode & ATTR_WRAP))  					break;  			} -			if (newx >= tlinelen(newy)) +            if (newx >= tlinelen(TLINE(newy)))  				break; -			gp = &term.line[newy][newx]; +			gp = &TLINE(newy)[newx];  			delim = ISDELIM(gp->u); -			if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim -					|| (delim && gp->u != prevgp->u))) +			if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim || +			    (delim && !(gp->u == ' ' && prevgp->u == ' '))))  				break;  			*x = newx; @@ -561,20 +643,16 @@ selsnap(int *x, int *y, int direction)  		 * has set ATTR_WRAP at its end. Then the whole next or  		 * previous line will be selected.  		 */ -		*x = (direction < 0) ? 0 : term.col - 1; -		if (direction < 0) { -			for (; *y > 0; *y += direction) { -				if (!(term.line[*y-1][term.col-1].mode -						& ATTR_WRAP)) { -					break; -				} +        *x = (direction < 0) ? 0 : term.col - 1; +        if (direction < 0) { +            for (; *y > rtop; *y -= 1) { +                if (!tiswrapped(TLINE(*y-1))) +                        break;  			}  		} else if (direction > 0) { -			for (; *y < term.row-1; *y += direction) { -				if (!(term.line[*y][term.col-1].mode -						& ATTR_WRAP)) { +			for (; *y < rbot; *y += 1) { +				if (!tiswrapped(TLINE(*y)))  					break; -				}  			}  		}  		break; @@ -585,39 +663,34 @@ char *  getsel(void)  {  	char *str, *ptr; -	int y, bufsize, lastx, linelen; -	const Glyph *gp, *last; +	int y, lastx, linelen; +	const Glyph *gp, *lgp; -	if (sel.ob.x == -1) +	if (sel.ob.x == -1 || sel.alt != IS_SET(MODE_ALTSCREEN))  		return NULL; -	bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ; -	ptr = str = xmalloc(bufsize); +	str = xmalloc((term.col + 1) * (sel.ne.y - sel.nb.y + 1) * UTF_SIZ); +	ptr = str;  	/* append every set & selected glyph to the selection */  	for (y = sel.nb.y; y <= sel.ne.y; y++) { -		if ((linelen = tlinelen(y)) == 0) { +		Line line = TLINE(y); + +		if ((linelen = tlinelen(line)) == 0) {  			*ptr++ = '\n';  			continue;  		}  		if (sel.type == SEL_RECTANGULAR) { -			gp = &term.line[y][sel.nb.x]; +			gp = &line[sel.nb.x];  			lastx = sel.ne.x;  		} else { -			gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; +			gp = &line[sel.nb.y == y ? sel.nb.x : 0];  			lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;  		} -		last = &term.line[y][MIN(lastx, linelen-1)]; -		while (last >= gp && last->u == ' ') -			--last; - -		for ( ; gp <= last; ++gp) { -			if (gp->mode & ATTR_WDUMMY) -				continue; +		lgp = &line[MIN(lastx, linelen-1)]; -			ptr += utf8encode(gp->u, ptr); -		} +		ptr = tgetglyphs(ptr, gp, lgp);  		/*  		 * Copy and pasting of line endings is inconsistent @@ -629,10 +702,10 @@ getsel(void)  		 * FIXME: Fix the computer world.  		 */  		if ((y < sel.ne.y || lastx >= linelen) && -		    (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR)) +		    (!(lgp->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR))  			*ptr++ = '\n';  	} -	*ptr = 0; +	*ptr = '\0';  	return str;  } @@ -641,9 +714,15 @@ selclear(void)  {  	if (sel.ob.x == -1)  		return; +	selremove(); +	tsetdirt(sel.nb.y, sel.ne.y); +} + +void +selremove(void) +{  	sel.mode = SEL_IDLE;  	sel.ob.x = -1; -	tsetdirt(sel.nb.y, sel.ne.y);  }  void @@ -845,6 +924,8 @@ ttywrite(const char *s, size_t n, int may_echo)  {  	const char *next; +    kscrolldown(&((Arg){ .i = term.scr })); +  	if (may_echo && IS_SET(MODE_ECHO))  		twrite(s, n, 1); @@ -980,7 +1061,7 @@ tsetdirtattr(int attr)  	for (i = 0; i < term.row-1; i++) {  		for (j = 0; j < term.col-1; j++) {  			if (term.line[i][j].mode & attr) { -				tsetdirt(i, i); +				term.dirty[i] = 1;  				break;  			}  		} @@ -990,7 +1071,8 @@ tsetdirtattr(int attr)  void  tfulldirt(void)  { -	tsetdirt(0, term.row-1); +    for (int i = 0; i < term.row; i++) +        term.dirty[i] = 1;  }  void @@ -1008,109 +1090,260 @@ tcursor(int mode)  }  void +tresetcursor(void) +{ +	term.c = (TCursor){ { .mode = ATTR_NULL, .fg = defaultfg, .bg = defaultbg }, +	                    .x = 0, .y = 0, .state = CURSOR_DEFAULT }; +} + +void  treset(void)  {  	uint i; +    int x, y; -	term.c = (TCursor){{ -		.mode = ATTR_NULL, -		.fg = defaultfg, -		.bg = defaultbg -	}, .x = 0, .y = 0, .state = CURSOR_DEFAULT}; +	tresetcursor();  	memset(term.tabs, 0, term.col * sizeof(*term.tabs));  	for (i = tabspaces; i < term.col; i += tabspaces)  		term.tabs[i] = 1;  	term.top = 0; +	term.histf = 0; +	term.scr = 0;  	term.bot = term.row - 1;  	term.mode = MODE_WRAP|MODE_UTF8;  	memset(term.trantbl, CS_USA, sizeof(term.trantbl));  	term.charset = 0; +    selremove();  	for (i = 0; i < 2; i++) { -		tmoveto(0, 0); -		tcursor(CURSOR_SAVE); -		tclearregion(0, 0, term.col-1, term.row-1); -		tswapscreen(); +        tcursor(CURSOR_SAVE); /* reset saved cursor */ +        for (y = 0; y < term.row; y++) +            for (x = 0; x < term.col; x++) +                tclearglyph(&term.line[y][x], 0); +        tswapscreen();  	} +    tfulldirt();  }  void  tnew(int col, int row)  { -	term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } }; -	tresize(col, row); -	treset(); +    int i, j; + +    for (i = 0; i < 2; i++) { +        term.line = xmalloc(row * sizeof(Line)); +        for (j = 0; j < row; j++) +            term.line[j] = xmalloc(col * sizeof(Glyph)); +        term.col = col, term.row = row; +        tswapscreen(); +    } +    term.dirty = xmalloc(row * sizeof(*term.dirty)); +    term.tabs = xmalloc(col * sizeof(*term.tabs)); +    for (i = 0; i < HISTSIZE; i++) +        term.hist[i] = xmalloc(col * sizeof(Glyph)); +    treset();  } +/* handle it with care */  void  tswapscreen(void)  { -	Line *tmp = term.line; +	static Line *altline; +	static int altcol, altrow; +	Line *tmpline = term.line; +	int tmpcol = term.col, tmprow = term.row; -	term.line = term.alt; -	term.alt = tmp; +	term.line = altline; +	term.col = altcol, term.row = altrow; +	altline = tmpline; +	altcol = tmpcol, altrow = tmprow;  	term.mode ^= MODE_ALTSCREEN; -	tfulldirt();  }  void -tscrolldown(int orig, int n) +tloaddefscreen(int clear, int loadcursor)  { -	int i; -	Line temp; +	int col, row, alt = IS_SET(MODE_ALTSCREEN); -	LIMIT(n, 0, term.bot-orig+1); +	if (alt) { +		if (clear) +			tclearregion(0, 0, term.col-1, term.row-1, 1); +		col = term.col, row = term.row; +		tswapscreen(); +	} +	if (loadcursor) +		tcursor(CURSOR_LOAD); +	if (alt) +		tresizedef(col, row); +} -	tsetdirt(orig, term.bot-n); -	tclearregion(0, term.bot-n+1, term.col-1, term.bot); +void +tloadaltscreen(int clear, int savecursor) +{ +	int col, row, def = !IS_SET(MODE_ALTSCREEN); -	for (i = term.bot; i >= orig+n; i--) { -		temp = term.line[i]; -		term.line[i] = term.line[i-n]; -		term.line[i-n] = temp; +	if (savecursor) +		tcursor(CURSOR_SAVE); +	if (def) { +		col = term.col, row = term.row; +		tswapscreen(); +		term.scr = 0; +		tresizealt(col, row);  	} +	if (clear) +		tclearregion(0, 0, term.col-1, term.row-1, 1); +} -	selscroll(orig, n); +int +tisaltscreen(void) +{ +	return IS_SET(MODE_ALTSCREEN);  } +  void -tscrollup(int orig, int n) +kscrolldown(const Arg* a)  { -	int i; -	Line temp; +    int n = a->i; -	LIMIT(n, 0, term.bot-orig+1); +    if (!term.scr || IS_SET(MODE_ALTSCREEN)) +        return; -	tclearregion(0, orig, term.col-1, orig+n-1); -	tsetdirt(orig+n, term.bot); +    if (n < 0) +        n = MAX(term.row / -n, 1); -	for (i = orig; i <= term.bot-n; i++) { -		temp = term.line[i]; -		term.line[i] = term.line[i+n]; -		term.line[i+n] = temp; -	} +    if (n <= term.scr) { +        term.scr -= n; +    } else { +        n = term.scr; +        term.scr = 0; +    } +	if (sel.ob.x != -1 && !sel.alt) +		selmove(-n); /* negate change in term.scr */ +	tfulldirt(); +} + + + +void +kscrollup(const Arg* a) +{ +    int n = a->i; + +    if (!term.histf || IS_SET(MODE_ALTSCREEN)) +        return; + +    if (n < 0) +        n = MAX(term.row / -n, 1); + +    if (term.scr + n <= term.histf) { +        term.scr += n; +    } else { +        n = term.histf - term.scr; +        term.scr = term.histf; +    } + +    if (sel.ob.x != -1 && !sel.alt) +        selmove(n); /* negate change in term.scr */ +    tfulldirt(); -	selscroll(orig, -n);  }  void -selscroll(int orig, int n) +tscrolldown(int top, int n)  { -	if (sel.ob.x == -1 || sel.alt != IS_SET(MODE_ALTSCREEN)) -		return; +    int i, bot = term.bot; +    Line temp; -	if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) { -		selclear(); -	} else if (BETWEEN(sel.nb.y, orig, term.bot)) { -		sel.ob.y += n; -		sel.oe.y += n; -		if (sel.ob.y < term.top || sel.ob.y > term.bot || -		    sel.oe.y < term.top || sel.oe.y > term.bot) { -			selclear(); -		} else { -			selnormalize(); -		} +    if (n <= 0) +        return; +    n = MIN(n, bot-top+1); + +    tsetdirt(top, bot-n); +    tclearregion(0, bot-n+1, term.col-1, bot, 1); + +    for (i = bot; i >= top+n; i--) { +        temp = term.line[i]; +        term.line[i] = term.line[i-n]; +        term.line[i-n] = temp; +    } + +    if (sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN)) +        selscroll(top, bot, n); +} + +void +tscrollup(int top, int bot, int n, int mode) +{ +    int i, j, s; +    int alt = IS_SET(MODE_ALTSCREEN); +    int savehist = !alt && top == 0 && mode != SCROLL_NOSAVEHIST; +    Line temp; + +    if (n <= 0) +        return; +    n = MIN(n, bot-top+1); + +    if (savehist) { +        for (i = 0; i < n; i++) { +            term.histi = (term.histi + 1) % HISTSIZE; +            temp = term.hist[term.histi]; +            for (j = 0; j < term.col; j++) +                tclearglyph(&temp[j], 1); +            term.hist[term.histi] = term.line[i]; +            term.line[i] = temp; +        } +        term.histf = MIN(term.histf + n, HISTSIZE); +        s = n; +        if (term.scr) { +            j = term.scr; +            term.scr = MIN(j + n, HISTSIZE); +            s = j + n - term.scr; +        } +        if (mode != SCROLL_RESIZE) +            tfulldirt(); +    } else { +        tclearregion(0, top, term.col-1, top+n-1, 1); +        tsetdirt(top+n, bot); +    } + +    for (i = top; i <= bot-n; i++) { +        temp = term.line[i]; +        term.line[i] = term.line[i+n]; +        term.line[i+n] = temp; +    } + +    if (sel.ob.x != -1 && sel.alt == alt) { +        if (!savehist) { +            selscroll(top, bot, -n); +        } else if (s > 0) { +            selmove(-s); +            if (-term.scr + sel.nb.y < -term.histf) +                selremove(); +        } +    } +} + +void +selmove(int n) + { +	sel.ob.y += n, sel.nb.y += n; +	sel.oe.y += n, sel.ne.y += n; +} + +void +selscroll(int top, int bot, int n) +{ +    /* turn absolute coordinates into relative */ +    top += term.scr, bot += term.scr; + +    if (BETWEEN(sel.nb.y, top, bot) != BETWEEN(sel.ne.y, top, bot)) { +        selclear(); +    } else if (BETWEEN(sel.nb.y, top, bot)) { +        selmove(n); +        if (sel.nb.y < top || sel.ne.y > bot) +            selclear();  	}  } @@ -1120,7 +1353,7 @@ tnewline(int first_col)  	int y = term.c.y;  	if (y == term.bot) { -		tscrollup(term.top, 1); +		tscrollup(term.top, term.bot, 1, SCROLL_SAVEHIST);  	} else {  		y++;  	} @@ -1128,6 +1361,29 @@ tnewline(int first_col)  }  void +readcolonargs(char **p, int cursor, int params[][CAR_PER_ARG]) +{ +	int i = 0; +	for (; i < CAR_PER_ARG; i++) +		params[cursor][i] = -1; + +	if (**p != ':') +		return; + +	char *np = NULL; +	i = 0; + +	while (**p == ':' && i < CAR_PER_ARG) { +		while (**p == ':') +			(*p)++; +		params[cursor][i] = strtol(*p, &np, 10); +		*p = np; +		i++; +	} +} + + +void  csiparse(void)  {  	char *p = csiescseq.buf, *np; @@ -1152,6 +1408,7 @@ csiparse(void)  		p = np;  		if (sep == ';' && *p == ':')  			sep = ':'; /* allow override to colon once */ +		readcolonargs(&p, csiescseq.narg-1, csiescseq.carg);  // TJK here dont know if to put before or after above  		if (*p != sep || csiescseq.narg == ESC_ARG_SIZ)  			break;  		p++; @@ -1187,101 +1444,115 @@ tmoveto(int x, int y)  void  tsetchar(Rune u, const Glyph *attr, int x, int y)  { -	static const char *vt100_0[62] = { /* 0x41 - 0x7e */ -		"↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */ -		0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ -		0, 0, 0, 0, 0, 0, 0, 0, /* P - W */ -		0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */ -		"◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */ -		"", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */ -		"⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */ -		"│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */ -	}; +    static const char *vt100_0[62] = { /* 0x41 - 0x7e */ +        "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */ +        0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ +        0, 0, 0, 0, 0, 0, 0, 0, /* P - W */ +        0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */ +        "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */ +        "", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */ +        "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */ +        "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */ +    }; -	/* -	 * The table is proudly stolen from rxvt. -	 */ -	if (term.trantbl[term.charset] == CS_GRAPHIC0 && -	   BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41]) -		utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ); - -	if (term.line[y][x].mode & ATTR_WIDE) { -		if (x+1 < term.col) { -			term.line[y][x+1].u = ' '; -			term.line[y][x+1].mode &= ~ATTR_WDUMMY; -		} -	} else if (term.line[y][x].mode & ATTR_WDUMMY) { -		term.line[y][x-1].u = ' '; -		term.line[y][x-1].mode &= ~ATTR_WIDE; -	} +    /* +     * The table is proudly stolen from rxvt. +     */ +    if (term.trantbl[term.charset] == CS_GRAPHIC0 && +            BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41]) +        utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ); + +    if (term.line[y][x].mode & ATTR_WIDE) { +        if (x+1 < term.col) { +            term.line[y][x+1].u = ' '; +            term.line[y][x+1].mode &= ~ATTR_WDUMMY; +        } +    } else if (term.line[y][x].mode & ATTR_WDUMMY) { +        term.line[y][x-1].u = ' '; +        term.line[y][x-1].mode &= ~ATTR_WIDE; +    } -	term.dirty[y] = 1; -	term.line[y][x] = *attr; -	term.line[y][x].u = u; +    term.dirty[y] = 1; +    term.line[y][x] = *attr; +    term.line[y][x].u = u; +    term.line[y][x].mode |= ATTR_SET; + +	if (isboxdraw(u)) +		term.line[y][x].mode |= ATTR_BOXDRAW;  } + +  void -tclearregion(int x1, int y1, int x2, int y2) +tclearglyph(Glyph *gp, int usecurattr)  { -	int x, y, temp; -	Glyph *gp; +	if (usecurattr) { +		gp->fg = term.c.attr.fg; +		gp->bg = term.c.attr.bg; +	} else { +		gp->fg = defaultfg; +		gp->bg = defaultbg; +	} +	gp->mode = ATTR_NULL; +	gp->u = ' '; +} -	if (x1 > x2) -		temp = x1, x1 = x2, x2 = temp; -	if (y1 > y2) -		temp = y1, y1 = y2, y2 = temp; -	LIMIT(x1, 0, term.col-1); -	LIMIT(x2, 0, term.col-1); -	LIMIT(y1, 0, term.row-1); -	LIMIT(y2, 0, term.row-1); -	for (y = y1; y <= y2; y++) { +void +tclearregion(int x1, int y1, int x2, int y2, int usecurattr) +{ +	int x, y; +	/* regionselected() takes relative coordinates */ +	if (regionselected(x1+term.scr, y1+term.scr, x2+term.scr, y2+term.scr)) +		selremove(); + +    for (y = y1; y <= y2; y++) {  		term.dirty[y] = 1; -		for (x = x1; x <= x2; x++) { -			gp = &term.line[y][x]; -			if (selected(x, y)) -				selclear(); -			gp->fg = term.c.attr.fg; -			gp->bg = term.c.attr.bg; -			gp->mode = 0; -			gp->u = ' '; -		} +		for (x = x1; x <= x2; x++) +			tclearglyph(&term.line[y][x], usecurattr);  	}  }  void  tdeletechar(int n)  { -	int dst, src, size; -	Glyph *line; - -	LIMIT(n, 0, term.col - term.c.x); +    int src, dst, size; +    Line line; -	dst = term.c.x; -	src = term.c.x + n; -	size = term.col - src; -	line = term.line[term.c.y]; +    if (n <= 0) +        return; -	memmove(&line[dst], &line[src], size * sizeof(Glyph)); -	tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); +    dst = term.c.x; +    src = MIN(term.c.x + n, term.col); +    size = term.col - src; +    if (size > 0) { +        /* +         * otherwise src would point beyond the array +         * https://stackoverflow.com/questions/29844298 +         */ +        line = term.line[term.c.y]; +        memmove(&line[dst], &line[src], size * sizeof(Glyph)); +    } +    tclearregion(dst + size, term.c.y, term.col - 1, term.c.y, 1);  }  void  tinsertblank(int n)  { -	int dst, src, size; -	Glyph *line; +    int src, dst, size; +    Line line; -	LIMIT(n, 0, term.col - term.c.x); - -	dst = term.c.x + n; -	src = term.c.x; -	size = term.col - dst; -	line = term.line[term.c.y]; - -	memmove(&line[dst], &line[src], size * sizeof(Glyph)); -	tclearregion(src, term.c.y, dst - 1, term.c.y); +    if (n <= 0) +        return; +    dst = MIN(term.c.x + n, term.col); +    src = term.c.x; +    size = term.col - dst; +    if (size > 0) { /* otherwise dst would point beyond the array */ +        line = term.line[term.c.y]; +        memmove(&line[dst], &line[src], size * sizeof(Glyph)); +    } +    tclearregion(src, term.c.y, dst - 1, term.c.y, 1);  }  void @@ -1295,7 +1566,7 @@ void  tdeleteline(int n)  {  	if (BETWEEN(term.c.y, term.top, term.bot)) -		tscrollup(term.c.y, n); +		tscrollup(term.c.y, term.bot, n, SCROLL_NOSAVEHIST);  }  int32_t @@ -1368,6 +1639,10 @@ tsetattr(const int *attr, int l)  				ATTR_STRUCK     );  			term.c.attr.fg = defaultfg;  			term.c.attr.bg = defaultbg; +			term.c.attr.ustyle = -1; +			term.c.attr.ucolor[0] = -1; +			term.c.attr.ucolor[1] = -1; +			term.c.attr.ucolor[2] = -1;  			break;  		case 1:  			term.c.attr.mode |= ATTR_BOLD; @@ -1379,7 +1654,14 @@ tsetattr(const int *attr, int l)  			term.c.attr.mode |= ATTR_ITALIC;  			break;  		case 4: -			term.c.attr.mode |= ATTR_UNDERLINE; +			term.c.attr.ustyle = csiescseq.carg[i][0]; + +			if (term.c.attr.ustyle != 0) +				term.c.attr.mode |= ATTR_UNDERLINE; +			else +				term.c.attr.mode &= ~ATTR_UNDERLINE; + +			term.c.attr.mode ^= ATTR_DIRTYUNDERLINE;  			break;  		case 5: /* slow blink */  			/* FALLTHROUGH */ @@ -1430,6 +1712,18 @@ tsetattr(const int *attr, int l)  		case 49:  			term.c.attr.bg = defaultbg;  			break; +		case 58: +			term.c.attr.ucolor[0] = csiescseq.carg[i][1]; +			term.c.attr.ucolor[1] = csiescseq.carg[i][2]; +			term.c.attr.ucolor[2] = csiescseq.carg[i][3]; +			term.c.attr.mode ^= ATTR_DIRTYUNDERLINE; +			break; +		case 59: +			term.c.attr.ucolor[0] = -1; +			term.c.attr.ucolor[1] = -1; +			term.c.attr.ucolor[2] = -1; +			term.c.attr.mode ^= ATTR_DIRTYUNDERLINE; +			break;  		default:  			if (BETWEEN(attr[i], 30, 37)) {  				term.c.attr.fg = attr[i] - 30; @@ -1469,7 +1763,7 @@ tsetscroll(int t, int b)  void  tsetmode(int priv, int set, const int *args, int narg)  { -	int alt; const int *lim; +	const int *lim;  	for (lim = args + narg; args < lim; ++args) {  		if (priv) { @@ -1530,26 +1824,20 @@ tsetmode(int priv, int set, const int *args, int narg)  				xsetmode(set, MODE_8BIT);  				break;  			case 1049: /* swap screen & set/restore cursor as xterm */ -				if (!allowaltscreen) -					break; -				tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); -				/* FALLTHROUGH */  			case 47: /* swap screen */ -			case 1047: +			case 1047: /*swap screen, clearing alternate screen */  				if (!allowaltscreen)  					break; -				alt = IS_SET(MODE_ALTSCREEN); -				if (alt) { -					tclearregion(0, 0, term.col-1, -							term.row-1); -				} -				if (set ^ alt) /* set is always 1 or 0 */ -					tswapscreen(); -				if (*args != 1049) -					break; +				if (set) +					tloadaltscreen(*args == 1049, *args == 1049); +				else +					tloaddefscreen(*args == 1047, *args == 1049); +				break;  				/* FALLTHROUGH */ -			case 1048: -				tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); +            case 1048: +                if (!allowaltscreen) +                    break; +                tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);  				break;  			case 2004: /* 2004: bracketed paste mode */  				xsetmode(set, MODE_BRCKTPASTE); @@ -1600,7 +1888,7 @@ void  csihandle(void)  {  	char buf[40]; -	int len; +	int n, x;  	switch (csiescseq.mode[0]) {  	default: @@ -1698,19 +1986,29 @@ csihandle(void)  	case 'J': /* ED -- Clear screen */  		switch (csiescseq.arg[0]) {  		case 0: /* below */ -			tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); +			tclearregion(term.c.x, term.c.y, term.col-1, term.c.y, 1);  			if (term.c.y < term.row-1) { -				tclearregion(0, term.c.y+1, term.col-1, -						term.row-1); +				tclearregion(0, term.c.y+1, term.col-1, term.row-1, 1);  			}  			break;  		case 1: /* above */ -			if (term.c.y > 0) -				tclearregion(0, 0, term.col-1, term.c.y-1); -			tclearregion(0, term.c.y, term.c.x, term.c.y); +			if (term.c.y >= 1) +				tclearregion(0, 0, term.col-1, term.c.y-1, 1); +			tclearregion(0, term.c.y, term.c.x, term.c.y, 1);  			break;  		case 2: /* all */ -			tclearregion(0, 0, term.col-1, term.row-1); +			if (IS_SET(MODE_ALTSCREEN)) { +				tclearregion(0, 0, term.col-1, term.row-1, 1); +				break; +			} +			/* vte does this: +			   tscrollup(0, term.row-1, term.row, SCROLL_SAVEHIST); */ + +			/* alacritty does this: */ +			for (n = term.row-1; n >= 0 && tlinelen(term.line[n]) == 0; n--); +			if (n >= 0) +				tscrollup(0, term.row-1, n+1, SCROLL_SAVEHIST); +			tscrollup(0, term.row-1, term.row-n-1, SCROLL_NOSAVEHIST);  			break;  		default:  			goto unknown; @@ -1719,21 +2017,21 @@ csihandle(void)  	case 'K': /* EL -- Clear line */  		switch (csiescseq.arg[0]) {  		case 0: /* right */ -			tclearregion(term.c.x, term.c.y, term.col-1, -					term.c.y); +			tclearregion(term.c.x, term.c.y, term.col-1, term.c.y, 1);  			break;  		case 1: /* left */ -			tclearregion(0, term.c.y, term.c.x, term.c.y); +			tclearregion(0, term.c.y, term.c.x, term.c.y, 1);  			break;  		case 2: /* all */ -			tclearregion(0, term.c.y, term.col-1, term.c.y); +			tclearregion(0, term.c.y, term.col-1, term.c.y, 1);  			break;  		}  		break;  	case 'S': /* SU -- Scroll <n> line up */  		if (csiescseq.priv) break;  		DEFAULT(csiescseq.arg[0], 1); -		tscrollup(term.top, csiescseq.arg[0]); +		/* xterm, urxvt, alacritty save this in history */ +		tscrollup(term.top, term.bot, csiescseq.arg[0], SCROLL_SAVEHIST);  		break;  	case 'T': /* SD -- Scroll <n> line down */  		DEFAULT(csiescseq.arg[0], 1); @@ -1751,9 +2049,11 @@ csihandle(void)  		tdeleteline(csiescseq.arg[0]);  		break;  	case 'X': /* ECH -- Erase <n> char */ +		if (csiescseq.arg[0] < 0) +			return;  		DEFAULT(csiescseq.arg[0], 1); -		tclearregion(term.c.x, term.c.y, -				term.c.x + csiescseq.arg[0] - 1, term.c.y); +		x = MIN(term.c.x + csiescseq.arg[0], term.col) - 1; +		tclearregion(term.c.x, term.c.y, x, term.c.y, 1);  		break;  	case 'P': /* DCH -- Delete <n> char */  		DEFAULT(csiescseq.arg[0], 1); @@ -1779,9 +2079,9 @@ csihandle(void)  			ttywrite("\033[0n", sizeof("\033[0n") - 1, 0);  			break;  		case 6: /* Report Cursor Position (CPR) "<row>;<column>R" */ -			len = snprintf(buf, sizeof(buf), "\033[%i;%iR", +			n = snprintf(buf, sizeof(buf), "\033[%i;%iR",  			               term.c.y+1, term.c.x+1); -			ttywrite(buf, len, 0); +			ttywrite(buf, n, 0);  			break;  		default:  			goto unknown; @@ -1813,6 +2113,33 @@ csihandle(void)  			goto unknown;  		}  		break; +	case 't': /* title stack operations */ +		switch (csiescseq.arg[0]) { +		case 22: /* pust current title on stack */ +			switch (csiescseq.arg[1]) { +			case 0: +			case 1: +			case 2: +				xpushtitle(); +				break; +			default: +				goto unknown; +			} +			break; +		case 23: /* pop last title from stack */ +			switch (csiescseq.arg[1]) { +			case 0: +			case 1: +			case 2: +				xsettitle(NULL, 1); +				break; +			default: +				goto unknown; +			} +			break; +		default: +			goto unknown; +		}  	}  } @@ -1891,7 +2218,7 @@ strhandle(void)  		switch (par) {  		case 0:  			if (narg > 1) { -				xsettitle(strescseq.args[1]); +				xsettitle(strescseq.args[1], 0);  				xseticontitle(strescseq.args[1]);  			}  			return; @@ -1901,7 +2228,7 @@ strhandle(void)  			return;  		case 2:  			if (narg > 1) -				xsettitle(strescseq.args[1]); +				xsettitle(strescseq.args[1], 0);  			return;  		case 52:  			if (narg > 2 && allowwindowops) { @@ -1960,7 +2287,7 @@ strhandle(void)  		}  		break;  	case 'k': /* old title set compatibility */ -		xsettitle(strescseq.args[0]); +		xsettitle(strescseq.args[0], 0);  		return;  	case 'P': /* DCS -- Device Control String */  	case '_': /* APC -- Application Program Command */ @@ -2079,16 +2406,8 @@ tdumpsel(void)  void  tdumpline(int n)  { -	char buf[UTF_SIZ]; -	const Glyph *bp, *end; - -	bp = &term.line[n][0]; -	end = &bp[MIN(tlinelen(n), term.col) - 1]; -	if (bp != end || bp->u != ' ') { -		for ( ; bp <= end; ++bp) -			tprinter(buf, utf8encode(bp->u, buf)); -	} -	tprinter("\n", 1); +    char str[(term.col + 1) * UTF_SIZ]; +    tprinter(str, tgetline(str, &term.line[n][0]));  }  void @@ -2309,7 +2628,7 @@ eschandle(uchar ascii)  		return 0;  	case 'D': /* IND -- Linefeed */  		if (term.c.y == term.bot) { -			tscrollup(term.top, 1); +			tscrollup(term.top, term.bot, 1, SCROLL_SAVEHIST);  		} else {  			tmoveto(term.c.x, term.c.y+1);  		} @@ -2332,6 +2651,7 @@ eschandle(uchar ascii)  		break;  	case 'c': /* RIS -- Reset to initial state */  		treset(); +		xfreetitlestack();  		resettitle();  		xloadcols();  		xsetmode(0, MODE_HIDE); @@ -2466,7 +2786,9 @@ check_control_code:  		 */  		return;  	} -	if (selected(term.c.x, term.c.y)) + +    /* selected() takes relative coordinates */ +	if (selected(term.c.x + term.scr, term.c.y + term.scr))  		selclear();  	gp = &term.line[term.c.y][term.c.x]; @@ -2506,6 +2828,7 @@ check_control_code:  	if (term.c.x+width < term.col) {  		tmoveto(term.c.x+width, term.c.y);  	} else { +		term.wrapcwidth[IS_SET(MODE_ALTSCREEN)] = width;  		term.c.state |= CURSOR_WRAPNEXT;  	}  } @@ -2543,91 +2866,290 @@ twrite(const char *buf, int buflen, int show_ctrl)  }  void +rscrolldown(int n) +{ +    int i; +    Line temp; + +    /* can never be true as of now +       if (IS_SET(MODE_ALTSCREEN)) +       return; */ + +    if ((n = MIN(n, term.histf)) <= 0) +        return; + +    for (i = term.c.y + n; i >= n; i--) { +        temp = term.line[i]; +        term.line[i] = term.line[i-n]; +        term.line[i-n] = temp; +    } +    for (/*i = n - 1 */; i >= 0; i--) { +        temp = term.line[i]; +        term.line[i] = term.hist[term.histi]; +        term.hist[term.histi] = temp; +        term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE; +    } +    term.c.y += n; +    term.histf -= n; +    if ((i = term.scr - n) >= 0) { +        term.scr = i; +    } else { +        term.scr = 0; +        if (sel.ob.x != -1 && !sel.alt) +            selmove(-i); +    } +} + + + +void  tresize(int col, int row)  { -	int i; -	int minrow = MIN(row, term.row); -	int mincol = MIN(col, term.col);  	int *bp; -	TCursor c; +	/* col and row are always MAX(_, 1)  	if (col < 1 || row < 1) { -		fprintf(stderr, -		        "tresize: error resizing to %dx%d\n", col, row); +		fprintf(stderr, "tresize: error resizing to %dx%d\n", col, row);  		return; -	} - -	/* -	 * slide screen to keep cursor where we expect it - -	 * tscrollup would work here, but we can optimize to -	 * memmove because we're freeing the earlier lines -	 */ -	for (i = 0; i <= term.c.y - row; i++) { -		free(term.line[i]); -		free(term.alt[i]); -	} -	/* ensure that both src and dst are not NULL */ -	if (i > 0) { -		memmove(term.line, term.line + i, row * sizeof(Line)); -		memmove(term.alt, term.alt + i, row * sizeof(Line)); -	} -	for (i += row; i < term.row; i++) { -		free(term.line[i]); -		free(term.alt[i]); -	} +	} */ -	/* resize to new height */ -	term.line = xrealloc(term.line, row * sizeof(Line)); -	term.alt  = xrealloc(term.alt,  row * sizeof(Line));  	term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));  	term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + 	if (col > term.col) { + 		bp = term.tabs + term.col; + 		memset(bp, 0, sizeof(*term.tabs) * (col - term.col)); + 		while (--bp > term.tabs && !*bp) + 			/* nothing */ ; + 		for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) + 			*bp = 1; + 	} -	/* resize each row to new width, zero-pad if needed */ -	for (i = 0; i < minrow; i++) { -		term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); -		term.alt[i]  = xrealloc(term.alt[i],  col * sizeof(Glyph)); -	} +	if (IS_SET(MODE_ALTSCREEN)) +		tresizealt(col, row); +	else +		tresizedef(col, row); +} -	/* allocate any new rows */ -	for (/* i = minrow */; i < row; i++) { -		term.line[i] = xmalloc(col * sizeof(Glyph)); -		term.alt[i] = xmalloc(col * sizeof(Glyph)); + +void +tresizedef(int col, int row) +{ +	int i, j; + +	/* return if dimensions haven't changed */ +	if (term.col == col && term.row == row) { +		tfulldirt(); +		return;  	} -	if (col > term.col) { -		bp = term.tabs + term.col; - -		memset(bp, 0, sizeof(*term.tabs) * (col - term.col)); -		while (--bp > term.tabs && !*bp) -			/* nothing */ ; -		for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) -			*bp = 1; +	if (col != term.col) { +		if (!sel.alt) +			selremove(); +		treflow(col, row); +	} else { +		/* slide screen up if otherwise cursor would get out of the screen */ +		if (term.c.y >= row) { +			tscrollup(0, term.row - 1, term.c.y - row + 1, SCROLL_RESIZE); +			term.c.y = row - 1; + 		} +		for (i = row; i < term.row; i++) +			free(term.line[i]); + +		/* resize to new height */ +		term.line = xrealloc(term.line, row * sizeof(Line)); +		/* allocate any new rows */ +		for (i = term.row; i < row; i++) { +			term.line[i] = xmalloc(col * sizeof(Glyph)); +			for (j = 0; j < col; j++) +				tclearglyph(&term.line[i][j], 0); + 		} +		/* scroll down as much as height has increased */ +		rscrolldown(row - term.row);  	}  	/* update terminal size */ -	term.col = col; -	term.row = row; +	term.col = col, term.row = row;  	/* reset scrolling region */ -	tsetscroll(0, row-1); -	/* make use of the LIMIT in tmoveto */ -	tmoveto(term.c.x, term.c.y); -	/* Clearing both screens (it makes dirty all lines) */ -	c = term.c; -	for (i = 0; i < 2; i++) { -		if (mincol < col && 0 < minrow) { -			tclearregion(mincol, 0, col - 1, minrow - 1); -		} -		if (0 < col && minrow < row) { -			tclearregion(0, minrow, col - 1, row - 1); -		} -		tswapscreen(); -		tcursor(CURSOR_LOAD); -	} -	term.c = c; +	term.top = 0, term.bot = row - 1; +	/* dirty all lines */ +	tfulldirt(); +} + + + +void +tresizealt(int col, int row) +{ +    int i, j; + +    /* return if dimensions haven't changed */ +    if (term.col == col && term.row == row) { +        tfulldirt(); +        return; +    } +    if (sel.alt) +        selremove(); +    /* slide screen up if otherwise cursor would get out of the screen */ +    for (i = 0; i <= term.c.y - row; i++) +        free(term.line[i]); +    if (i > 0) { +        /* ensure that both src and dst are not NULL */ +        memmove(term.line, term.line + i, row * sizeof(Line)); +        term.c.y = row - 1; +    } +    for (i += row; i < term.row; i++) +        free(term.line[i]); +    /* resize to new height */ +    term.line = xrealloc(term.line, row * sizeof(Line)); +    /* resize to new width */ +    for (i = 0; i < MIN(row, term.row); i++) { +        term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); +        for (j = term.col; j < col; j++) +            tclearglyph(&term.line[i][j], 0); +    } +    /* allocate any new rows */ +    for (/*i = MIN(row, term.row) */; i < row; i++) { +        term.line[i] = xmalloc(col * sizeof(Glyph)); +        for (j = 0; j < col; j++) +            tclearglyph(&term.line[i][j], 0); +    } +    /* update cursor */ +    if (term.c.x >= col) { +        term.c.state &= ~CURSOR_WRAPNEXT; +        term.c.x = col - 1; +    } else { +        UPDATEWRAPNEXT(1, col); +    } +    /* update terminal size */ +    term.col = col, term.row = row; +    /* reset scrolling region */ +    term.top = 0, term.bot = row - 1; +    /* dirty all lines */ +    tfulldirt(); + } + + + + + +void +treflow(int col, int row) +{ +    int i, j; +    int oce, nce, bot, scr; +    int ox = 0, oy = -term.histf, nx = 0, ny = -1, len; +    int cy = -1; /* proxy for new y coordinate of cursor */ +    int nlines; +    Line *buf, line; + +    /* y coordinate of cursor line end */ +    for (oce = term.c.y; oce < term.row - 1 && +            tiswrapped(term.line[oce]); oce++); + +    nlines = term.histf + oce + 1; +    if (col < term.col) { +        /* each line can take this many lines after reflow */ +        j = (term.col + col - 1) / col; +        nlines = j * nlines; +        if (nlines > HISTSIZE + RESIZEBUFFER + row) { +            nlines = HISTSIZE + RESIZEBUFFER + row; +            oy = -(nlines / j - oce - 1); +        } +    } +    buf = xmalloc(nlines * sizeof(Line)); +    do { +        if (!nx) +            buf[++ny] = xmalloc(col * sizeof(Glyph)); +        if (!ox) { +            line = TLINEABS(oy); +            len = tlinelen(line); +        } +        if (oy == term.c.y) { +            if (!ox) +                len = MAX(len, term.c.x + 1); +            /* update cursor */ +            if (cy < 0 && term.c.x - ox < col - nx) { +                term.c.x = nx + term.c.x - ox, cy = ny; +                UPDATEWRAPNEXT(0, col); +            } +        } +        /* get reflowed lines in buf */ +        if (col - nx > len - ox) { +            memcpy(&buf[ny][nx], &line[ox], (len-ox) * sizeof(Glyph)); +            nx += len - ox; +            if (len == 0 || !(line[len - 1].mode & ATTR_WRAP)) { +                for (j = nx; j < col; j++) +                    tclearglyph(&buf[ny][j], 0); +                nx = 0; +            } else if (nx > 0) { +                buf[ny][nx - 1].mode &= ~ATTR_WRAP; +            } +            ox = 0, oy++; +        } else if (col - nx == len - ox) { +            memcpy(&buf[ny][nx], &line[ox], (col-nx) * sizeof(Glyph)); +            ox = 0, oy++, nx = 0; +        } else/* if (col - nx < len - ox) */ { +            memcpy(&buf[ny][nx], &line[ox], (col-nx) * sizeof(Glyph)); +            ox += col - nx; +            buf[ny][col - 1].mode |= ATTR_WRAP; +            nx = 0; +        } +    } while (oy <= oce); +    if (nx) +        for (j = nx; j < col; j++) +            tclearglyph(&buf[ny][j], 0); + +    /* free extra lines */ +    for (i = row; i < term.row; i++) +        free(term.line[i]); +    /* resize to new height */ +    term.line = xrealloc(term.line, row * sizeof(Line)); + +    bot = MIN(ny, row - 1); +    scr = MAX(row - term.row, 0); +    /* update y coordinate of cursor line end */ +    nce = MIN(oce + scr, bot); +    /* update cursor y coordinate */ +    term.c.y = nce - (ny - cy); +    if (term.c.y < 0) { +        j = nce, nce = MIN(nce + -term.c.y, bot); +        term.c.y += nce - j; +        while (term.c.y < 0) { +            free(buf[ny--]); +            term.c.y++; +        } +    } +    /* allocate new rows */ +    for (i = row - 1; i > nce; i--) { +        term.line[i] = xmalloc(col * sizeof(Glyph)); +        for (j = 0; j < col; j++) +            tclearglyph(&term.line[i][j], 0); +    } +    /* fill visible area */ +    for (/*i = nce */; i >= term.row; i--, ny--) +        term.line[i] = buf[ny]; +    for (/*i = term.row - 1 */; i >= 0; i--, ny--) { +        free(term.line[i]); +        term.line[i] = buf[ny]; +    } +    /* fill lines in history buffer and update term.histf */ +    for (/*i = -1 */; ny >= 0 && i >= -HISTSIZE; i--, ny--) { +        j = (term.histi + i + 1 + HISTSIZE) % HISTSIZE; +        free(term.hist[j]); +        term.hist[j] = buf[ny]; +    } +    term.histf = -i - 1; +    term.scr = MIN(term.scr, term.histf); +    /* resize rest of the history lines */ +    for (/*i = -term.histf - 1 */; i >= -HISTSIZE; i--) { +        j = (term.histi + i + 1 + HISTSIZE) % HISTSIZE; +        term.hist[j] = xrealloc(term.hist[j], col * sizeof(Glyph)); +    } +    free(buf);  }  void  resettitle(void)  { -	xsettitle(NULL); +	xsettitle(NULL, 0);  }  void @@ -2640,7 +3162,7 @@ drawregion(int x1, int y1, int x2, int y2)  			continue;  		term.dirty[y] = 0; -		xdrawline(term.line[y], x1, y, x2); +		xdrawline(TLINE(y), x1, y, x2);  	}  } diff --git a/st.desktop b/st.desktop new file mode 100644 index 0000000..2d2b76f --- /dev/null +++ b/st.desktop @@ -0,0 +1,12 @@ +[Desktop Entry] +Type=Application +Exec=st +TryExec=st +Icon=utilities-terminal +Terminal=false +Categories=System;TerminalEmulator; + +Name=st +GenericName=Terminal +Comment=st is a simple terminal implementation for X +StartupWMClass=st-256color @@ -22,18 +22,22 @@  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_SET        = 1 << 0, +	ATTR_BOLD       = 1 << 1, +	ATTR_FAINT      = 1 << 2, +	ATTR_ITALIC     = 1 << 3, +	ATTR_UNDERLINE  = 1 << 4, +	ATTR_BLINK      = 1 << 5, +	ATTR_REVERSE    = 1 << 6, +	ATTR_INVISIBLE  = 1 << 7, +	ATTR_STRUCK     = 1 << 8, +	ATTR_WRAP       = 1 << 9, +	ATTR_WIDE       = 1 << 10, +	ATTR_WDUMMY     = 1 << 11, +	ATTR_SELECTED   = 1 << 12, +	ATTR_BOXDRAW    = 1 << 11,  	ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, +	ATTR_DIRTYUNDERLINE = 1 << 15,  };  enum selection_mode { @@ -65,6 +69,8 @@ typedef struct {  	ushort mode;      /* attribute flags */  	uint32_t fg;      /* foreground  */  	uint32_t bg;      /* background  */ +	int ustyle;	      /* underline style */ +	int ucolor[3];    /* underline color */  } Glyph;  typedef Glyph *Line; @@ -81,6 +87,8 @@ void die(const char *, ...);  void redraw(void);  void draw(void); +void kscrolldown(const Arg *); +void kscrollup(const Arg *);  void printscreen(const Arg *);  void printsel(const Arg *);  void sendbreak(const Arg *); @@ -88,6 +96,7 @@ void toggleprinter(const Arg *);  int tattrset(int);  void tnew(int, int); +int tisaltscreen(void);  void tresize(int, int);  void tsetdirtattr(int);  void ttyhangup(void); @@ -111,6 +120,14 @@ void *xmalloc(size_t);  void *xrealloc(void *, size_t);  char *xstrdup(const char *); +int isboxdraw(Rune); +ushort boxdrawindex(const Glyph *); +#ifdef XFT_VERSION +/* only exposed to x.c, otherwise we'll need Xft.h for the types */ +void boxdraw_xinit(Display *, Colormap, XftDraw *, Visual *); +void drawboxes(int, int, int, int, XftColor *, XftColor *, const XftGlyphFontSpec *, int); +#endif +  /* config.h globals */  extern char *utmp;  extern char *scroll; @@ -124,3 +141,4 @@ extern unsigned int tabspaces;  extern unsigned int defaultfg;  extern unsigned int defaultbg;  extern unsigned int defaultcs; +extern const int boxdraw, boxdraw_bold, boxdraw_braille; @@ -1,4 +1,5 @@  st-mono| simpleterm monocolor, +	Su,  	acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,  	am,  	bce, @@ -161,7 +162,7 @@ st-mono| simpleterm monocolor,  	rin=\E[%p1%dT,  	ritm=\E[23m,  	rmacs=\E(B, -	rmcup=\E[?1049l, +	rmcup=\E[?1049l\E[23;0;0t,  	rmir=\E[4l,  	rmkx=\E[?1l\E>,  	rmso=\E[27m, @@ -172,7 +173,7 @@ st-mono| simpleterm monocolor,  	sitm=\E[3m,  	sgr0=\E[0m,  	smacs=\E(0, -	smcup=\E[?1049h, +	smcup=\E[?1049h\E[22;0;0t,  	smir=\E[4h,  	smkx=\E[?1h\E=,  	smso=\E[7m, @@ -32,7 +32,9 @@ void xloadcols(void);  int xsetcolorname(int, const char *);  int xgetcolor(int, unsigned char *, unsigned char *, unsigned char *);  void xseticontitle(char *); -void xsettitle(char *); +void xfreetitlestack(void); +void xsettitle(char *, int); +void xpushtitle(void);  int xsetcursor(int);  void xsetmode(int, unsigned int);  void xsetpointermotion(int); @@ -45,6 +45,19 @@ typedef struct {  	signed char appcursor; /* application cursor */  } Key; +/* Undercurl slope types */ +enum undercurl_slope_type { +	UNDERCURL_SLOPE_ASCENDING = 0, +	UNDERCURL_SLOPE_TOP_CAP = 1, +	UNDERCURL_SLOPE_DESCENDING = 2, +	UNDERCURL_SLOPE_BOTTOM_CAP = 3 +}; + +typedef enum { +	PixelGeometry, +	CellGeometry +} Geometry; +  /* X modifiers */  #define XK_ANY_MOD    UINT_MAX  #define XK_NO_MOD     0 @@ -63,6 +76,9 @@ static void ttysend(const Arg *);  /* config.h for applying patches and the configuration. */  #include "config.h" +/* size of title stack */ +#define TITLESTACKSIZE 8 +  /* XEMBED messages */  #define XEMBED_FOCUS_IN  4  #define XEMBED_FOCUS_OUT 5 @@ -220,6 +236,8 @@ static DC dc;  static XWindow xw;  static XSelection xsel;  static TermWindow win; +static int tstki; /* title stack index */ +static char *titlestack[TITLESTACKSIZE]; /* title stack */  /* Font Ring Cache */  enum { @@ -686,6 +704,8 @@ setsel(char *str, Time t)  	XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, t);  	if (XGetSelectionOwner(xw.dpy, XA_PRIMARY) != xw.win)  		selclear(); + +	xclipcopy();  }  void @@ -1127,7 +1147,7 @@ xicdestroy(XIC xim, XPointer client, XPointer call)  }  void -xinit(int cols, int rows) +xinit(int w, int h)  {  	XGCValues gcvalues;  	Cursor cursor; @@ -1152,8 +1172,16 @@ xinit(int cols, int rows)  	xloadcols();  	/* adjust fixed window geometry */ -	win.w = 2 * borderpx + cols * win.cw; -	win.h = 2 * borderpx + rows * win.ch; +	switch (geometry) { +	case CellGeometry: +		win.w = 2 * borderpx + w * win.cw; +		win.h = 2 * borderpx + h * win.ch; +		break; +	case PixelGeometry: +		win.w = w; +		win.h = h; +		break; +	}  	if (xw.gm & XNegative)  		xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2;  	if (xw.gm & YNegative) @@ -1240,6 +1268,8 @@ xinit(int cols, int rows)  	xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0);  	if (xsel.xtarget == None)  		xsel.xtarget = XA_STRING; + +	boxdraw_xinit(xw.dpy, xw.cmap, xw.draw, xw.vis);  }  int @@ -1287,7 +1317,13 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x  		}  		/* Lookup character index with default font. */ -		glyphidx = XftCharIndex(xw.dpy, font->match, rune); +		if (mode & ATTR_BOXDRAW) { +			/* minor shoehorning: boxdraw uses only this ushort */ +			glyphidx = boxdrawindex(&glyphs[i]); +		} else { +			/* Lookup character index with default font. */ +			glyphidx = XftCharIndex(xw.dpy, font->match, rune); +		}  		if (glyphidx) {  			specs[numspecs].font = font->match;  			specs[numspecs].glyph = glyphidx; @@ -1374,6 +1410,51 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x  	return numspecs;  } +static int isSlopeRising (int x, int iPoint, int waveWidth) +{ +	//    .     .     .     . +	//   / \   / \   / \   / \ +	//  /   \ /   \ /   \ /   \ +	// .     .     .     .     . + +	// Find absolute `x` of point +	x += iPoint * (waveWidth/2); + +	// Find index of absolute wave +	int absSlope = x / ((float)waveWidth/2); + +	return (absSlope % 2); +} + +static int getSlope (int x, int iPoint, int waveWidth) +{ +	// Sizes: Caps are half width of slopes +	//    1_2       1_2       1_2      1_2 +	//   /   \     /   \     /   \    /   \ +	//  /     \   /     \   /     \  /     \ +	// 0       3_0       3_0      3_0       3_ +	// <2->    <1>         <---6----> + +	// Find type of first point +	int firstType; +	x -= (x / waveWidth) * waveWidth; +	if (x < (waveWidth * (2.f/6.f))) +		firstType = UNDERCURL_SLOPE_ASCENDING; +	else if (x < (waveWidth * (3.f/6.f))) +		firstType = UNDERCURL_SLOPE_TOP_CAP; +	else if (x < (waveWidth * (5.f/6.f))) +		firstType = UNDERCURL_SLOPE_DESCENDING; +	else +		firstType = UNDERCURL_SLOPE_BOTTOM_CAP; + +	// Find type of given point +	int pointType = (iPoint % 4); +	pointType += firstType; +	pointType %= 4; + +	return pointType; +} +  void  xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)  { @@ -1492,12 +1573,366 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i  	XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1);  	/* Render the glyphs. */ -	XftDrawGlyphFontSpec(xw.draw, fg, specs, len); +	if (base.mode & ATTR_BOXDRAW) { +		drawboxes(winx, winy, width / len, win.ch, fg, bg, specs, len); +	} else { +		/* Render the glyphs. */ +		XftDrawGlyphFontSpec(xw.draw, fg, specs, len); +	}  	/* Render underline and strikethrough. */  	if (base.mode & ATTR_UNDERLINE) { -		XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent * chscale + 1, -				width, 1); +		// Underline Color +		const int widthThreshold  = 28; // +1 width every widthThreshold px of font +		int wlw = (win.ch / widthThreshold) + 1; // Wave Line Width +		int linecolor; +		if ((base.ucolor[0] >= 0) && +			!(base.mode & ATTR_BLINK && win.mode & MODE_BLINK) && +			!(base.mode & ATTR_INVISIBLE) +		) { +			// Special color for underline +			// Index +			if (base.ucolor[1] < 0) { +				linecolor = dc.col[base.ucolor[0]].pixel; +			} +			// RGB +			else { +				XColor lcolor; +				lcolor.red = base.ucolor[0] * 257; +				lcolor.green = base.ucolor[1] * 257; +				lcolor.blue = base.ucolor[2] * 257; +				lcolor.flags = DoRed | DoGreen | DoBlue; +				XAllocColor(xw.dpy, xw.cmap, &lcolor); +				linecolor = lcolor.pixel; +			} +		} else { +			// Foreground color for underline +			linecolor = fg->pixel; +		} + +		XGCValues ugcv = { +			.foreground = linecolor, +			.line_width = wlw, +			.line_style = LineSolid, +			.cap_style = CapNotLast +		}; + +		GC ugc = XCreateGC(xw.dpy, XftDrawDrawable(xw.draw), +			GCForeground | GCLineWidth | GCLineStyle | GCCapStyle, +			&ugcv); + +		// Underline Style +		if (base.ustyle != 3) { +			//XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1, width, 1); +			XFillRectangle(xw.dpy, XftDrawDrawable(xw.draw), ugc, winx, +				winy + dc.font.ascent + 1, width, wlw); +		} else if (base.ustyle == 3) { +			int ww = win.cw;//width; +			int wh = dc.font.descent - wlw/2 - 1;//r.height/7; +			int wx = winx; +			int wy = winy + win.ch - dc.font.descent; + +#if UNDERCURL_STYLE == UNDERCURL_CURLY +			// Draw waves +			int narcs = charlen * 2 + 1; +			XArc *arcs = xmalloc(sizeof(XArc) * narcs); + +			int i = 0; +			for (i = 0; i < charlen-1; i++) { +				arcs[i*2] = (XArc) { +					.x = wx + win.cw * i + ww / 4, +					.y = wy, +					.width = win.cw / 2, +					.height = wh, +					.angle1 = 0, +					.angle2 = 180 * 64 +				}; +				arcs[i*2+1] = (XArc) { +					.x = wx + win.cw * i + ww * 0.75, +					.y = wy, +					.width = win.cw/2, +					.height = wh, +					.angle1 = 180 * 64, +					.angle2 = 180 * 64 +				}; +			} +			// Last wave +			arcs[i*2] = (XArc) {wx + ww * i + ww / 4, wy, ww / 2, wh, +			0, 180 * 64 }; +			// Last wave tail +			arcs[i*2+1] = (XArc) {wx + ww * i + ww * 0.75, wy, ceil(ww / 2.), +			wh, 180 * 64, 90 * 64}; +			// First wave tail +			i++; +			arcs[i*2] = (XArc) {wx - ww/4 - 1, wy, ceil(ww / 2.), wh, 270 * 64, +			90 * 64 }; + +			XDrawArcs(xw.dpy, XftDrawDrawable(xw.draw), ugc, arcs, narcs); + +			free(arcs); +#elif UNDERCURL_STYLE == UNDERCURL_SPIKY +			// Make the underline corridor larger +			/* +			wy -= wh; +			*/ +			wh *= 2; + +			// Set the angle of the slope to 45° +			ww = wh; + +			// Position of wave is independent of word, it's absolute +			wx = (wx / (ww/2)) * (ww/2); + +			int marginStart = winx - wx; + +			// Calculate number of points with floating precision +			float n = width;					// Width of word in pixels +			n = (n / ww) * 2;					// Number of slopes (/ or \) +			n += 2;								// Add two last points +			int npoints = n;					// Convert to int + +			// Total length of underline +			float waveLength = 0; + +			if (npoints >= 3) { +				// We add an aditional slot in case we use a bonus point +				XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1)); + +				// First point (Starts with the word bounds) +				points[0] = (XPoint) { +					.x = wx + marginStart, +					.y = (isSlopeRising(wx, 0, ww)) +						? (wy - marginStart + ww/2.f) +						: (wy + marginStart) +				}; + +				// Second point (Goes back to the absolute point coordinates) +				points[1] = (XPoint) { +					.x = (ww/2.f) - marginStart, +					.y = (isSlopeRising(wx, 1, ww)) +						? (ww/2.f - marginStart) +						: (-ww/2.f + marginStart) +				}; +				waveLength += (ww/2.f) - marginStart; + +				// The rest of the points +				for (int i = 2; i < npoints-1; i++) { +					points[i] = (XPoint) { +						.x = ww/2, +						.y = (isSlopeRising(wx, i, ww)) +							? wh/2 +							: -wh/2 +					}; +					waveLength += ww/2; +				} + +				// Last point +				points[npoints-1] = (XPoint) { +					.x = ww/2, +					.y = (isSlopeRising(wx, npoints-1, ww)) +						? wh/2 +						: -wh/2 +				}; +				waveLength += ww/2; + +				// End +				if (waveLength < width) { // Add a bonus point? +					int marginEnd = width - waveLength; +					points[npoints] = (XPoint) { +						.x = marginEnd, +						.y = (isSlopeRising(wx, npoints, ww)) +							? (marginEnd) +							: (-marginEnd) +					}; + +					npoints++; +				} else if (waveLength > width) { // Is last point too far? +					int marginEnd = waveLength - width; +					points[npoints-1].x -= marginEnd; +					if (isSlopeRising(wx, npoints-1, ww)) +						points[npoints-1].y -= (marginEnd); +					else +						points[npoints-1].y += (marginEnd); +				} + +				// Draw the lines +				XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints, +						CoordModePrevious); + +				// Draw a second underline with an offset of 1 pixel +				if ( ((win.ch / (widthThreshold/2)) % 2)) { +					points[0].x++; + +					XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, +							npoints, CoordModePrevious); +				} + +				// Free resources +				free(points); +			} +#else // UNDERCURL_CAPPED +			// Cap is half of wave width +			float capRatio = 0.5f; + +			// Make the underline corridor larger +			wh *= 2; + +			// Set the angle of the slope to 45° +			ww = wh; +			ww *= 1 + capRatio; // Add a bit of width for the cap + +			// Position of wave is independent of word, it's absolute +			wx = (wx / ww) * ww; + +			float marginStart; +			switch(getSlope(winx, 0, ww)) { +				case UNDERCURL_SLOPE_ASCENDING: +					marginStart = winx - wx; +					break; +				case UNDERCURL_SLOPE_TOP_CAP: +					marginStart = winx - (wx + (ww * (2.f/6.f))); +					break; +				case UNDERCURL_SLOPE_DESCENDING: +					marginStart = winx - (wx + (ww * (3.f/6.f))); +					break; +				case UNDERCURL_SLOPE_BOTTOM_CAP: +					marginStart = winx - (wx + (ww * (5.f/6.f))); +					break; +			} + +			// Calculate number of points with floating precision +			float n = width;					// Width of word in pixels +												//					   ._. +			n = (n / ww) * 4;					// Number of points (./   \.) +			n += 2;								// Add two last points +			int npoints = n;					// Convert to int + +			// Position of the pen to draw the lines +			float penX = 0; +			float penY = 0; + +			if (npoints >= 3) { +				XPoint *points = xmalloc(sizeof(XPoint) * (npoints + 1)); + +				// First point (Starts with the word bounds) +				penX = winx; +				switch (getSlope(winx, 0, ww)) { +					case UNDERCURL_SLOPE_ASCENDING: +						penY = wy + wh/2.f - marginStart; +						break; +					case UNDERCURL_SLOPE_TOP_CAP: +						penY = wy; +						break; +					case UNDERCURL_SLOPE_DESCENDING: +						penY = wy + marginStart; +						break; +					case UNDERCURL_SLOPE_BOTTOM_CAP: +						penY = wy + wh/2.f; +						break; +				} +				points[0].x = penX; +				points[0].y = penY; + +				// Second point (Goes back to the absolute point coordinates) +				switch (getSlope(winx, 1, ww)) { +					case UNDERCURL_SLOPE_ASCENDING: +						penX += ww * (1.f/6.f) - marginStart; +						penY += 0; +						break; +					case UNDERCURL_SLOPE_TOP_CAP: +						penX += ww * (2.f/6.f) - marginStart; +						penY += -wh/2.f + marginStart; +						break; +					case UNDERCURL_SLOPE_DESCENDING: +						penX += ww * (1.f/6.f) - marginStart; +						penY += 0; +						break; +					case UNDERCURL_SLOPE_BOTTOM_CAP: +						penX += ww * (2.f/6.f) - marginStart; +						penY += -marginStart + wh/2.f; +						break; +				} +				points[1].x = penX; +				points[1].y = penY; + +				// The rest of the points +				for (int i = 2; i < npoints; i++) { +					switch (getSlope(winx, i, ww)) { +						case UNDERCURL_SLOPE_ASCENDING: +						case UNDERCURL_SLOPE_DESCENDING: +							penX += ww * (1.f/6.f); +							penY += 0; +							break; +						case UNDERCURL_SLOPE_TOP_CAP: +							penX += ww * (2.f/6.f); +							penY += -wh / 2.f; +							break; +						case UNDERCURL_SLOPE_BOTTOM_CAP: +							penX += ww * (2.f/6.f); +							penY += wh / 2.f; +							break; +					} +					points[i].x = penX; +					points[i].y = penY; +				} + +				// End +				float waveLength = penX - winx; +				if (waveLength < width) { // Add a bonus point? +					int marginEnd = width - waveLength; +					penX += marginEnd; +					switch(getSlope(winx, npoints, ww)) { +						case UNDERCURL_SLOPE_ASCENDING: +						case UNDERCURL_SLOPE_DESCENDING: +							//penY += 0; +							break; +						case UNDERCURL_SLOPE_TOP_CAP: +							penY += -marginEnd; +							break; +						case UNDERCURL_SLOPE_BOTTOM_CAP: +							penY += marginEnd; +							break; +					} + +					points[npoints].x = penX; +					points[npoints].y = penY; + +					npoints++; +				} else if (waveLength > width) { // Is last point too far? +					int marginEnd = waveLength - width; +					points[npoints-1].x -= marginEnd; +					switch(getSlope(winx, npoints-1, ww)) { +						case UNDERCURL_SLOPE_TOP_CAP: +							points[npoints-1].y += marginEnd; +							break; +						case UNDERCURL_SLOPE_BOTTOM_CAP: +							points[npoints-1].y -= marginEnd; +							break; +						default: +							break; +					} +				} + +				// Draw the lines +				XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, npoints, +						CoordModeOrigin); + +				// Draw a second underline with an offset of 1 pixel +				if ( ((win.ch / (widthThreshold/2)) % 2)) { +					for (int i = 0; i < npoints; i++) +						points[i].x++; + +					XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), ugc, points, +							npoints, CoordModeOrigin); +				} + +				// Free resources +				free(points); +			} +#endif +		} + +		XFreeGC(xw.dpy, ugc);  	}  	if (base.mode & ATTR_STRUCK) { @@ -1535,7 +1970,7 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)  	/*  	 * Select the right color for the right mode.  	 */ -	g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE; +	g.mode &= ATTR_BOLD|ATTR_ITALIC|ATTR_UNDERLINE|ATTR_STRUCK|ATTR_WIDE|ATTR_BOXDRAW;  	if (IS_SET(MODE_REVERSE)) {  		g.mode |= ATTR_REVERSE; @@ -1632,10 +2067,30 @@ xseticontitle(char *p)  }  void -xsettitle(char *p) +xfreetitlestack(void)  { -	XTextProperty prop; -	DEFAULT(p, opt_title); +	for (int i = 0; i < LEN(titlestack); i++) { +		free(titlestack[i]); +		titlestack[i] = NULL; +	} +} + +void +xsettitle(char *p, int pop) +{ + 	XTextProperty prop; + +	free(titlestack[tstki]); +	if (pop) { +		titlestack[tstki] = NULL; +		tstki = (tstki - 1 + TITLESTACKSIZE) % TITLESTACKSIZE; +		p = titlestack[tstki] ? titlestack[tstki] : opt_title; +	} else if (p) { +		titlestack[tstki] = xstrdup(p); +	} else { +		titlestack[tstki] = NULL; +		p = opt_title; +	}  	if (p[0] == '\0')  		p = opt_title; @@ -1648,6 +2103,16 @@ xsettitle(char *p)  	XFree(prop.value);  } +void +xpushtitle(void) +{ +	int tstkin = (tstki + 1) % TITLESTACKSIZE; + +	free(titlestack[tstkin]); +	titlestack[tstkin] = titlestack[tstki] ? xstrdup(titlestack[tstki]) : NULL; +	tstki = tstkin; +} +  int  xstartdraw(void)  { @@ -2060,6 +2525,12 @@ main(int argc, char *argv[])  	case 'g':  		xw.gm = XParseGeometry(EARGF(usage()),  				&xw.l, &xw.t, &cols, &rows); +		geometry = CellGeometry; +		break; +	case 'G': +		xw.gm = XParseGeometry(EARGF(usage()), +				&xw.l, &xw.t, &width, &height); +		geometry = PixelGeometry;  		break;  	case 'i':  		xw.isfixed = 1; @@ -2096,10 +2567,19 @@ run:  	setlocale(LC_CTYPE, "");  	XSetLocaleModifiers(""); +	switch (geometry) { +	case CellGeometry: +		xinit(cols, rows); +		break; +	case PixelGeometry: +		xinit(width, height); +		cols = (win.w - 2 * borderpx) / win.cw; +		rows = (win.h - 2 * borderpx) / win.ch; +		break; +	}  	cols = MAX(cols, 1);  	rows = MAX(rows, 1);  	tnew(cols, rows); -	xinit(cols, rows);  	xsetenv();  	selinit();  	run();  | 
