X-Git-Url: https://git.xinqibao.xyz/st.git/blobdiff_plain/91fa81dbac4f09de234921cdf5afacb8c8295919..c092bce366ce7469e92f59c889b31fa9037976b2:/st.c

diff --git a/st.c b/st.c
index 5b98c6f..f3b0b08 100644
--- a/st.c
+++ b/st.c
@@ -36,7 +36,8 @@
 
 #define USAGE \
 	"st " VERSION " (c) 2010-2012 st engineers\n" \
-	"usage: st [-t title] [-c class] [-w windowid] [-v] [-e command...]\n"
+	"usage: st [-t title] [-c class] [-g geometry]" \
+	" [-w windowid] [-v] [-f file] [-e command...]\n"
 
 /* XEMBED messages */
 #define XEMBED_FOCUS_IN  4
@@ -54,6 +55,7 @@
 
 #define SELECT_TIMEOUT (20*1000) /* 20 ms */
 #define DRAW_TIMEOUT  (20*1000) /* 20 ms */
+#define REDRAW_TIMEOUT (80*1000) /* 80 ms */
 
 #define SERRNO strerror(errno)
 #define MIN(a, b)  ((a) < (b) ? (a) : (b))
@@ -74,6 +76,7 @@ enum glyph_attribute {
 	ATTR_UNDERLINE = 2,
 	ATTR_BOLD      = 4,
 	ATTR_GFX       = 8,
+	ATTR_ITALIC    = 16,
 };
 
 enum cursor_movement {
@@ -97,11 +100,11 @@ enum glyph_state {
 };
 
 enum term_mode {
-	MODE_WRAP        = 1,
+	MODE_WRAP	= 1,
 	MODE_INSERT      = 2,
 	MODE_APPKEYPAD   = 4,
 	MODE_ALTSCREEN   = 8,
-	MODE_CRLF        = 16,
+	MODE_CRLF	= 16,
 	MODE_MOUSEBTN    = 32,
 	MODE_MOUSEMOTION = 64,
 	MODE_MOUSE       = 32|64,
@@ -110,8 +113,8 @@ enum term_mode {
 
 enum escape_state {
 	ESC_START      = 1,
-	ESC_CSI        = 2,
-	ESC_STR        = 4, /* DSC, OSC, PM, APC */
+	ESC_CSI	= 2,
+	ESC_STR	= 4, /* DSC, OSC, PM, APC */
 	ESC_ALTCHARSET = 8,
 	ESC_STR_END    = 16, /* a final string was encountered */
 };
@@ -152,21 +155,21 @@ typedef struct {
 /* ESC '[' [[ [<priv>] <arg> [;]] <mode>] */
 typedef struct {
 	char buf[ESC_BUF_SIZ]; /* raw string */
-	int len;               /* raw string length */
+	int len;	       /* raw string length */
 	char priv;
 	int arg[ESC_ARG_SIZ];
-	int narg;              /* nb of args */
+	int narg;	      /* nb of args */
 	char mode;
 } CSIEscape;
 
 /* STR Escape sequence structs */
 /* ESC type [[ [<priv>] <arg> [;]] <mode>] ESC '\' */
 typedef struct {
-	char type;             /* ESC type ... */
+	char type;	     /* ESC type ... */
 	char buf[STR_BUF_SIZ]; /* raw string */
-	int len;               /* raw string length */
+	int len;	       /* raw string length */
 	char *args[STR_ARG_SIZ];
-	int narg;              /* nb of args */
+	int narg;	      /* nb of args */
 } STREscape;
 
 /* Internal representation of the screen */
@@ -194,6 +197,8 @@ typedef struct {
 	XIM xim;
 	XIC xic;
 	int scr;
+	Bool isfixed; /* is fixed geometry? */
+	int fx, fy, fw, fh; /* fixed geometry */
 	int w;	/* window width */
 	int h;	/* window height */
 	int ch; /* char height */
@@ -217,6 +222,7 @@ typedef struct {
 	struct {int x, y;} b, e;
 	char *clip;
 	Atom xtarget;
+	bool alt;
 	struct timeval tclick1;
 	struct timeval tclick2;
 } Selection;
@@ -233,11 +239,12 @@ typedef struct {
 		short lbearing;
 		short rbearing;
 		XFontSet set;
-	} font, bfont;
+	} font, bfont, ifont;
 } DC;
 
 static void die(const char*, ...);
 static void draw(void);
+static void redraw(void);
 static void drawregion(int, int, int, int);
 static void execsh(void);
 static void sigchld(int);
@@ -262,7 +269,7 @@ static void tinsertblankline(int);
 static void tmoveto(int, int);
 static void tnew(int, int);
 static void tnewline(int);
-static void tputtab(void);
+static void tputtab(bool);
 static void tputc(char*);
 static void treset(void);
 static int tresize(int, int);
@@ -273,6 +280,7 @@ static void tsetchar(char*);
 static void tsetscroll(int, int);
 static void tswapscreen(void);
 static void tsetdirt(int, int);
+static void tsetmode(bool, bool, int *, int);
 static void tfulldirt(void);
 
 static void ttynew(void);
@@ -283,7 +291,7 @@ static void ttywrite(const char *, size_t);
 static void xdraws(char *, Glyph, int, int, int, int);
 static void xhints(void);
 static void xclear(int, int, int, int);
-static void xcopy();
+static void xcopy(void);
 static void xdrawcursor(void);
 static void xinit(void);
 static void xloadcols(void);
@@ -303,12 +311,13 @@ static void brelease(XEvent *);
 static void bpress(XEvent *);
 static void bmotion(XEvent *);
 static void selnotify(XEvent *);
+static void selclear(XEvent *);
 static void selrequest(XEvent *);
 
 static void selinit(void);
 static inline bool selected(int, int);
 static void selcopy(void);
-static void selpaste();
+static void selpaste(void);
 static void selscroll(int, int);
 
 static int utf8decode(char *, long *);
@@ -328,6 +337,7 @@ static void (*handler[LASTEvent])(XEvent *) = {
 	[MotionNotify] = bmotion,
 	[ButtonPress] = bpress,
 	[ButtonRelease] = brelease,
+	[SelectionClear] = selclear,
 	[SelectionNotify] = selnotify,
 	[SelectionRequest] = selrequest,
 };
@@ -341,7 +351,9 @@ static STREscape strescseq;
 static int cmdfd;
 static pid_t pid;
 static Selection sel;
+static FILE *fileio;
 static char **opt_cmd  = NULL;
+static char *opt_io    = NULL;
 static char *opt_title = NULL;
 static char *opt_embed = NULL;
 static char *opt_class = NULL;
@@ -503,7 +515,7 @@ mousereport(XEvent *e) {
 	int state = e->xbutton.state;
 	char buf[] = { '\033', '[', 'M', 0, 32+x+1, 32+y+1 };
 	static int ob, ox, oy;
-	
+
 	/* from urxvt */
 	if(e->xbutton.type == MotionNotify) {
 		if(!IS_SET(MODE_MOUSEMOTION) || (x == ox && y == oy))
@@ -521,11 +533,11 @@ mousereport(XEvent *e) {
 			ox = x, oy = y;
 		}
 	}
-	
+
 	buf[3] = 32 + button + (state & ShiftMask ? 4 : 0)
 		+ (state & Mod4Mask    ? 8  : 0)
 		+ (state & ControlMask ? 16 : 0);
-	
+
 	ttywrite(buf, sizeof(buf));
 }
 
@@ -571,6 +583,7 @@ selcopy(void) {
 		}
 		*ptr = 0;
 	}
+	sel.alt = IS_SET(MODE_ALTSCREEN);
 	xsetsel(str);
 }
 
@@ -601,6 +614,14 @@ selpaste() {
 	XConvertSelection(xw.dpy, XA_PRIMARY, sel.xtarget, XA_PRIMARY, xw.win, CurrentTime);
 }
 
+void selclear(XEvent *e) {
+	if(sel.bx == -1)
+		return;
+	sel.bx = -1;
+	tsetdirt(sel.b.y, sel.e.y);
+	draw();
+}
+
 void
 selrequest(XEvent *e) {
 	XSelectionRequestEvent *xsre;
@@ -729,6 +750,10 @@ execsh(void) {
 	char **args;
 	char *envshell = getenv("SHELL");
 
+	unsetenv("COLUMNS");
+	unsetenv("LINES");
+	unsetenv("TERMCAP");
+
 	DEFAULT(envshell, SHELL);
 	putenv("TERM="TNAME);
 	args = opt_cmd ? opt_cmd : (char*[]){envshell, "-i", NULL};
@@ -750,7 +775,7 @@ sigchld(int a) {
 void
 ttynew(void) {
 	int m, s;
-	
+
 	/* seems to work fine on linux, openbsd and freebsd */
 	struct winsize w = {term.row, term.col, 0, 0};
 	if(openpty(&m, &s, NULL, NULL, &w) < 0)
@@ -775,6 +800,16 @@ ttynew(void) {
 		close(s);
 		cmdfd = m;
 		signal(SIGCHLD, sigchld);
+		if(opt_io) {
+			if(!strcmp(opt_io, "-")) {
+				fileio = stdout;
+			} else {
+				if(!(fileio = fopen(opt_io, "w"))) {
+					fprintf(stderr, "Error opening %s:%s\n",
+						opt_io, strerror(errno));
+				}
+			}
+		}
 	}
 }
 
@@ -827,7 +862,8 @@ ttyresize(int x, int y) {
 
 	w.ws_row = term.row;
 	w.ws_col = term.col;
-	w.ws_xpixel = w.ws_ypixel = 0;
+	w.ws_xpixel = xw.w;
+	w.ws_ypixel = xw.h;
 	if(ioctl(cmdfd, TIOCSWINSZ, &w) < 0)
 		fprintf(stderr, "Couldn't set window size: %s\n", SERRNO);
 }
@@ -870,7 +906,7 @@ treset(void) {
 	}, .x = 0, .y = 0, .state = CURSOR_DEFAULT};
 
 	memset(term.tabs, 0, term.col * sizeof(*term.tabs));
-	for (i = TAB; i < term.col; i += TAB)
+	for(i = TAB; i < term.col; i += TAB)
 		term.tabs[i] = 1;
 	term.top = 0, term.bot = term.row - 1;
 	term.mode = MODE_WRAP;
@@ -909,11 +945,11 @@ void
 tscrolldown(int orig, int n) {
 	int i;
 	Line temp;
-	
+
 	LIMIT(n, 0, term.bot-orig+1);
 
 	tclearregion(0, term.bot-n+1, term.col-1, term.bot);
-	
+
 	for(i = term.bot; i >= orig+n; i--) {
 		temp = term.line[i];
 		term.line[i] = term.line[i-n];
@@ -931,9 +967,9 @@ tscrollup(int orig, int n) {
 	int i;
 	Line temp;
 	LIMIT(n, 0, term.bot-orig+1);
-	
+
 	tclearregion(0, orig, term.col-1, orig+n-1);
-	
+
 	for(i = orig; i <= term.bot-n; i++) {
 		 temp = term.line[i];
 		 term.line[i] = term.line[i+n];
@@ -950,7 +986,7 @@ void
 selscroll(int orig, int n) {
 	if(sel.bx == -1)
 		return;
-	
+
 	if(BETWEEN(sel.by, orig, term.bot) || BETWEEN(sel.ey, orig, term.bot)) {
 		if((sel.by += n) > term.bot || (sel.ey += n) < term.top) {
 			sel.bx = -1;
@@ -987,7 +1023,7 @@ csiparse(void) {
 	csiescseq.narg = 0;
 	if(*p == '?')
 		csiescseq.priv = 1, p++;
-	
+
 	while(p < csiescseq.buf+csiescseq.len) {
 		while(isdigit(*p)) {
 			csiescseq.arg[csiescseq.narg] *= 10;
@@ -1046,7 +1082,7 @@ tdeletechar(int n) {
 	int src = term.c.x + n;
 	int dst = term.c.x;
 	int size = term.col - src;
-	
+
 	term.dirty[term.c.y] = 1;
 
 	if(src >= term.col) {
@@ -1096,13 +1132,17 @@ 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);
+			term.c.attr.mode &= ~(ATTR_REVERSE | ATTR_UNDERLINE | ATTR_BOLD \
+					| ATTR_ITALIC);
 			term.c.attr.fg = DefaultFG;
 			term.c.attr.bg = DefaultBG;
 			break;
 		case 1:
 			term.c.attr.mode |= ATTR_BOLD;
 			break;
+		case 3: /* enter standout (highlight) */
+			term.c.attr.mode |= ATTR_ITALIC;
+			break;
 		case 4:
 			term.c.attr.mode |= ATTR_UNDERLINE;
 			break;
@@ -1112,6 +1152,9 @@ tsetattr(int *attr, int l) {
 		case 22:
 			term.c.attr.mode &= ~ATTR_BOLD;
 			break;
+		case 23: /* leave standout (highlight) mode */
+			term.c.attr.mode &= ~ATTR_ITALIC;
+			break;
 		case 24:
 			term.c.attr.mode &= ~ATTR_UNDERLINE;
 			break;
@@ -1127,7 +1170,7 @@ tsetattr(int *attr, int l) {
 					fprintf(stderr, "erresc: bad fgcolor %d\n", attr[i]);
 			}
 			else
-				fprintf(stderr, "erresc: gfx attr %d unknown\n", attr[i]);
+				fprintf(stderr, "erresc(38): gfx attr %d unknown\n", attr[i]);
 			break;
 		case 39:
 			term.c.attr.fg = DefaultFG;
@@ -1141,7 +1184,7 @@ tsetattr(int *attr, int l) {
 					fprintf(stderr, "erresc: bad bgcolor %d\n", attr[i]);
 			}
 			else
-				fprintf(stderr, "erresc: gfx attr %d unknown\n", attr[i]);
+				fprintf(stderr, "erresc(48): gfx attr %d unknown\n", attr[i]);
 			break;
 		case 49:
 			term.c.attr.bg = DefaultBG;
@@ -1156,8 +1199,7 @@ tsetattr(int *attr, int l) {
 			else if(BETWEEN(attr[i], 100, 107))
 				term.c.attr.fg = attr[i] - 100 + 8;
 			else
-				fprintf(stderr, "erresc: gfx attr %d unknown\n", attr[i]), csidump();
-			
+				fprintf(stderr, "erresc(default): gfx attr %d unknown\n", attr[i]), csidump();
 			break;
 		}
 	}
@@ -1178,6 +1220,79 @@ tsetscroll(int t, int b) {
 	term.bot = b;
 }
 
+#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
+
+void
+tsetmode(bool priv, bool set, int *args, int narg) {
+	int *lim, mode;
+
+	for(lim = args + narg; args < lim; ++args) {
+		if(priv) {
+			switch(*args) {
+			case 1:
+				MODBIT(term.mode, set, MODE_APPKEYPAD);
+				break;
+			case 5: /* DECSCNM -- Reverve video */
+				mode = term.mode;
+				MODBIT(term.mode,set, MODE_REVERSE);
+				if(mode != term.mode)
+					redraw();
+				break;
+			case 7:
+				MODBIT(term.mode, set, MODE_WRAP);
+				break;
+			case 20:
+				MODBIT(term.mode, set, MODE_CRLF);
+				break;
+			case 12: /* att610 -- Start blinking cursor (IGNORED) */
+				break;
+			case 25:
+				MODBIT(term.c.state, !set, CURSOR_HIDE);
+				break;
+			case 1000: /* 1000,1002: enable xterm mouse report */
+				MODBIT(term.mode, set, MODE_MOUSEBTN);
+				break;
+			case 1002:
+				MODBIT(term.mode, set, MODE_MOUSEMOTION);
+				break;
+			case 1049: /* = 1047 and 1048 */
+			case 47:
+			case 1047:
+				if(IS_SET(MODE_ALTSCREEN))
+					tclearregion(0, 0, term.col-1, term.row-1);
+				if((set && !IS_SET(MODE_ALTSCREEN)) ||
+				    (!set && IS_SET(MODE_ALTSCREEN))) {
+					    tswapscreen();
+				}
+				if(*args != 1049)
+					break;
+				/* pass through */
+			case 1048:
+				tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
+				break;
+			default:
+				fprintf(stderr,
+					"erresc: unknown private set/reset mode %d\n",
+					*args);
+				break;
+			}
+		} else {
+			switch(*args) {
+			case 4:
+				MODBIT(term.mode, set, MODE_INSERT);
+				break;
+			default:
+				fprintf(stderr,
+					"erresc: unknown set/reset mode %d\n",
+					*args);
+				break;
+			}
+		}
+	}
+}
+#undef MODBIT
+
+
 void
 csihandle(void) {
 	switch(csiescseq.mode) {
@@ -1230,20 +1345,20 @@ csihandle(void) {
 		}
 		break;
 	case 'G': /* CHA -- Move to <col> */
-	case '`': /* XXX: HPA -- same? */
+	case '`': /* HPA */
 		DEFAULT(csiescseq.arg[0], 1);
 		tmoveto(csiescseq.arg[0]-1, term.c.y);
 		break;
 	case 'H': /* CUP -- Move to <row> <col> */
-	case 'f': /* XXX: HVP -- same? */
+	case 'f': /* HVP */
 		DEFAULT(csiescseq.arg[0], 1);
 		DEFAULT(csiescseq.arg[1], 1);
 		tmoveto(csiescseq.arg[1]-1, csiescseq.arg[0]-1);
 		break;
 	case 'I': /* CHT -- Cursor Forward Tabulation <n> tab stops */
 		DEFAULT(csiescseq.arg[0], 1);
-		while (csiescseq.arg[0]--)
-			tputtab();
+		while(csiescseq.arg[0]--)
+			tputtab(1);
 		break;
 	case 'J': /* ED -- Clear screen */
 		sel.bx = -1;
@@ -1291,58 +1406,7 @@ csihandle(void) {
 		tinsertblankline(csiescseq.arg[0]);
 		break;
 	case 'l': /* RM -- Reset Mode */
-		if(csiescseq.priv) {
-			switch(csiescseq.arg[0]) {
-			case 1:
-				term.mode &= ~MODE_APPKEYPAD;
-				break;
-			case 5: /* DECSCNM -- Remove reverse video */
-				if(IS_SET(MODE_REVERSE)) {
-					term.mode &= ~MODE_REVERSE;
-					draw();
-				}
-				break;
-			case 7:
-				term.mode &= ~MODE_WRAP;
-				break;
-			case 12: /* att610 -- Stop blinking cursor (IGNORED) */
-				break;
-			case 20:
-				term.mode &= ~MODE_CRLF;
-				break;
-			case 25:
-				term.c.state |= CURSOR_HIDE;
-				break;
-			case 1000: /* disable X11 xterm mouse reporting */
-				term.mode &= ~MODE_MOUSEBTN;
-				break;
-			case 1002:
-				term.mode &= ~MODE_MOUSEMOTION;
-				break;
-			case 1049: /* = 1047 and 1048 */
-			case 47:
-			case 1047:
-				if(IS_SET(MODE_ALTSCREEN)) {
-					tclearregion(0, 0, term.col-1, term.row-1);
-					tswapscreen();
-				}
-				if(csiescseq.arg[0] != 1049)
-					break;
-			case 1048:
-				tcursor(CURSOR_LOAD);
-				break;
-			default:
-				goto unknown;
-			}
-		} else {
-			switch(csiescseq.arg[0]) {
-			case 4:
-				term.mode &= ~MODE_INSERT;
-				break;
-			default:
-				goto unknown;
-			}
-		}
+		tsetmode(csiescseq.priv, 0, csiescseq.arg, csiescseq.narg);
 		break;
 	case 'M': /* DL -- Delete <n> lines */
 		DEFAULT(csiescseq.arg[0], 1);
@@ -1356,64 +1420,17 @@ csihandle(void) {
 		DEFAULT(csiescseq.arg[0], 1);
 		tdeletechar(csiescseq.arg[0]);
 		break;
-	/* XXX: (CSI n Z) CBT -- Cursor Backward Tabulation <n> tab stops */
+	case 'Z': /* CBT -- Cursor Backward Tabulation <n> tab stops */
+		DEFAULT(csiescseq.arg[0], 1);
+		while(csiescseq.arg[0]--)
+			tputtab(0);
+		break;
 	case 'd': /* VPA -- Move to <row> */
 		DEFAULT(csiescseq.arg[0], 1);
 		tmoveto(term.c.x, csiescseq.arg[0]-1);
 		break;
 	case 'h': /* SM -- Set terminal mode */
-		if(csiescseq.priv) {
-			switch(csiescseq.arg[0]) {
-			case 1:
-				term.mode |= MODE_APPKEYPAD;
-				break;
-			case 5: /* DECSCNM -- Reverve video */
-				if(!IS_SET(MODE_REVERSE)) {
-					term.mode |= MODE_REVERSE;
-					draw();
-				}
-				break;
-			case 7:
-				term.mode |= MODE_WRAP;
-				break;
-			case 20:
-				term.mode |= MODE_CRLF;
-				break;
-			case 12: /* att610 -- Start blinking cursor (IGNORED) */
-				 /* fallthrough for xterm cvvis = CSI [ ? 12 ; 25 h */
-				if(csiescseq.narg > 1 && csiescseq.arg[1] != 25)
-					break;
-			case 25:
-				term.c.state &= ~CURSOR_HIDE;
-				break;
-			case 1000: /* 1000,1002: enable xterm mouse report */
-				term.mode |= MODE_MOUSEBTN;
-				break;
-			case 1002:
-				term.mode |= MODE_MOUSEMOTION;
-				break;
-			case 1049: /* = 1047 and 1048 */
-			case 47:
-			case 1047:
-				if(IS_SET(MODE_ALTSCREEN))
-					tclearregion(0, 0, term.col-1, term.row-1);
-				else
-					tswapscreen();
-				if(csiescseq.arg[0] != 1049)
-					break;
-			case 1048:
-				tcursor(CURSOR_SAVE);
-				break;
-			default: goto unknown;
-			}
-		} else {
-			switch(csiescseq.arg[0]) {
-			case 4:
-				term.mode |= MODE_INSERT;
-				break;
-			default: goto unknown;
-			}
-		};
+		tsetmode(csiescseq.priv, 1, csiescseq.arg, csiescseq.narg);
 		break;
 	case 'm': /* SGR -- Terminal attribute (color) */
 		tsetattr(csiescseq.arg, csiescseq.narg);
@@ -1461,7 +1478,12 @@ void
 strhandle(void) {
 	char *p;
 
-	p = strescseq.buf; 
+	/*
+	 * TODO: make this being useful in case of color palette change.
+	 */
+	strparse();
+
+	p = strescseq.buf;
 
 	switch(strescseq.type) {
 	case ']': /* OSC -- Operating System Command */
@@ -1487,6 +1509,9 @@ strhandle(void) {
 			break;
 		}
 		break;
+	case 'k': /* old title set compatibility */
+		XStoreName(xw.dpy, xw.win, strescseq.buf);
+		break;
 	case 'P': /* DSC -- Device Control String */
 	case '_': /* APC -- Application Program Command */
 	case '^': /* PM -- Privacy Message */
@@ -1528,17 +1553,32 @@ strreset(void) {
 }
 
 void
-tputtab(void) {
-	unsigned x;
+tputtab(bool forward) {
+	unsigned x = term.c.x;
 
-	for (x = term.c.x + 1; x < term.col && !term.tabs[x]; ++x)
-		/* nothing */ ;
+	if(forward) {
+		if(x == term.col)
+			return;
+		for(++x; x < term.col && !term.tabs[x]; ++x)
+			/* nothing */ ;
+	} else {
+		if(x == 0)
+			return;
+		for(--x; x > 0 && !term.tabs[x]; --x)
+			/* nothing */ ;
+	}
 	tmoveto(x, term.c.y);
 }
 
 void
 tputc(char *c) {
 	char ascii = *c;
+
+	if(fileio) {
+		putc(ascii, fileio);
+		fflush(fileio);
+	}
+
 	if(term.esc & ESC_START) {
 		if(term.esc & ESC_CSI) {
 			csiescseq.buf[csiescseq.len++] = ascii;
@@ -1557,7 +1597,7 @@ tputc(char *c) {
 				break;
 			default:
 				strescseq.buf[strescseq.len++] = ascii;
-				if (strescseq.len+1 >= STR_BUF_SIZ) {
+				if(strescseq.len+1 >= STR_BUF_SIZ) {
 					term.esc = 0;
 					strhandle();
 				}
@@ -1587,6 +1627,7 @@ tputc(char *c) {
 			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;
@@ -1650,7 +1691,7 @@ tputc(char *c) {
 			sel.bx = -1;
 		switch(ascii) {
 		case '\t':
-			tputtab();
+			tputtab(1);
 			break;
 		case '\b':
 			tmoveto(term.c.x-1, term.c.y);
@@ -1735,13 +1776,13 @@ tresize(int col, int row) {
 		term.line[i] = calloc(col, sizeof(Glyph));
 		term.alt [i] = calloc(col, sizeof(Glyph));
 	}
-	if (col > term.col) {
+	if(col > term.col) {
 		bool *bp = term.tabs + term.col;
 
 		memset(bp, 0, sizeof(*term.tabs) * (col - term.col));
-		while (--bp > term.tabs && !*bp)
+		while(--bp > term.tabs && !*bp)
 			/* nothing */ ;
-		for (bp += TAB; bp < term.tabs + col; bp += TAB)
+		for(bp += TAB; bp < term.tabs + col; bp += TAB)
 			*bp = 1;
 	}
 	/* update terminal size */
@@ -1776,7 +1817,7 @@ xloadcols(void) {
 		} else
 			dc.col[i] = color.pixel;
 	}
-	
+
 	/* load colors [16-255] ; same colors as xterm */
 	for(i = 16, r = 0; r < 6; r++)
 		for(g = 0; g < 6; g++)
@@ -1794,7 +1835,7 @@ xloadcols(void) {
 
 	for(r = 0; r < 24; r++, i++) {
 		color.red = color.green = color.blue = 0x0808 + 0x0a0a * r;
-		if (!XAllocColor(xw.dpy, xw.cmap, &color)) {
+		if(!XAllocColor(xw.dpy, xw.cmap, &color)) {
 			dc.col[i] = white;
 			fprintf(stderr, "Could not allocate color %d\n", i);
 		} else
@@ -1806,24 +1847,33 @@ void
 xclear(int x1, int y1, int x2, int y2) {
 	XSetForeground(xw.dpy, dc.gc, dc.col[IS_SET(MODE_REVERSE) ? DefaultFG : DefaultBG]);
 	XFillRectangle(xw.dpy, xw.buf, dc.gc,
-	               BORDER + x1 * xw.cw, BORDER + y1 * xw.ch,
-	               (x2-x1+1) * xw.cw, (y2-y1+1) * xw.ch);
+		       BORDER + x1 * xw.cw, BORDER + y1 * xw.ch,
+		       (x2-x1+1) * xw.cw, (y2-y1+1) * xw.ch);
 }
 
 void
 xhints(void) {
 	XClassHint class = {opt_class ? opt_class : TNAME, TNAME};
 	XWMHints wm = {.flags = InputHint, .input = 1};
-	XSizeHints size = {
-		.flags = PSize | PResizeInc | PBaseSize,
-		.height = xw.h,
-		.width = xw.w,
-		.height_inc = xw.ch,
-		.width_inc = xw.cw,
-		.base_height = 2*BORDER,
-		.base_width = 2*BORDER,
-	};
-	XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, &size, &wm, &class);
+	XSizeHints *sizeh = NULL;
+
+	sizeh = XAllocSizeHints();
+	if(xw.isfixed == False) {
+		sizeh->flags = PSize | PResizeInc | PBaseSize;
+		sizeh->height = xw.h;
+		sizeh->width = xw.w;
+		sizeh->height_inc = xw.ch;
+		sizeh->width_inc = xw.cw;
+		sizeh->base_height = 2*BORDER;
+		sizeh->base_width = 2*BORDER;
+	} else {
+		sizeh->flags = PMaxSize | PMinSize;
+		sizeh->min_width = sizeh->max_width = xw.fw;
+		sizeh->min_height = sizeh->max_height = xw.fh;
+	}
+
+	XSetWMProperties(xw.dpy, xw.win, NULL, NULL, NULL, 0, sizeh, &wm, &class);
+	XFree(sizeh);
 }
 
 XFontSet
@@ -1860,14 +1910,20 @@ xgetfontinfo(XFontSet set, int *ascent, int *descent, short *lbearing, short *rb
 }
 
 void
-initfonts(char *fontstr, char *bfontstr) {
-	if((dc.font.set = xinitfont(fontstr)) == NULL ||
-	   (dc.bfont.set = xinitfont(bfontstr)) == NULL)
-		die("Can't load font %s\n", dc.font.set ? BOLDFONT : FONT);
+initfonts(char *fontstr, char *bfontstr, char *ifontstr) {
+	if((dc.font.set = xinitfont(fontstr)) == NULL)
+		die("Can't load font %s\n", fontstr);
+	if((dc.bfont.set = xinitfont(bfontstr)) == NULL)
+		die("Can't load bfont %s\n", bfontstr);
+	if((dc.ifont.set = xinitfont(ifontstr)) == NULL)
+		die("Can't load ifont %s\n", ifontstr);
+
 	xgetfontinfo(dc.font.set, &dc.font.ascent, &dc.font.descent,
 	    &dc.font.lbearing, &dc.font.rbearing);
 	xgetfontinfo(dc.bfont.set, &dc.bfont.ascent, &dc.bfont.descent,
 	    &dc.bfont.lbearing, &dc.bfont.rbearing);
+	xgetfontinfo(dc.ifont.set, &dc.ifont.ascent, &dc.ifont.descent,
+	    &dc.ifont.lbearing, &dc.ifont.rbearing);
 }
 
 void
@@ -1875,13 +1931,14 @@ xinit(void) {
 	XSetWindowAttributes attrs;
 	Cursor cursor;
 	Window parent;
+	int sw, sh;
 
 	if(!(xw.dpy = XOpenDisplay(NULL)))
 		die("Can't open display\n");
 	xw.scr = XDefaultScreen(xw.dpy);
-	
+
 	/* font */
-	initfonts(FONT, BOLDFONT);
+	initfonts(FONT, BOLDFONT, ITALICFONT);
 
 	/* XXX: Assuming same size for bold font */
 	xw.cw = dc.font.rbearing - dc.font.lbearing;
@@ -1891,21 +1948,35 @@ xinit(void) {
 	xw.cmap = XDefaultColormap(xw.dpy, xw.scr);
 	xloadcols();
 
-	/* window - default size */
-	xw.h = 2*BORDER + term.row * xw.ch;
-	xw.w = 2*BORDER + term.col * xw.cw;
+	/* adjust fixed window geometry */
+	if(xw.isfixed) {
+		sw = DisplayWidth(xw.dpy, xw.scr);
+		sh = DisplayHeight(xw.dpy, xw.scr);
+		if(xw.fx < 0)
+			xw.fx = sw + xw.fx - xw.fw - 1;
+		if(xw.fy < 0)
+			xw.fy = sh + xw.fy - xw.fh - 1;
+
+		xw.h = xw.fh;
+		xw.w = xw.fw;
+	} else {
+		/* window - default size */
+		xw.h = 2*BORDER + term.row * xw.ch;
+		xw.w = 2*BORDER + term.col * xw.cw;
+		xw.fx = 0;
+		xw.fy = 0;
+	}
 
 	attrs.background_pixel = dc.col[DefaultBG];
 	attrs.border_pixel = dc.col[DefaultBG];
 	attrs.bit_gravity = NorthWestGravity;
 	attrs.event_mask = FocusChangeMask | KeyPressMask
 		| ExposureMask | VisibilityChangeMask | StructureNotifyMask
-		| ButtonMotionMask | ButtonPressMask | ButtonReleaseMask
-		| EnterWindowMask | LeaveWindowMask;
+		| ButtonMotionMask | ButtonPressMask | ButtonReleaseMask;
 	attrs.colormap = xw.cmap;
 
 	parent = opt_embed ? strtol(opt_embed, NULL, 0) : XRootWindow(xw.dpy, xw.scr);
-	xw.win = XCreateWindow(xw.dpy, parent, 0, 0,
+	xw.win = XCreateWindow(xw.dpy, parent, xw.fx, xw.fy,
 			xw.w, xw.h, 0, XDefaultDepth(xw.dpy, xw.scr), InputOutput,
 			XDefaultVisual(xw.dpy, xw.scr),
 			CWBackPixel | CWBorderPixel | CWBitGravity | CWEventMask
@@ -1921,7 +1992,7 @@ xinit(void) {
 					   XNFocusWindow, xw.win, NULL);
 	/* gc */
 	dc.gc = XCreateGC(xw.dpy, xw.win, 0, NULL);
-	
+
 	/* white cursor, black outline */
 	cursor = XCreateFontCursor(xw.dpy, XC_xterm);
 	XDefineCursor(xw.dpy, xw.win, cursor);
@@ -1943,7 +2014,7 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
 	int winx = BORDER+x*xw.cw, winy = BORDER+y*xw.ch + dc.font.ascent, width = charlen*xw.cw;
 	XFontSet fontset = dc.font.set;
 	int i;
-	
+
 	/* only switch default fg/bg if term is in RV mode */
 	if(IS_SET(MODE_REVERSE)) {
 		if(fg == DefaultFG)
@@ -1960,6 +2031,9 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
 		fontset = dc.bfont.set;
 	}
 
+	if(base.mode & ATTR_ITALIC)
+		fontset = dc.ifont.set;
+
 	XSetBackground(xw.dpy, dc.gc, dc.col[bg]);
 	XSetForeground(xw.dpy, dc.gc, dc.col[fg]);
 
@@ -1974,7 +2048,7 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
 	}
 
 	XmbDrawImageString(xw.dpy, xw.buf, fontset, dc.gc, winx, winy, s, bytelen);
-	
+
 	if(base.mode & ATTR_UNDERLINE)
 		XDrawLine(xw.dpy, xw.buf, dc.gc, winx, winy+1, winx+width-1, winy+1);
 }
@@ -1982,9 +2056,8 @@ xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) {
 /* copy buffer pixmap to screen pixmap */
 void
 xcopy() {
-        XdbeSwapInfo swpinfo[1] = {{xw.win, XdbeCopied}};
-        XdbeSwapBuffers(xw.dpy, swpinfo, 1);
-
+	XdbeSwapInfo swpinfo[1] = {{xw.win, XdbeCopied}};
+	XdbeSwapBuffers(xw.dpy, swpinfo, 1);
 }
 
 void
@@ -1993,10 +2066,10 @@ xdrawcursor(void) {
 	static int oldy = 0;
 	int sl;
 	Glyph g = {{' '}, ATTR_NULL, DefaultBG, DefaultCS, 0};
-	
+
 	LIMIT(oldx, 0, term.col-1);
 	LIMIT(oldy, 0, term.row-1);
-	
+
 	if(term.line[term.c.y][term.c.x].state & GLYPH_SET)
 		memcpy(g.c, term.line[term.c.y][term.c.x].c, UTF_SIZ);
 
@@ -2007,7 +2080,7 @@ xdrawcursor(void) {
 	} else
 		xclear(oldx, oldy, oldx, oldy);
 
-	xcopy(oldx, oldy, 1, 1);
+	xcopy();
 
 	/* draw the new one */
 	if(!(term.c.state & CURSOR_HIDE)) {
@@ -2022,7 +2095,15 @@ xdrawcursor(void) {
 		oldx = term.c.x, oldy = term.c.y;
 	}
 
-	xcopy(term.c.x, term.c.y, 1, 1);
+	xcopy();
+}
+
+void
+redraw(void) {
+	struct timespec tv = {0, REDRAW_TIMEOUT * 1000};
+	tfulldirt();
+	draw();
+	nanosleep(&tv, NULL);
 }
 
 void
@@ -2037,7 +2118,10 @@ drawregion(int x1, int y1, int x2, int y2) {
 	int ic, ib, x, y, ox, sl;
 	Glyph base, new;
 	char buf[DRAW_BUF_SIZ];
+	bool ena_sel = sel.bx != -1, alt = IS_SET(MODE_ALTSCREEN);
 
+	if((sel.alt && !alt) || (!sel.alt && alt))
+		ena_sel = 0;
 	if(!(xw.state & WIN_VISIBLE))
 		return;
 
@@ -2050,7 +2134,7 @@ drawregion(int x1, int y1, int x2, int y2) {
 		ic = ib = ox = 0;
 		for(x = x1; x < x2; x++) {
 			new = term.line[y][x];
-			if(sel.bx != -1 && *(new.c) && selected(x, y))
+			if(ena_sel && *(new.c) && selected(x, y))
 				new.mode ^= ATTR_REVERSE;
 			if(ib > 0 && (!(new.state & GLYPH_SET) || ATTRCMP(base, new) ||
 						  ib >= DRAW_BUF_SIZ-UTF_SIZ)) {
@@ -2080,8 +2164,8 @@ expose(XEvent *ev) {
 	if(xw.state & WIN_REDRAW) {
 		if(!e->count)
 			xw.state &= ~WIN_REDRAW;
-        }
-        xcopy();
+	}
+	xcopy();
 }
 
 void
@@ -2143,7 +2227,7 @@ kpress(XEvent *ev) {
 	meta = e->state & Mod1Mask;
 	shift = e->state & ShiftMask;
 	len = XmbLookupString(xw.xic, e, buf, sizeof(buf), &ksym, &status);
-	
+
 	/* 1. custom keys from config.h */
 	if((customkey = kmap(ksym, e->state)))
 		ttywrite(customkey, strlen(customkey));
@@ -2183,11 +2267,11 @@ void
 cmessage(XEvent *e) {
 	/* See xembed specs
 	   http://standards.freedesktop.org/xembed-spec/xembed-spec-latest.html */
-	if (e->xclient.message_type == xw.xembed && e->xclient.format == 32) {
-		if (e->xclient.data.l[1] == XEMBED_FOCUS_IN) {
+	if(e->xclient.message_type == xw.xembed && e->xclient.format == 32) {
+		if(e->xclient.data.l[1] == XEMBED_FOCUS_IN) {
 			xw.state |= WIN_FOCUSED;
 			xseturgency(0);
-		} else if (e->xclient.data.l[1] == XEMBED_FOCUS_OUT) {
+		} else if(e->xclient.data.l[1] == XEMBED_FOCUS_OUT) {
 			xw.state &= ~WIN_FOCUSED;
 		}
 		draw();
@@ -2197,10 +2281,10 @@ cmessage(XEvent *e) {
 void
 resize(XEvent *e) {
 	int col, row;
-	
+
 	if(e->xconfigure.width == xw.w && e->xconfigure.height == xw.h)
 		return;
-	
+
 	xw.w = e->xconfigure.width;
 	xw.h = e->xconfigure.height;
 	col = (xw.w - 2*BORDER) / xw.cw;
@@ -2209,8 +2293,8 @@ resize(XEvent *e) {
 		return;
 	if(tresize(col, row))
 		draw();
-	ttyresize(col, row);
 	xresize(col, row);
+	ttyresize(col, row);
 }
 
 bool
@@ -2227,7 +2311,7 @@ run(void) {
 	int xfd = XConnectionNumber(xw.dpy);
 	struct timeval timeout = {0};
 	bool stuff_to_print = 0;
-	
+
 	for(;;) {
 		FD_ZERO(&rfd);
 		FD_SET(cmdfd, &rfd);
@@ -2261,8 +2345,12 @@ run(void) {
 
 int
 main(int argc, char *argv[]) {
-	int i;
-	
+	int i, bitm, xr, yr;
+	unsigned int wr, hr;
+
+	xw.fw = xw.fh = xw.fx = xw.fy = 0;
+	xw.isfixed = False;
+
 	for(i = 1; i < argc; i++) {
 		switch(argv[i][0] != '-' || argv[i][2] ? -1 : argv[i][1]) {
 		case 't':
@@ -2274,10 +2362,34 @@ main(int argc, char *argv[]) {
 		case 'w':
 			if(++i < argc) opt_embed = argv[i];
 			break;
+		case 'f':
+			if(++i < argc) opt_io = argv[i];
+			break;
 		case 'e':
 			/* eat every remaining arguments */
 			if(++i < argc) opt_cmd = &argv[i];
 			goto run;
+		case 'g':
+			if(++i >= argc)
+				break;
+
+			bitm = XParseGeometry(argv[i], &xr, &yr, &wr, &hr);
+			if(bitm & XValue)
+				xw.fx = xr;
+			if(bitm & YValue)
+				xw.fy = yr;
+			if(bitm & WidthValue)
+				xw.fw = (int)wr;
+			if(bitm & HeightValue)
+				xw.fh = (int)hr;
+			if(bitm & XNegative && xw.fx == 0)
+				xw.fx = -1;
+			if(bitm & XNegative && xw.fy == 0)
+				xw.fy = -1;
+
+			if(xw.fh != 0 && xw.fw != 0)
+				xw.isfixed = True;
+			break;
 		case 'v':
 		default:
 			die(USAGE);
@@ -2293,3 +2405,4 @@ main(int argc, char *argv[]) {
 	run();
 	return 0;
 }
+