X-Git-Url: https://git.xinqibao.xyz/st.git/blobdiff_plain/93661042a2a0cf6f3655360f5c52215c684a4211..98a1085d0e7c3d84e19185ba666c4b0c725cd974:/st.c?ds=inline

diff --git a/st.c b/st.c
index b148401..097775d 100644
--- a/st.c
+++ b/st.c
@@ -70,14 +70,13 @@ char *argv0;
 #define LEN(a)     (sizeof(a) / sizeof(a)[0])
 #define DEFAULT(a, b)     (a) = (a) ? (a) : (b)
 #define BETWEEN(x, a, b)  ((a) <= (x) && (x) <= (b))
-#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f))
+#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == '\177')
 #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f))
 #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c))
 #define LIMIT(x, a, b)    (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
 #define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || (a).bg != (b).bg)
 #define IS_SET(flag) ((term.mode & (flag)) != 0)
-#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + (t1.tv_usec-t2.tv_usec)/1000)
-#define CEIL(x) (((x) != (int) (x)) ? (x) + 1 : (x))
+#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + (t1.tv_nsec-t2.tv_nsec)/1E6)
 #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
 
 #define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b))
@@ -87,18 +86,19 @@ char *argv0;
 #define TRUEBLUE(x)      (((x) & 0xff) << 8)
 
 
-#define VT102ID "\033[?6c"
-
 enum glyph_attribute {
 	ATTR_NULL      = 0,
-	ATTR_REVERSE   = 1,
-	ATTR_UNDERLINE = 2,
-	ATTR_BOLD      = 4,
-	ATTR_ITALIC    = 8,
-	ATTR_BLINK     = 16,
-	ATTR_WRAP      = 32,
-	ATTR_WIDE      = 64,
-	ATTR_WDUMMY    = 128,
+	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,
 };
 
 enum cursor_movement {
@@ -113,27 +113,27 @@ enum cursor_state {
 };
 
 enum term_mode {
-	MODE_WRAP        = 1,
-	MODE_INSERT      = 2,
-	MODE_APPKEYPAD   = 4,
-	MODE_ALTSCREEN   = 8,
-	MODE_CRLF        = 16,
-	MODE_MOUSEBTN    = 32,
-	MODE_MOUSEMOTION = 64,
-	MODE_REVERSE     = 128,
-	MODE_KBDLOCK     = 256,
-	MODE_HIDE        = 512,
-	MODE_ECHO        = 1024,
-	MODE_APPCURSOR   = 2048,
-	MODE_MOUSESGR    = 4096,
-	MODE_8BIT        = 8192,
-	MODE_BLINK       = 16384,
-	MODE_FBLINK      = 32768,
-	MODE_FOCUS       = 65536,
-	MODE_MOUSEX10    = 131072,
-	MODE_MOUSEMANY   = 262144,
-	MODE_BRCKTPASTE  = 524288,
-	MODE_PRINT       = 1048576,
+	MODE_WRAP        = 1 << 0,
+	MODE_INSERT      = 1 << 1,
+	MODE_APPKEYPAD   = 1 << 2,
+	MODE_ALTSCREEN   = 1 << 3,
+	MODE_CRLF        = 1 << 4,
+	MODE_MOUSEBTN    = 1 << 5,
+	MODE_MOUSEMOTION = 1 << 6,
+	MODE_REVERSE     = 1 << 7,
+	MODE_KBDLOCK     = 1 << 8,
+	MODE_HIDE        = 1 << 9,
+	MODE_ECHO        = 1 << 10,
+	MODE_APPCURSOR   = 1 << 11,
+	MODE_MOUSESGR    = 1 << 12,
+	MODE_8BIT        = 1 << 13,
+	MODE_BLINK       = 1 << 14,
+	MODE_FBLINK      = 1 << 15,
+	MODE_FOCUS       = 1 << 16,
+	MODE_MOUSEX10    = 1 << 17,
+	MODE_MOUSEMANY   = 1 << 18,
+	MODE_BRCKTPASTE  = 1 << 19,
+	MODE_PRINT       = 1 << 20,
 	MODE_MOUSE       = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\
 	                  |MODE_MOUSEMANY,
 };
@@ -294,8 +294,8 @@ typedef struct {
 	char *clip;
 	Atom xtarget;
 	bool alt;
-	struct timeval tclick1;
-	struct timeval tclick2;
+	struct timespec tclick1;
+	struct timespec tclick2;
 } Selection;
 
 typedef union {
@@ -356,6 +356,7 @@ static void csidump(void);
 static void csihandle(void);
 static void csiparse(void);
 static void csireset(void);
+static int eschandle(uchar ascii);
 static void strdump(void);
 static void strhandle(void);
 static void strparse(void);
@@ -372,6 +373,7 @@ static void tdeletechar(int);
 static void tdeleteline(int);
 static void tinsertblank(int);
 static void tinsertblankline(int);
+static int tlinelen(int);
 static void tmoveto(int, int);
 static void tmoveato(int, int);
 static void tnew(int, int);
@@ -379,7 +381,7 @@ static void tnewline(int);
 static void tputtab(int);
 static void tputc(char *, int);
 static void treset(void);
-static int tresize(int, int);
+static void tresize(int, int);
 static void tscrollup(int, int);
 static void tscrolldown(int, int);
 static void tsetattr(int *, int);
@@ -401,6 +403,7 @@ static void ttyread(void);
 static void ttyresize(void);
 static void ttysend(char *, size_t);
 static void ttywrite(const char *, size_t);
+static void tstrsequence(uchar c);
 
 static void xdraws(char *, Glyph, int, int, int, int);
 static void xhints(void);
@@ -659,7 +662,10 @@ y2row(int y) {
 static int tlinelen(int y) {
 	int i = term.col;
 
-	while (i > 0 && term.line[y][i - 1].c[0] == ' ')
+	if(term.line[y][i - 1].mode & ATTR_WRAP)
+		return i;
+
+	while(i > 0 && term.line[y][i - 1].c[0] == ' ')
 		--i;
 
 	return i;
@@ -679,6 +685,9 @@ selnormalize(void) {
 	sel.nb.y = MIN(sel.ob.y, sel.oe.y);
 	sel.ne.y = MAX(sel.ob.y, sel.oe.y);
 
+	selsnap(sel.snap, &sel.nb.x, &sel.nb.y, -1);
+	selsnap(sel.snap, &sel.ne.x, &sel.ne.y, +1);
+
 	/* expand selection over line breaks */
 	if (sel.type == SEL_RECTANGULAR)
 		return;
@@ -703,7 +712,8 @@ selected(int x, int y) {
 void
 selsnap(int mode, int *x, int *y, int direction) {
 	int newx, newy, xt, yt;
-	Glyph *gp;
+	bool delim, prevdelim;
+	Glyph *gp, *prevgp;
 
 	switch(mode) {
 	case SNAP_WORD:
@@ -711,6 +721,8 @@ selsnap(int mode, int *x, int *y, int direction) {
 		 * Snap around if the word wraps around at the end or
 		 * beginning of a line.
 		 */
+		prevgp = &term.line[*y][*x];
+		prevdelim = strchr(worddelimiters, prevgp->c[0]) != NULL;
 		for(;;) {
 			newx = *x + direction;
 			newy = *y;
@@ -732,11 +744,15 @@ selsnap(int mode, int *x, int *y, int direction) {
 				break;
 
 			gp = &term.line[newy][newx];
-			if (!(gp->mode & ATTR_WDUMMY) && strchr(worddelimiters, gp->c[0]))
+			delim = strchr(worddelimiters, gp->c[0]) != NULL;
+			if(!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
+					|| (delim && gp->c[0] != prevgp->c[0])))
 				break;
 
 			*x = newx;
 			*y = newy;
+			prevgp = gp;
+			prevdelim = delim;
 		}
 		break;
 	case SNAP_LINE:
@@ -774,15 +790,6 @@ getbuttoninfo(XEvent *e) {
 
 	sel.oe.x = x2col(e->xbutton.x);
 	sel.oe.y = y2row(e->xbutton.y);
-
-	if(sel.ob.y < sel.oe.y
-			|| (sel.ob.y == sel.oe.y && sel.ob.x < sel.oe.x)) {
-		selsnap(sel.snap, &sel.ob.x, &sel.ob.y, -1);
-		selsnap(sel.snap, &sel.oe.x, &sel.oe.y, +1);
-	} else {
-		selsnap(sel.snap, &sel.oe.x, &sel.oe.y, -1);
-		selsnap(sel.snap, &sel.ob.x, &sel.ob.y, +1);
-	}
 	selnormalize();
 
 	sel.type = SEL_REGULAR;
@@ -860,7 +867,7 @@ mousereport(XEvent *e) {
 
 void
 bpress(XEvent *e) {
-	struct timeval now;
+	struct timespec now;
 	Mousekey *mk;
 
 	if(IS_SET(MODE_MOUSE) && !(e->xbutton.state & forceselmod)) {
@@ -877,7 +884,7 @@ bpress(XEvent *e) {
 	}
 
 	if(e->xbutton.button == Button1) {
-		gettimeofday(&now, NULL);
+		clock_gettime(CLOCK_MONOTONIC, &now);
 
 		/* Clear previous selection, logically and visually. */
 		selclear(NULL);
@@ -897,8 +904,6 @@ bpress(XEvent *e) {
 		} else {
 			sel.snap = 0;
 		}
-		selsnap(sel.snap, &sel.ob.x, &sel.ob.y, -1);
-		selsnap(sel.snap, &sel.oe.x, &sel.oe.y, +1);
 		selnormalize();
 
 		/*
@@ -917,7 +922,7 @@ bpress(XEvent *e) {
 char *
 getsel(void) {
 	char *str, *ptr;
-	int x, y, bufsize, size, ex;
+	int y, bufsize, size, lastx, linelen;
 	Glyph *gp, *last;
 
 	if(sel.ob.x == -1)
@@ -928,16 +933,19 @@ getsel(void) {
 
 	/* append every set & selected glyph to the selection */
 	for(y = sel.nb.y; y < sel.ne.y + 1; y++) {
-		gp = &term.line[y][0];
-		last = &gp[term.col-1];
+		linelen = tlinelen(y);
 
-		while(last >= gp && !(selected(last - gp, y) &&
-				      strcmp(last->c, " ") != 0)) {
-			--last;
+		if(sel.type == SEL_RECTANGULAR) {
+			gp = &term.line[y][sel.nb.x];
+			lastx = sel.ne.x;
+		} else {
+			gp = &term.line[y][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)];
 
-		for(x = 0; gp <= last; x++, ++gp) {
-			if(!selected(x, y) || (gp->mode & ATTR_WDUMMY))
+		for( ; gp <= last; ++gp) {
+			if(gp->mode & ATTR_WDUMMY)
 				continue;
 
 			size = utf8len(gp->c);
@@ -954,20 +962,8 @@ getsel(void) {
 		 * st.
 		 * FIXME: Fix the computer world.
 		 */
-		if(y < sel.ne.y && !(x > 0 && (gp-1)->mode & ATTR_WRAP))
+		if((y < sel.ne.y || lastx >= linelen) && !(last->mode & ATTR_WRAP))
 			*ptr++ = '\n';
-
-		/*
-		 * If the last selected line expands in the selection
-		 * after the visible text '\n' is appended.
-		 */
-		if(y == sel.ne.y) {
-			ex = sel.ne.x;
-			if(sel.nb.y == sel.ne.y && sel.ne.x < sel.nb.x)
-				ex = sel.nb.x;
-			if(tlinelen(y) < ex)
-				*ptr++ = '\n';
-		}
 	}
 	*ptr = 0;
 	return str;
@@ -1146,23 +1142,29 @@ die(const char *errstr, ...) {
 
 void
 execsh(void) {
-	char **args;
-	char *envshell = getenv("SHELL");
-	const struct passwd *pass = getpwuid(getuid());
+	char **args, *sh;
+	const struct passwd *pw;
 	char buf[sizeof(long) * 8 + 1];
 
+	errno = 0;
+	if((pw = getpwuid(getuid())) == NULL) {
+		if(errno)
+			die("getpwuid:%s\n", strerror(errno));
+		else
+			die("who are you?\n");
+	}
 	unsetenv("COLUMNS");
 	unsetenv("LINES");
 	unsetenv("TERMCAP");
 
-	if(pass) {
-		setenv("LOGNAME", pass->pw_name, 1);
-		setenv("USER", pass->pw_name, 1);
-		setenv("SHELL", pass->pw_shell, 0);
-		setenv("HOME", pass->pw_dir, 0);
-	}
-
+	sh = (pw->pw_shell[0]) ? pw->pw_shell : shell;
 	snprintf(buf, sizeof(buf), "%lu", xw.win);
+
+	setenv("LOGNAME", pw->pw_name, 1);
+	setenv("USER", pw->pw_name, 1);
+	setenv("SHELL", sh, 1);
+	setenv("HOME", pw->pw_dir, 1);
+	setenv("TERM", termname, 1);
 	setenv("WINDOWID", buf, 1);
 
 	signal(SIGCHLD, SIG_DFL);
@@ -1172,25 +1174,22 @@ execsh(void) {
 	signal(SIGTERM, SIG_DFL);
 	signal(SIGALRM, SIG_DFL);
 
-	DEFAULT(envshell, shell);
-	setenv("TERM", termname, 1);
-	args = opt_cmd ? opt_cmd : (char *[]){envshell, "-i", NULL};
+	args = opt_cmd ? opt_cmd : (char *[]){sh, "-i", NULL};
 	execvp(args[0], args);
 	exit(EXIT_FAILURE);
 }
 
 void
 sigchld(int a) {
-	int stat = 0;
+	int stat, ret;
 
 	if(waitpid(pid, &stat, 0) < 0)
 		die("Waiting for pid %hd failed: %s\n", pid, strerror(errno));
 
-	if(WIFEXITED(stat)) {
-		exit(WEXITSTATUS(stat));
-	} else {
-		exit(EXIT_FAILURE);
-	}
+	ret = WIFEXITED(stat) ? WEXITSTATUS(stat) : EXIT_FAILURE;
+	if (ret != EXIT_SUCCESS)
+		die("child finished with error '%d'\n", stat);
+	exit(EXIT_SUCCESS);
 }
 
 void
@@ -1364,9 +1363,12 @@ treset(void) {
 	memset(term.trantbl, sizeof(term.trantbl), CS_USA);
 	term.charset = 0;
 
-	tclearregion(0, 0, term.col-1, term.row-1);
-	tmoveto(0, 0);
-	tcursor(CURSOR_SAVE);
+	for(i = 0; i < 2; i++) {
+		tmoveto(0, 0);
+		tcursor(CURSOR_SAVE);
+		tclearregion(0, 0, term.col-1, term.row-1);
+		tswapscreen();
+	}
 }
 
 void
@@ -1559,6 +1561,7 @@ tsetchar(char *c, Glyph *attr, int x, int y) {
 void
 tclearregion(int x1, int y1, int x2, int y2) {
 	int x, y, temp;
+	Glyph *gp;
 
 	if(x1 > x2)
 		temp = x1, x1 = x2, x2 = temp;
@@ -1573,10 +1576,13 @@ tclearregion(int x1, int y1, int x2, int y2) {
 	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(NULL);
-			term.line[y][x] = term.c.attr;
-			memcpy(term.line[y][x].c, " ", 2);
+			gp->fg = term.c.attr.fg;
+			gp->bg = term.c.attr.bg;
+			gp->mode = 0;
+			memcpy(gp->c, " ", 2);
 		}
 	}
 }
@@ -1682,15 +1688,24 @@ tsetattr(int *attr, int l) {
 	for(i = 0; i < l; i++) {
 		switch(attr[i]) {
 		case 0:
-			term.c.attr.mode &= ~(ATTR_REVERSE | ATTR_UNDERLINE \
-					| ATTR_BOLD | ATTR_ITALIC \
-					| ATTR_BLINK);
+			term.c.attr.mode &= ~(
+				ATTR_BOLD       |
+				ATTR_FAINT      |
+				ATTR_ITALIC     |
+				ATTR_UNDERLINE  |
+				ATTR_BLINK      |
+				ATTR_REVERSE    |
+				ATTR_INVISIBLE  |
+				ATTR_STRUCK     );
 			term.c.attr.fg = defaultfg;
 			term.c.attr.bg = defaultbg;
 			break;
 		case 1:
 			term.c.attr.mode |= ATTR_BOLD;
 			break;
+		case 2:
+			term.c.attr.mode |= ATTR_FAINT;
+			break;
 		case 3:
 			term.c.attr.mode |= ATTR_ITALIC;
 			break;
@@ -1698,15 +1713,21 @@ tsetattr(int *attr, int l) {
 			term.c.attr.mode |= ATTR_UNDERLINE;
 			break;
 		case 5: /* slow blink */
+			/* FALLTHROUGH */
 		case 6: /* rapid blink */
 			term.c.attr.mode |= ATTR_BLINK;
 			break;
 		case 7:
 			term.c.attr.mode |= ATTR_REVERSE;
 			break;
-		case 21:
+		case 8:
+			term.c.attr.mode |= ATTR_INVISIBLE;
+			break;
+		case 9:
+			term.c.attr.mode |= ATTR_STRUCK;
+			break;
 		case 22:
-			term.c.attr.mode &= ~ATTR_BOLD;
+			term.c.attr.mode &= ~(ATTR_BOLD | ATTR_FAINT);
 			break;
 		case 23:
 			term.c.attr.mode &= ~ATTR_ITALIC;
@@ -1715,12 +1736,17 @@ tsetattr(int *attr, int l) {
 			term.c.attr.mode &= ~ATTR_UNDERLINE;
 			break;
 		case 25:
-		case 26:
 			term.c.attr.mode &= ~ATTR_BLINK;
 			break;
 		case 27:
 			term.c.attr.mode &= ~ATTR_REVERSE;
 			break;
+		case 28:
+			term.c.attr.mode &= ~ATTR_INVISIBLE;
+			break;
+		case 29:
+			term.c.attr.mode &= ~ATTR_STRUCK;
+			break;
 		case 38:
 			if ((idx = tdefcolor(attr, &i, l)) >= 0)
 				term.c.attr.fg = idx;
@@ -1947,7 +1973,7 @@ csihandle(void) {
 		break;
 	case 'c': /* DA -- Device Attributes */
 		if(csiescseq.arg[0] == 0)
-			ttywrite(VT102ID, sizeof(VT102ID) - 1);
+			ttywrite(vtiden, sizeof(vtiden) - 1);
 		break;
 	case 'C': /* CUF -- Cursor <n> Forward */
 	case 'a': /* HPR -- Cursor <n> Forward */
@@ -2256,12 +2282,10 @@ tdumpline(int n) {
 	Glyph *bp, *end;
 
 	bp = &term.line[n][0];
-	end = &bp[term.col-1];
-	while(end > bp && !strcmp(" ", end->c))
-		--end;
-	if(bp != end || strcmp(bp->c, " ")) {
+	end = &bp[MIN(tlinelen(n), term.col) - 1];
+	if(bp != end || bp->c[0] != ' ') {
 		for( ;bp <= end; ++bp)
-			tprinter(bp->c, strlen(bp->c));
+			tprinter(bp->c, utf8len(bp->c));
 	}
 	tprinter("\n", 1);
 }
@@ -2295,13 +2319,13 @@ techo(char *buf, int len) {
 	for(; len > 0; buf++, len--) {
 		char c = *buf;
 
-		if(ISCONTROL(c)) { /* control code */
+		if(ISCONTROL((uchar) c)) { /* control code */
 			if(c & 0x80) {
 				c &= 0x7f;
 				tputc("^", 1);
 				tputc("[", 1);
 			} else if(c != '\n' && c != '\r' && c != '\t') {
-				c ^= '\x40';
+				c ^= 0x40;
 				tputc("^", 1);
 			}
 			tputc(&c, 1);
@@ -2319,10 +2343,48 @@ tdeftran(char ascii) {
 	static int vcs[] = {CS_GRAPHIC0, CS_USA};
 	char *p;
 
-	if((p = strchr(cs, ascii)) == NULL)
+	if((p = strchr(cs, ascii)) == NULL) {
 		fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii);
-	else
+	} else {
 		term.trantbl[term.icharset] = vcs[p - cs];
+	}
+}
+
+void
+tdectest(char c) {
+	static char E[UTF_SIZ] = "E";
+	int x, y;
+
+	if(c == '8') { /* DEC screen alignment test. */
+		for(x = 0; x < term.col; ++x) {
+			for(y = 0; y < term.row; ++y)
+				tsetchar(E, &term.c.attr, x, y);
+		}
+	}
+}
+
+void
+tstrsequence(uchar c) {
+	if (c & 0x80) {
+		switch (c) {
+		case 0x90:   /* DCS -- Device Control String */
+			c = 'P';
+			break;
+		case 0x9f:   /* APC -- Application Program Command */
+			c = '_';
+			break;
+		case 0x9e:   /* PM -- Privacy Message */
+			c = '^';
+			break;
+		case 0x9d:   /* OSC -- Operating System Command */
+			c = ']';
+			break;
+		}
+	}
+	strreset();
+	strescseq.type = c;
+	term.esc |= ESC_STR;
+	return;
 }
 
 void
@@ -2379,37 +2441,113 @@ tcontrolcode(uchar ascii) {
 	case 0177:   /* DEL (IGNORED) */
 		return;
 	case 0x84:   /* TODO: IND */
-	case 0x85:   /* TODO: NEL */
-	case 0x88:   /* TODO: HTS */
+		break;
+	case 0x85:   /* NEL -- Next line */
+		tnewline(1); /* always go to first col */
+		break;
+	case 0x88:   /* HTS -- Horizontal tab stop */
+		term.tabs[term.c.x] = 1;
+		break;
 	case 0x8d:   /* TODO: RI */
 	case 0x8e:   /* TODO: SS2 */
 	case 0x8f:   /* TODO: SS3 */
-	case 0x90:   /* TODO: DCS */
 	case 0x98:   /* TODO: SOS */
-	case 0x9a:   /* TODO: DECID */
+		break;
+	case 0x9a:   /* DECID -- Identify Terminal */
+		ttywrite(vtiden, sizeof(vtiden) - 1);
+		break;
 	case 0x9b:   /* TODO: CSI */
 	case 0x9c:   /* TODO: ST */
-	case 0x9d:   /* TODO: OSC */
-	case 0x9e:   /* TODO: PM */
-	case 0x9f:   /* TODO: APC */
 		break;
+	case 0x90:   /* DCS -- Device Control String */
+	case 0x9f:   /* APC -- Application Program Command */
+	case 0x9e:   /* PM -- Privacy Message */
+	case 0x9d:   /* OSC -- Operating System Command */
+		tstrsequence(ascii);
+		return;
 	}
 	/* only CAN, SUB, \a and C1 chars interrupt a sequence */
 	term.esc &= ~(ESC_STR_END|ESC_STR);
 	return;
 }
 
-void
-tdectest(char c) {
-	static char E[UTF_SIZ] = "E";
-	int x, y;
-
-	if(c == '8') { /* DEC screen alignment test. */
-		for(x = 0; x < term.col; ++x) {
-			for(y = 0; y < term.row; ++y)
-				tsetchar(E, &term.c.attr, x, y);
+/*
+ * returns 1 when the sequence is finished and it hasn't to read
+ * more characters for this sequence, otherwise 0
+ */
+int
+eschandle(uchar ascii) {
+	switch(ascii) {
+	case '[':
+		term.esc |= ESC_CSI;
+		return 0;
+	case '#':
+		term.esc |= ESC_TEST;
+		return 0;
+	case 'P': /* DCS -- Device Control String */
+	case '_': /* APC -- Application Program Command */
+	case '^': /* PM -- Privacy Message */
+	case ']': /* OSC -- Operating System Command */
+	case 'k': /* old title set compatibility */
+		tstrsequence(ascii);
+		return 0;
+	case '(': /* set primary charset G0 */
+	case ')': /* set secondary charset G1 */
+	case '*': /* set tertiary charset G2 */
+	case '+': /* set quaternary charset G3 */
+		term.icharset = ascii - '(';
+		term.esc |= ESC_ALTCHARSET;
+		return 0;
+	case 'D': /* IND -- Linefeed */
+		if(term.c.y == term.bot) {
+			tscrollup(term.top, 1);
+		} else {
+			tmoveto(term.c.x, term.c.y+1);
+		}
+		break;
+	case 'E': /* NEL -- Next line */
+		tnewline(1); /* always go to first col */
+		break;
+	case 'H': /* HTS -- Horizontal tab stop */
+		term.tabs[term.c.x] = 1;
+		break;
+	case 'M': /* RI -- Reverse index */
+		if(term.c.y == term.top) {
+			tscrolldown(term.top, 1);
+		} else {
+			tmoveto(term.c.x, term.c.y-1);
 		}
+		break;
+	case 'Z': /* DECID -- Identify Terminal */
+		ttywrite(vtiden, sizeof(vtiden) - 1);
+		break;
+	case 'c': /* RIS -- Reset to inital state */
+		treset();
+		xresettitle();
+		xloadcols();
+		break;
+	case '=': /* DECPAM -- Application keypad */
+		term.mode |= MODE_APPKEYPAD;
+		break;
+	case '>': /* DECPNM -- Normal keypad */
+		term.mode &= ~MODE_APPKEYPAD;
+		break;
+	case '7': /* DECSC -- Save Cursor */
+		tcursor(CURSOR_SAVE);
+		break;
+	case '8': /* DECRC -- Restore Cursor */
+		tcursor(CURSOR_LOAD);
+		break;
+	case '\\': /* ST -- String Terminator */
+		if(term.esc & ESC_STR_END)
+			strhandle();
+		break;
+	default:
+		fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n",
+			(uchar) ascii, isprint(ascii)? ascii:'.');
+		break;
 	}
+	return 1;
 }
 
 void
@@ -2496,78 +2634,9 @@ tputc(char *c, int len) {
 		} else if(term.esc & ESC_TEST) {
 			tdectest(ascii);
 		} else {
-			switch(ascii) {
-			case '[':
-				term.esc |= ESC_CSI;
-				return;
-			case '#':
-				term.esc |= ESC_TEST;
-				return;
-			case 'P': /* DCS -- Device Control String */
-			case '_': /* APC -- Application Program Command */
-			case '^': /* PM -- Privacy Message */
-			case ']': /* OSC -- Operating System Command */
-			case 'k': /* old title set compatibility */
-				strreset();
-				strescseq.type = ascii;
-				term.esc |= ESC_STR;
-				return;
-			case '(': /* set primary charset G0 */
-			case ')': /* set secondary charset G1 */
-			case '*': /* set tertiary charset G2 */
-			case '+': /* set quaternary charset G3 */
-				term.icharset = ascii - '(';
-				term.esc |= ESC_ALTCHARSET;
+			if (!eschandle(ascii))
 				return;
-			case 'D': /* IND -- Linefeed */
-				if(term.c.y == term.bot) {
-					tscrollup(term.top, 1);
-				} else {
-					tmoveto(term.c.x, term.c.y+1);
-				}
-				break;
-			case 'E': /* NEL -- Next line */
-				tnewline(1); /* always go to first col */
-				break;
-			case 'H': /* HTS -- Horizontal tab stop */
-				term.tabs[term.c.x] = 1;
-				break;
-			case 'M': /* RI -- Reverse index */
-				if(term.c.y == term.top) {
-					tscrolldown(term.top, 1);
-				} else {
-					tmoveto(term.c.x, term.c.y-1);
-				}
-				break;
-			case 'Z': /* DECID -- Identify Terminal */
-				ttywrite(VT102ID, sizeof(VT102ID) - 1);
-				break;
-			case 'c': /* RIS -- Reset to inital state */
-				treset();
-				xresettitle();
-				xloadcols();
-				break;
-			case '=': /* DECPAM -- Application keypad */
-				term.mode |= MODE_APPKEYPAD;
-				break;
-			case '>': /* DECPNM -- Normal keypad */
-				term.mode &= ~MODE_APPKEYPAD;
-				break;
-			case '7': /* DECSC -- Save Cursor */
-				tcursor(CURSOR_SAVE);
-				break;
-			case '8': /* DECRC -- Restore Cursor */
-				tcursor(CURSOR_LOAD);
-				break;
-			case '\\': /* ST -- String Terminator */
-				if(term.esc & ESC_STR_END)
-					strhandle();
-				break;
-			default:
-				fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n",
-					(uchar) ascii, isprint(ascii)? ascii:'.');
-				break;
-			}
+			/* sequence already finished */
 		}
 		term.esc = 0;
 		/*
@@ -2607,18 +2676,20 @@ tputc(char *c, int len) {
 	}
 }
 
-int
+void
 tresize(int col, int row) {
 	int i;
 	int minrow = MIN(row, term.row);
 	int mincol = MIN(col, term.col);
 	int slide = term.c.y - row + 1;
 	bool *bp;
-	Line *orig;
 	TCursor c;
 
-	if(col < 1 || row < 1)
-		return 0;
+	if(col < 1 || row < 1) {
+		fprintf(stderr,
+		        "tresize: error resizing to %dx%d\n", col, row);
+		return;
+	}
 
 	/* free unneeded rows */
 	i = 0;
@@ -2648,14 +2719,12 @@ tresize(int col, int row) {
 
 	/* resize each row to new width, zero-pad if needed */
 	for(i = 0; i < minrow; i++) {
-		term.dirty[i] = 1;
 		term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
 		term.alt[i]  = xrealloc(term.alt[i],  col * sizeof(Glyph));
 	}
 
 	/* allocate any new rows */
 	for(/* i == minrow */; i < row; i++) {
-		term.dirty[i] = 1;
 		term.line[i] = xmalloc(col * sizeof(Glyph));
 		term.alt[i] = xmalloc(col * sizeof(Glyph));
 	}
@@ -2675,10 +2744,9 @@ tresize(int col, int row) {
 	tsetscroll(0, row-1);
 	/* make use of the LIMIT in tmoveto */
 	tmoveto(term.c.x, term.c.y);
-	/* Clearing both screens */
-	orig = term.line;
+	/* Clearing both screens (it makes dirty all lines) */
 	c = term.c;
-	do {
+	for(i = 0; i < 2; i++) {
 		if(mincol < col && 0 < minrow) {
 			tclearregion(mincol, 0, col - 1, minrow - 1);
 		}
@@ -2687,10 +2755,8 @@ tresize(int col, int row) {
 		}
 		tswapscreen();
 		tcursor(CURSOR_LOAD);
-	} while(orig != term.line);
+	}
 	term.c = c;
-
-	return (slide > 0);
 }
 
 void
@@ -2891,6 +2957,7 @@ xloadfonts(char *fontstr, double fontsize) {
 	FcPattern *pattern;
 	FcResult r_sz, r_psz;
 	double fontval;
+	float ceilf(float);
 
 	if(fontstr[0] == '-') {
 		pattern = XftXlfdParse(fontstr, False, False);
@@ -2936,8 +3003,8 @@ xloadfonts(char *fontstr, double fontsize) {
 	}
 
 	/* Setting character width and height. */
-	xw.cw = CEIL(dc.font.width * cwscale);
-	xw.ch = CEIL(dc.font.height * chscale);
+	xw.cw = ceilf(dc.font.width * cwscale);
+	xw.ch = ceilf(dc.font.height * chscale);
 
 	FcPatternDel(pattern, FC_SLANT);
 	FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
@@ -2992,6 +3059,7 @@ xzoom(const Arg *arg) {
 	xloadfonts(usedfont, usedfontsize + arg->i);
 	cresize(0, 0);
 	redraw(0);
+	xhints();
 }
 
 void
@@ -3155,7 +3223,7 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
 		 * change basic system colors [0-7]
 		 * to bright system colors [8-15]
 		 */
-		if(BETWEEN(base.fg, 0, 7))
+		if(BETWEEN(base.fg, 0, 7) && !(base.mode & ATTR_FAINT))
 			fg = &dc.col[base.fg + 8];
 
 		if(base.mode & ATTR_ITALIC) {
@@ -3199,9 +3267,20 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
 		bg = temp;
 	}
 
+	if(base.mode & ATTR_FAINT && !(base.mode & ATTR_BOLD)) {
+		colfg.red = fg->color.red / 2;
+		colfg.green = fg->color.green / 2;
+		colfg.blue = fg->color.blue / 2;
+		XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &revfg);
+		fg = &revfg;
+	}
+
 	if(base.mode & ATTR_BLINK && term.mode & MODE_BLINK)
 		fg = bg;
 
+	if(base.mode & ATTR_INVISIBLE)
+		fg = bg;
+
 	/* Intelligent cleaning up of the borders. */
 	if(x == 0) {
 		xclear(0, (y == 0)? 0 : winy, borderpx,
@@ -3244,28 +3323,22 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
 			bytelen -= u8cblen;
 
 			doesexist = XftCharExists(xw.dpy, font->match, unicodep);
-			if(oneatatime || !doesexist || bytelen <= 0) {
-				if(oneatatime || bytelen <= 0) {
-					if(doesexist) {
-						u8fl++;
-						u8fblen += u8cblen;
-					}
-				}
-
-				if(u8fl > 0) {
-					XftDrawStringUtf8(xw.draw, fg,
-							font->match, xp,
-							winy + font->ascent,
-							(FcChar8 *)u8fs,
-							u8fblen);
-					xp += xw.cw * u8fl;
-
-				}
-				break;
+			if(doesexist) {
+					u8fl++;
+					u8fblen += u8cblen;
+					if(!oneatatime && bytelen > 0)
+							continue;
 			}
 
-			u8fl++;
-			u8fblen += u8cblen;
+			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)
@@ -3352,6 +3425,11 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
 				width, 1);
 	}
 
+	if(base.mode & ATTR_STRUCK) {
+		XftDrawRect(xw.draw, fg, winx, winy + 2 * font->ascent / 3,
+				width, 1);
+	}
+
 	/* Reset clip to none. */
 	XftDrawSetClip(xw.draw, 0);
 }
@@ -3713,11 +3791,14 @@ run(void) {
 	int w = xw.w, h = xw.h;
 	fd_set rfd;
 	int xfd = XConnectionNumber(xw.dpy), xev, blinkset = 0, dodraw = 0;
-	struct timeval drawtimeout, *tv = NULL, now, last, lastblink;
+	struct timespec drawtimeout, *tv = NULL, now, last, lastblink;
+	long deltatime;
 
 	/* Waiting for window mapping */
 	while(1) {
 		XNextEvent(xw.dpy, &ev);
+		if(XFilterEvent(&ev, None))
+			continue;
 		if(ev.type == ConfigureNotify) {
 			w = ev.xconfigure.width;
 			h = ev.xconfigure.height;
@@ -3729,17 +3810,15 @@ run(void) {
 	ttynew();
 	cresize(w, h);
 
-	gettimeofday(&last, NULL);
+	clock_gettime(CLOCK_MONOTONIC, &last);
 	lastblink = last;
 
 	for(xev = actionfps;;) {
-		long deltatime;
-
 		FD_ZERO(&rfd);
 		FD_SET(cmdfd, &rfd);
 		FD_SET(xfd, &rfd);
 
-		if(select(MAX(xfd, cmdfd)+1, &rfd, NULL, NULL, tv) < 0) {
+		if(pselect(MAX(xfd, cmdfd)+1, &rfd, NULL, NULL, tv, NULL) < 0) {
 			if(errno == EINTR)
 				continue;
 			die("select failed: %s\n", strerror(errno));
@@ -3756,9 +3835,9 @@ run(void) {
 		if(FD_ISSET(xfd, &rfd))
 			xev = actionfps;
 
-		gettimeofday(&now, NULL);
+		clock_gettime(CLOCK_MONOTONIC, &now);
 		drawtimeout.tv_sec = 0;
-		drawtimeout.tv_usec = (1000/xfps) * 1000;
+		drawtimeout.tv_nsec = (1000/xfps) * 1E6;
 		tv = &drawtimeout;
 
 		dodraw = 0;
@@ -3793,9 +3872,9 @@ run(void) {
 				if(blinkset) {
 					if(TIMEDIFF(now, lastblink) \
 							> blinktimeout) {
-						drawtimeout.tv_usec = 1;
+						drawtimeout.tv_nsec = 1000;
 					} else {
-						drawtimeout.tv_usec = (1000 * \
+						drawtimeout.tv_nsec = (1E6 * \
 							(blinktimeout - \
 							TIMEDIFF(now,
 								lastblink)));
@@ -3811,8 +3890,8 @@ run(void) {
 void
 usage(void) {
 	die("%s " VERSION " (c) 2010-2014 st engineers\n" \
-	"usage: st [-a] [-v] [-c class] [-f font] [-g geometry] [-o file]" \
-	" [-t title] [-w windowid] [-e command ...]\n", argv0);
+	"usage: st [-a] [-v] [-c class] [-f font] [-g geometry] [-o file]\n"
+	"          [-i] [-t title] [-w windowid] [-e command ...]\n", argv0);
 }
 
 int