st

Mahdi's build of st
git clone git://mahdi.pw/st.git
Log | Files | Refs | README | LICENSE

st-columns-rows-reflow-st-unpatched-new.diff (39148B)


      1 From: João F. BeyondMagic <[email protected]>
      2 Date: Tue, 27 Dec 2021
      3 Subject: [PATCH] Patch to redraw columns and rows in 
      4 case of hidden by resize. Extracted from 
      5 https://github.com/ashish-yadav11/st @ashish-yadav11 
      6 Source: https://github.com/nimaipatel/st/blob/master/patches/columns-rows-reflow-st-unpatched-new.patch
      7 diff --git a/st.c b/st.c
      8 index a9338e1..e3b052d 100644
      9 --- a/st.c
     10 +++ b/st.c
     11 @@ -35,6 +35,8 @@
     12  #define ESC_ARG_SIZ   16
     13  #define STR_BUF_SIZ   ESC_BUF_SIZ
     14  #define STR_ARG_SIZ   ESC_ARG_SIZ
     15 +#define HISTSIZE      2000
     16 +#define RESIZEBUFFER  1000
     17  
     18  /* macros */
     19  #define IS_SET(flag)		((term.mode & (flag)) != 0)
     20 @@ -42,6 +44,24 @@
     21  #define ISCONTROLC1(c)		(BETWEEN(c, 0x80, 0x9f))
     22  #define ISCONTROL(c)		(ISCONTROLC0(c) || ISCONTROLC1(c))
     23  #define ISDELIM(u)		(u && wcschr(worddelimiters, u))
     24 +#define STRESCARGREST(n)	((n) == 0 ? strescseq.buf : strescseq.args[(n)-1] + 1)
     25 +#define STRESCARGJUST(n)	(*(strescseq.args[n]) = '\0', STRESCARGREST(n))
     26 +
     27 +#define TLINE(y) ( \
     28 +	(y) < term.scr ? term.hist[(term.histi + (y) - term.scr + 1 + HISTSIZE) % HISTSIZE] \
     29 +	               : term.line[(y) - term.scr] \
     30 +)
     31 +
     32 +#define TLINEABS(y) ( \
     33 +	(y) < 0 ? term.hist[(term.histi + (y) + 1 + HISTSIZE) % HISTSIZE] : term.line[(y)] \
     34 +)
     35 +
     36 +#define UPDATEWRAPNEXT(alt, col) do { \
     37 +	if ((term.c.state & CURSOR_WRAPNEXT) && term.c.x + term.wrapcwidth[alt] < col) { \
     38 +		term.c.x += term.wrapcwidth[alt]; \
     39 +		term.c.state &= ~CURSOR_WRAPNEXT; \
     40 +	} \
     41 +} while (0);
     42  
     43  enum term_mode {
     44  	MODE_WRAP        = 1 << 0,
     45 @@ -53,6 +73,12 @@ enum term_mode {
     46  	MODE_UTF8        = 1 << 6,
     47  };
     48  
     49 +enum scroll_mode {
     50 +	SCROLL_RESIZE = -1,
     51 +	SCROLL_NOSAVEHIST = 0,
     52 +	SCROLL_SAVEHIST = 1
     53 +};
     54 +
     55  enum cursor_movement {
     56  	CURSOR_SAVE,
     57  	CURSOR_LOAD
     58 @@ -114,7 +140,11 @@ typedef struct {
     59  	int row;      /* nb row */
     60  	int col;      /* nb col */
     61  	Line *line;   /* screen */
     62 -	Line *alt;    /* alternate screen */
     63 + 	Line hist[HISTSIZE]; /* history buffer */
     64 +	int histi;           /* history index */
     65 +	int histf;           /* nb history available */
     66 +	int scr;             /* scroll back */
     67 +	int wrapcwidth[2];   /* used in updating WRAPNEXT when resizing */
     68  	int *dirty;   /* dirtyness of lines */
     69  	TCursor c;    /* cursor */
     70  	int ocx;      /* old cursor col */
     71 @@ -171,26 +201,37 @@ static void tprinter(char *, size_t);
     72  static void tdumpsel(void);
     73  static void tdumpline(int);
     74  static void tdump(void);
     75 -static void tclearregion(int, int, int, int);
     76 +static void tclearregion(int, int, int, int, int);
     77  static void tcursor(int);
     78 +static void tclearglyph(Glyph *, int);
     79 +static void tresetcursor(void);
     80  static void tdeletechar(int);
     81  static void tdeleteline(int);
     82  static void tinsertblank(int);
     83  static void tinsertblankline(int);
     84 -static int tlinelen(int);
     85 +static int tlinelen(Line len);
     86 +static int tiswrapped(Line line);
     87 +static char *tgetglyphs(char *, const Glyph *, const Glyph *);
     88 +static size_t tgetline(char *, const Glyph *);
     89  static void tmoveto(int, int);
     90  static void tmoveato(int, int);
     91  static void tnewline(int);
     92  static void tputtab(int);
     93  static void tputc(Rune);
     94  static void treset(void);
     95 -static void tscrollup(int, int);
     96 +static void tscrollup(int, int, int, int);
     97  static void tscrolldown(int, int);
     98 +static void treflow(int, int);
     99 +static void rscrolldown(int);
    100 +static void tresizedef(int, int);
    101 +static void tresizealt(int, int);
    102  static void tsetattr(const int *, int);
    103  static void tsetchar(Rune, const Glyph *, int, int);
    104  static void tsetdirt(int, int);
    105  static void tsetscroll(int, int);
    106  static void tswapscreen(void);
    107 +static void tloaddefscreen(int, int);
    108 +static void tloadaltscreen(int, int);
    109  static void tsetmode(int, int, const int *, int);
    110  static int twrite(const char *, int, int);
    111  static void tfulldirt(void);
    112 @@ -204,7 +245,10 @@ static void tstrsequence(uchar);
    113  static void drawregion(int, int, int, int);
    114  
    115  static void selnormalize(void);
    116 -static void selscroll(int, int);
    117 +static void selscroll(int, int, int);
    118 +static void selmove(int);
    119 +static void selremove(void);
    120 +static int regionselected(int, int, int, int);
    121  static void selsnap(int *, int *, int);
    122  
    123  static size_t utf8decode(const char *, Rune *, size_t);
    124 @@ -412,17 +456,46 @@ selinit(void)
    125  }
    126  
    127  int
    128 -tlinelen(int y)
    129 +tlinelen(Line line)
    130  {
    131 -	int i = term.col;
    132 +	int i = term.col - 1;
    133 +
    134 +	for (; i >= 0 && !(line[i].mode & (ATTR_SET | ATTR_WRAP)); i--);
    135 +	return i + 1;
    136 +}
    137  
    138 -	if (term.line[y][i - 1].mode & ATTR_WRAP)
    139 -		return i;
    140 +int
    141 +tiswrapped(Line line)
    142 +{
    143 +	int len = tlinelen(line);
    144  
    145 -	while (i > 0 && term.line[y][i - 1].u == ' ')
    146 -		--i;
    147 +	return len > 0 && (line[len - 1].mode & ATTR_WRAP);
    148 +}
    149  
    150 -	return i;
    151 +char *
    152 +tgetglyphs(char *buf, const Glyph *gp, const Glyph *lgp)
    153 +{
    154 +	while (gp <= lgp)
    155 +		if (gp->mode & ATTR_WDUMMY) {
    156 +			gp++;
    157 +		} else {
    158 +			buf += utf8encode((gp++)->u, buf);
    159 +		}
    160 +	return buf;
    161 +}
    162 +
    163 +size_t
    164 +tgetline(char *buf, const Glyph *fgp)
    165 +{
    166 +	char *ptr;
    167 +	const Glyph *lgp = &fgp[term.col - 1];
    168 +
    169 +	while (lgp > fgp && !(lgp->mode & (ATTR_SET | ATTR_WRAP)))
    170 +		lgp--;
    171 +	ptr = tgetglyphs(buf, fgp, lgp);
    172 +	if (!(lgp->mode & ATTR_WRAP))
    173 +		*(ptr++) = '\n';
    174 +	return ptr - buf;
    175  }
    176  
    177  void
    178 @@ -462,10 +535,11 @@ selextend(int col, int row, int type, int done)
    179  
    180  	sel.oe.x = col;
    181  	sel.oe.y = row;
    182 -	selnormalize();
    183  	sel.type = type;
    184 +	selnormalize();
    185  
    186 -	if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY)
    187 +	if (oldey != sel.oe.y || oldex != sel.oe.x ||
    188 +	    oldtype != sel.type || sel.mode == SEL_EMPTY)
    189  		tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey));
    190  
    191  	sel.mode = done ? SEL_IDLE : SEL_READY;
    192 @@ -489,46 +563,53 @@ selnormalize(void)
    193  	selsnap(&sel.nb.x, &sel.nb.y, -1);
    194  	selsnap(&sel.ne.x, &sel.ne.y, +1);
    195  
    196 -	/* expand selection over line breaks */
    197 +  /* expand selection over line breaks */
    198  	if (sel.type == SEL_RECTANGULAR)
    199  		return;
    200 -	i = tlinelen(sel.nb.y);
    201 -	if (i < sel.nb.x)
    202 +
    203 +  i = tlinelen(TLINE(sel.nb.y));
    204 +	if (sel.nb.x > i)
    205  		sel.nb.x = i;
    206 -	if (tlinelen(sel.ne.y) <= sel.ne.x)
    207 -		sel.ne.x = term.col - 1;
    208 +  if (sel.ne.x >= tlinelen(TLINE(sel.ne.y)))
    209 +    sel.ne.x = term.col - 1;
    210  }
    211  
    212  int
    213 -selected(int x, int y)
    214 +regionselected(int x1, int y1, int x2, int y2)
    215  {
    216 -	if (sel.mode == SEL_EMPTY || sel.ob.x == -1 ||
    217 -			sel.alt != IS_SET(MODE_ALTSCREEN))
    218 +	if (sel.ob.x == -1 || sel.mode == SEL_EMPTY ||
    219 +	    sel.alt != IS_SET(MODE_ALTSCREEN) || sel.nb.y > y2 || sel.ne.y < y1)
    220  		return 0;
    221  
    222 -	if (sel.type == SEL_RECTANGULAR)
    223 -		return BETWEEN(y, sel.nb.y, sel.ne.y)
    224 -		    && BETWEEN(x, sel.nb.x, sel.ne.x);
    225 +	return (sel.type == SEL_RECTANGULAR) ? sel.nb.x <= x2 && sel.ne.x >= x1
    226 +		: (sel.nb.y != y2 || sel.nb.x <= x2) &&
    227 +		  (sel.ne.y != y1 || sel.ne.x >= x1);
    228 +}
    229  
    230 -	return BETWEEN(y, sel.nb.y, sel.ne.y)
    231 -	    && (y != sel.nb.y || x >= sel.nb.x)
    232 -	    && (y != sel.ne.y || x <= sel.ne.x);
    233 +int
    234 +selected(int x, int y)
    235 +{
    236 +	return regionselected(x, y, x, y);
    237  }
    238  
    239  void
    240  selsnap(int *x, int *y, int direction)
    241  {
    242  	int newx, newy, xt, yt;
    243 +	int rtop = 0, rbot = term.row - 1;
    244  	int delim, prevdelim;
    245  	const Glyph *gp, *prevgp;
    246  
    247 +	if (!IS_SET(MODE_ALTSCREEN))
    248 +		rtop += -term.histf + term.scr, rbot += term.scr;
    249 +
    250  	switch (sel.snap) {
    251  	case SNAP_WORD:
    252  		/*
    253  		 * Snap around if the word wraps around at the end or
    254  		 * beginning of a line.
    255  		 */
    256 -		prevgp = &term.line[*y][*x];
    257 +		prevgp = &TLINE(*y)[*x];
    258  		prevdelim = ISDELIM(prevgp->u);
    259  		for (;;) {
    260  			newx = *x + direction;
    261 @@ -536,24 +617,24 @@ selsnap(int *x, int *y, int direction)
    262  			if (!BETWEEN(newx, 0, term.col - 1)) {
    263  				newy += direction;
    264  				newx = (newx + term.col) % term.col;
    265 -				if (!BETWEEN(newy, 0, term.row - 1))
    266 +				if (!BETWEEN(newy, rtop, rbot))
    267  					break;
    268  
    269  				if (direction > 0)
    270  					yt = *y, xt = *x;
    271  				else
    272  					yt = newy, xt = newx;
    273 -				if (!(term.line[yt][xt].mode & ATTR_WRAP))
    274 +				if (!(TLINE(yt)[xt].mode & ATTR_WRAP))
    275  					break;
    276  			}
    277  
    278 -			if (newx >= tlinelen(newy))
    279 +			if (newx >= tlinelen(TLINE(newy)))
    280  				break;
    281  
    282 -			gp = &term.line[newy][newx];
    283 +			gp = &TLINE(newy)[newx];
    284  			delim = ISDELIM(gp->u);
    285 -			if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
    286 -					|| (delim && gp->u != prevgp->u)))
    287 +			if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim ||
    288 +			    (delim && !(gp->u == ' ' && prevgp->u == ' '))))
    289  				break;
    290  
    291  			*x = newx;
    292 @@ -570,18 +651,14 @@ selsnap(int *x, int *y, int direction)
    293  		 */
    294  		*x = (direction < 0) ? 0 : term.col - 1;
    295  		if (direction < 0) {
    296 -			for (; *y > 0; *y += direction) {
    297 -				if (!(term.line[*y-1][term.col-1].mode
    298 -						& ATTR_WRAP)) {
    299 +			for (; *y > rtop; *y -= 1) {
    300 +				if (!tiswrapped(TLINE(*y-1)))
    301  					break;
    302 -				}
    303  			}
    304  		} else if (direction > 0) {
    305 -			for (; *y < term.row-1; *y += direction) {
    306 -				if (!(term.line[*y][term.col-1].mode
    307 -						& ATTR_WRAP)) {
    308 +			for (; *y < rbot; *y += 1) {
    309 +				if (!tiswrapped(TLINE(*y)))
    310  					break;
    311 -				}
    312  			}
    313  		}
    314  		break;
    315 @@ -592,40 +669,34 @@ char *
    316  getsel(void)
    317  {
    318  	char *str, *ptr;
    319 -	int y, bufsize, lastx, linelen;
    320 -	const Glyph *gp, *last;
    321 +	int y, lastx, linelen;
    322 +	const Glyph *gp, *lgp;
    323  
    324 -	if (sel.ob.x == -1)
    325 +	if (sel.ob.x == -1 || sel.alt != IS_SET(MODE_ALTSCREEN))
    326  		return NULL;
    327  
    328 -	bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ;
    329 -	ptr = str = xmalloc(bufsize);
    330 +	str = xmalloc((term.col + 1) * (sel.ne.y - sel.nb.y + 1) * UTF_SIZ);
    331 +	ptr = str;
    332  
    333  	/* append every set & selected glyph to the selection */
    334  	for (y = sel.nb.y; y <= sel.ne.y; y++) {
    335 -		if ((linelen = tlinelen(y)) == 0) {
    336 +		Line line = TLINE(y);
    337 +
    338 +		if ((linelen = tlinelen(line)) == 0) {
    339  			*ptr++ = '\n';
    340  			continue;
    341  		}
    342  
    343  		if (sel.type == SEL_RECTANGULAR) {
    344 -			gp = &term.line[y][sel.nb.x];
    345 +			gp = &line[sel.nb.x];
    346  			lastx = sel.ne.x;
    347  		} else {
    348 -			gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0];
    349 +			gp = &line[sel.nb.y == y ? sel.nb.x : 0];
    350  			lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
    351  		}
    352 -		last = &term.line[y][MIN(lastx, linelen-1)];
    353 -		while (last >= gp && last->u == ' ')
    354 -			--last;
    355 -
    356 -		for ( ; gp <= last; ++gp) {
    357 -			if (gp->mode & ATTR_WDUMMY)
    358 -				continue;
    359 -
    360 -			ptr += utf8encode(gp->u, ptr);
    361 -		}
    362 +		lgp = &line[MIN(lastx, linelen-1)];
    363  
    364 +		ptr = tgetglyphs(ptr, gp, lgp);
    365  		/*
    366  		 * Copy and pasting of line endings is inconsistent
    367  		 * in the inconsistent terminal and GUI world.
    368 @@ -636,10 +707,10 @@ getsel(void)
    369  		 * FIXME: Fix the computer world.
    370  		 */
    371  		if ((y < sel.ne.y || lastx >= linelen) &&
    372 -		    (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR))
    373 +		    (!(lgp->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR))
    374  			*ptr++ = '\n';
    375  	}
    376 -	*ptr = 0;
    377 +	*ptr = '\0';
    378  	return str;
    379  }
    380  
    381 @@ -648,9 +719,15 @@ selclear(void)
    382  {
    383  	if (sel.ob.x == -1)
    384  		return;
    385 +	selremove();
    386 +	tsetdirt(sel.nb.y, sel.ne.y);
    387 +}
    388 +
    389 +void
    390 +selremove(void)
    391 +{
    392  	sel.mode = SEL_IDLE;
    393  	sel.ob.x = -1;
    394 -	tsetdirt(sel.nb.y, sel.ne.y);
    395  }
    396  
    397  void
    398 @@ -852,6 +929,7 @@ ttywrite(const char *s, size_t n, int may_echo)
    399  {
    400  	const char *next;
    401  
    402 +	kscrolldown(&((Arg){ .i = term.scr }));
    403  	if (may_echo && IS_SET(MODE_ECHO))
    404  		twrite(s, n, 1);
    405  
    406 @@ -987,7 +1065,7 @@ tsetdirtattr(int attr)
    407  	for (i = 0; i < term.row-1; i++) {
    408  		for (j = 0; j < term.col-1; j++) {
    409  			if (term.line[i][j].mode & attr) {
    410 -				tsetdirt(i, i);
    411 +				term.dirty[i] = 1;
    412  				break;
    413  			}
    414  		}
    415 @@ -997,7 +1075,8 @@ tsetdirtattr(int attr)
    416  void
    417  tfulldirt(void)
    418  {
    419 -	tsetdirt(0, term.row-1);
    420 +  for (int i = 0; i < term.row; i++)
    421 +		term.dirty[i] = 1;
    422  }
    423  
    424  void
    425 @@ -1014,110 +1093,259 @@ tcursor(int mode)
    426  	}
    427  }
    428  
    429 +void
    430 +tresetcursor(void)
    431 +{
    432 +	term.c = (TCursor){ { .mode = ATTR_NULL, .fg = defaultfg, .bg = defaultbg },
    433 +	                    .x = 0, .y = 0, .state = CURSOR_DEFAULT };
    434 +}
    435 +
    436  void
    437  treset(void)
    438  {
    439  	uint i;
    440 +  int x, y;
    441  
    442 -	term.c = (TCursor){{
    443 -		.mode = ATTR_NULL,
    444 -		.fg = defaultfg,
    445 -		.bg = defaultbg
    446 -	}, .x = 0, .y = 0, .state = CURSOR_DEFAULT};
    447 +	tresetcursor();
    448  
    449  	memset(term.tabs, 0, term.col * sizeof(*term.tabs));
    450  	for (i = tabspaces; i < term.col; i += tabspaces)
    451  		term.tabs[i] = 1;
    452  	term.top = 0;
    453 +	term.histf = 0;
    454 +	term.scr = 0;
    455  	term.bot = term.row - 1;
    456  	term.mode = MODE_WRAP|MODE_UTF8;
    457  	memset(term.trantbl, CS_USA, sizeof(term.trantbl));
    458  	term.charset = 0;
    459  
    460 +  selremove();
    461  	for (i = 0; i < 2; i++) {
    462 -		tmoveto(0, 0);
    463 -		tcursor(CURSOR_SAVE);
    464 -		tclearregion(0, 0, term.col-1, term.row-1);
    465 +  	tcursor(CURSOR_SAVE); /* reset saved cursor */
    466 +		for (y = 0; y < term.row; y++)
    467 +			for (x = 0; x < term.col; x++)
    468 +				tclearglyph(&term.line[y][x], 0);
    469  		tswapscreen();
    470  	}
    471 +  tfulldirt();
    472  }
    473  
    474  void
    475  tnew(int col, int row)
    476  {
    477 -	term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } };
    478 -	tresize(col, row);
    479 -	treset();
    480 +	int i, j;
    481 +
    482 +	for (i = 0; i < 2; i++) {
    483 +		term.line = xmalloc(row * sizeof(Line));
    484 +		for (j = 0; j < row; j++)
    485 +			term.line[j] = xmalloc(col * sizeof(Glyph));
    486 +		term.col = col, term.row = row;
    487 +		tswapscreen();
    488 +	}
    489 +	term.dirty = xmalloc(row * sizeof(*term.dirty));
    490 +	term.tabs = xmalloc(col * sizeof(*term.tabs));
    491 +	for (i = 0; i < HISTSIZE; i++)
    492 +		term.hist[i] = xmalloc(col * sizeof(Glyph));
    493 +  treset();
    494  }
    495  
    496 +/* handle it with care */
    497  void
    498  tswapscreen(void)
    499  {
    500 -	Line *tmp = term.line;
    501 +	static Line *altline;
    502 +	static int altcol, altrow;
    503 +	Line *tmpline = term.line;
    504 +	int tmpcol = term.col, tmprow = term.row;
    505  
    506 -	term.line = term.alt;
    507 -	term.alt = tmp;
    508 +	term.line = altline;
    509 +	term.col = altcol, term.row = altrow;
    510 +	altline = tmpline;
    511 +	altcol = tmpcol, altrow = tmprow;
    512  	term.mode ^= MODE_ALTSCREEN;
    513 +}
    514 +
    515 +void
    516 +tloaddefscreen(int clear, int loadcursor)
    517 +{
    518 +	int col, row, alt = IS_SET(MODE_ALTSCREEN);
    519 +
    520 +	if (alt) {
    521 +		if (clear)
    522 +			tclearregion(0, 0, term.col-1, term.row-1, 1);
    523 +		col = term.col, row = term.row;
    524 +		tswapscreen();
    525 +	}
    526 +	if (loadcursor)
    527 +		tcursor(CURSOR_LOAD);
    528 +	if (alt)
    529 +		tresizedef(col, row);
    530 +}
    531 +
    532 +void
    533 +tloadaltscreen(int clear, int savecursor)
    534 +{
    535 +	int col, row, def = !IS_SET(MODE_ALTSCREEN);
    536 +
    537 +	if (savecursor)
    538 +		tcursor(CURSOR_SAVE);
    539 +	if (def) {
    540 +		col = term.col, row = term.row;
    541 +		tswapscreen();
    542 +		term.scr = 0;
    543 +		tresizealt(col, row);
    544 +	}
    545 +	if (clear)
    546 +		tclearregion(0, 0, term.col-1, term.row-1, 1);
    547 +}
    548 +
    549 +int
    550 +tisaltscreen(void)
    551 +{
    552 +	return IS_SET(MODE_ALTSCREEN);
    553 +}
    554 +
    555 +void
    556 +kscrolldown(const Arg* a)
    557 +{
    558 +	int n = a->i;
    559 +
    560 +	if (!term.scr || IS_SET(MODE_ALTSCREEN))
    561 +		return;
    562 +
    563 +	if (n < 0)
    564 +		n = MAX(term.row / -n, 1);
    565 +
    566 +	if (n <= term.scr) {
    567 +		term.scr -= n;
    568 +	} else {
    569 +		n = term.scr;
    570 +		term.scr = 0;
    571 +	}
    572 +
    573 +	if (sel.ob.x != -1 && !sel.alt)
    574 +		selmove(-n); /* negate change in term.scr */
    575  	tfulldirt();
    576  }
    577  
    578  void
    579 -tscrolldown(int orig, int n)
    580 +kscrollup(const Arg* a)
    581  {
    582 -	int i;
    583 +	int n = a->i;
    584 +
    585 +	if (!term.histf || IS_SET(MODE_ALTSCREEN))
    586 +		return;
    587 +
    588 +	if (n < 0)
    589 +		n = MAX(term.row / -n, 1);
    590 +
    591 +	if (term.scr + n <= term.histf) {
    592 +		term.scr += n;
    593 +	} else {
    594 +		n = term.histf - term.scr;
    595 +		term.scr = term.histf;
    596 +	}
    597 +
    598 +	if (sel.ob.x != -1 && !sel.alt)
    599 +		selmove(n); /* negate change in term.scr */
    600 +	tfulldirt();
    601 +}
    602 +
    603 +void
    604 +tscrolldown(int top, int n)
    605 +{
    606 +	int i, bot = term.bot;
    607  	Line temp;
    608  
    609 -	LIMIT(n, 0, term.bot-orig+1);
    610 +	if (n <= 0)
    611 +		return;
    612 +	n = MIN(n, bot-top+1);
    613  
    614 -	tsetdirt(orig, term.bot-n);
    615 -	tclearregion(0, term.bot-n+1, term.col-1, term.bot);
    616 +	tsetdirt(top, bot-n);
    617 +	tclearregion(0, bot-n+1, term.col-1, bot, 1);
    618  
    619 -	for (i = term.bot; i >= orig+n; i--) {
    620 +	for (i = bot; i >= top+n; i--) {
    621  		temp = term.line[i];
    622  		term.line[i] = term.line[i-n];
    623  		term.line[i-n] = temp;
    624  	}
    625  
    626 -	selscroll(orig, n);
    627 +	if (sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN))
    628 +		selscroll(top, bot, n);
    629  }
    630  
    631  void
    632 -tscrollup(int orig, int n)
    633 +tscrollup(int top, int bot, int n, int mode)
    634  {
    635 -	int i;
    636 +	int i, j, s;
    637 +	int alt = IS_SET(MODE_ALTSCREEN);
    638 +	int savehist = !alt && top == 0 && mode != SCROLL_NOSAVEHIST;
    639  	Line temp;
    640  
    641 -	LIMIT(n, 0, term.bot-orig+1);
    642 +	if (n <= 0)
    643 +		return;
    644 +	n = MIN(n, bot-top+1);
    645 +
    646 +	if (savehist) {
    647 +		for (i = 0; i < n; i++) {
    648 +			term.histi = (term.histi + 1) % HISTSIZE;
    649 +			temp = term.hist[term.histi];
    650 +			for (j = 0; j < term.col; j++)
    651 +				tclearglyph(&temp[j], 1);
    652 +			term.hist[term.histi] = term.line[i];
    653 +			term.line[i] = temp;
    654 +		}
    655 +		term.histf = MIN(term.histf + n, HISTSIZE);
    656 +		s = n;
    657 +		if (term.scr) {
    658 +			j = term.scr;
    659 +			term.scr = MIN(j + n, HISTSIZE);
    660 +			s = j + n - term.scr;
    661 +		}
    662 +		if (mode != SCROLL_RESIZE)
    663 +			tfulldirt();
    664 +	} else {
    665 +		tclearregion(0, top, term.col-1, top+n-1, 1);
    666 +		tsetdirt(top+n, bot);
    667 +	}
    668  
    669 -	tclearregion(0, orig, term.col-1, orig+n-1);
    670 -	tsetdirt(orig+n, term.bot);
    671  
    672 -	for (i = orig; i <= term.bot-n; i++) {
    673 +	for (i = top; i <= bot-n; i++) {
    674  		temp = term.line[i];
    675  		term.line[i] = term.line[i+n];
    676  		term.line[i+n] = temp;
    677  	}
    678  
    679 -	selscroll(orig, -n);
    680 +	if (sel.ob.x != -1 && sel.alt == alt) {
    681 +		if (!savehist) {
    682 +			selscroll(top, bot, -n);
    683 +		} else if (s > 0) {
    684 +			selmove(-s);
    685 +			if (-term.scr + sel.nb.y < -term.histf)
    686 +				selremove();
    687 +		}
    688 +	}
    689  }
    690  
    691  void
    692 -selscroll(int orig, int n)
    693 +selmove(int n)
    694  {
    695 -	if (sel.ob.x == -1)
    696 -		return;
    697 +	sel.ob.y += n, sel.nb.y += n;
    698 +	sel.oe.y += n, sel.ne.y += n;
    699 +}
    700 +
    701 +void
    702 +selscroll(int top, int bot, int n)
    703 +{
    704 +	/* turn absolute coordinates into relative */
    705 +	top += term.scr, bot += term.scr;
    706  
    707 -	if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) {
    708 +	if (BETWEEN(sel.nb.y, top, bot) != BETWEEN(sel.ne.y, top, bot)) {
    709  		selclear();
    710 -	} else if (BETWEEN(sel.nb.y, orig, term.bot)) {
    711 -		sel.ob.y += n;
    712 -		sel.oe.y += n;
    713 -		if (sel.ob.y < term.top || sel.ob.y > term.bot ||
    714 -		    sel.oe.y < term.top || sel.oe.y > term.bot) {
    715 +	} else if (BETWEEN(sel.nb.y, top, bot)) {
    716 +		selmove(n);
    717 +		if (sel.nb.y < top || sel.ne.y > bot)
    718  			selclear();
    719 -		} else {
    720 -			selnormalize();
    721 -		}
    722  	}
    723  }
    724  
    725 @@ -1127,7 +1355,7 @@ tnewline(int first_col)
    726  	int y = term.c.y;
    727  
    728  	if (y == term.bot) {
    729 -		tscrollup(term.top, 1);
    730 +		tscrollup(term.top, term.bot, 1, SCROLL_SAVEHIST);
    731  	} else {
    732  		y++;
    733  	}
    734 @@ -1217,75 +1445,79 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
    735  	} else if (term.line[y][x].mode & ATTR_WDUMMY) {
    736  		term.line[y][x-1].u = ' ';
    737  		term.line[y][x-1].mode &= ~ATTR_WIDE;
    738 -	}
    739 +  }
    740  
    741  	term.dirty[y] = 1;
    742  	term.line[y][x] = *attr;
    743  	term.line[y][x].u = u;
    744 +	term.line[y][x].mode |= ATTR_SET;
    745  }
    746  
    747  void
    748 -tclearregion(int x1, int y1, int x2, int y2)
    749 +tclearglyph(Glyph *gp, int usecurattr)
    750  {
    751 -	int x, y, temp;
    752 -	Glyph *gp;
    753 +	if (usecurattr) {
    754 +		gp->fg = term.c.attr.fg;
    755 +		gp->bg = term.c.attr.bg;
    756 +	} else {
    757 +		gp->fg = defaultfg;
    758 +		gp->bg = defaultbg;
    759 +	}
    760 +	gp->mode = ATTR_NULL;
    761 +	gp->u = ' ';
    762 +}
    763  
    764 -	if (x1 > x2)
    765 -		temp = x1, x1 = x2, x2 = temp;
    766 -	if (y1 > y2)
    767 -		temp = y1, y1 = y2, y2 = temp;
    768 +void
    769 +tclearregion(int x1, int y1, int x2, int y2, int usecurattr)
    770 +{
    771 +	int x, y;
    772  
    773 -	LIMIT(x1, 0, term.col-1);
    774 -	LIMIT(x2, 0, term.col-1);
    775 -	LIMIT(y1, 0, term.row-1);
    776 -	LIMIT(y2, 0, term.row-1);
    777 +	/* regionselected() takes relative coordinates */
    778 +	if (regionselected(x1+term.scr, y1+term.scr, x2+term.scr, y2+term.scr))
    779 +		selremove();
    780  
    781  	for (y = y1; y <= y2; y++) {
    782  		term.dirty[y] = 1;
    783 -		for (x = x1; x <= x2; x++) {
    784 -			gp = &term.line[y][x];
    785 -			if (selected(x, y))
    786 -				selclear();
    787 -			gp->fg = term.c.attr.fg;
    788 -			gp->bg = term.c.attr.bg;
    789 -			gp->mode = 0;
    790 -			gp->u = ' ';
    791 -		}
    792 +		for (x = x1; x <= x2; x++)
    793 +			tclearglyph(&term.line[y][x], usecurattr);
    794  	}
    795  }
    796  
    797  void
    798  tdeletechar(int n)
    799  {
    800 -	int dst, src, size;
    801 -	Glyph *line;
    802 -
    803 -	LIMIT(n, 0, term.col - term.c.x);
    804 +	int src, dst, size;
    805 +	Line line;
    806  
    807 +	if (n <= 0)
    808 +		return;
    809  	dst = term.c.x;
    810 -	src = term.c.x + n;
    811 +	src = MIN(term.c.x + n, term.col);
    812  	size = term.col - src;
    813 -	line = term.line[term.c.y];
    814 -
    815 -	memmove(&line[dst], &line[src], size * sizeof(Glyph));
    816 -	tclearregion(term.col-n, term.c.y, term.col-1, term.c.y);
    817 +	if (size > 0) { /* otherwise src would point beyond the array
    818 +	                   https://stackoverflow.com/questions/29844298 */
    819 +		line = term.line[term.c.y];
    820 +		memmove(&line[dst], &line[src], size * sizeof(Glyph));
    821 +	}
    822 +	tclearregion(dst + size, term.c.y, term.col - 1, term.c.y, 1);
    823  }
    824  
    825  void
    826  tinsertblank(int n)
    827  {
    828 -	int dst, src, size;
    829 -	Glyph *line;
    830 -
    831 -	LIMIT(n, 0, term.col - term.c.x);
    832 +	int src, dst, size;
    833 +	Line line;
    834  
    835 -	dst = term.c.x + n;
    836 +	if (n <= 0)
    837 +		return;
    838 +	dst = MIN(term.c.x + n, term.col);
    839  	src = term.c.x;
    840  	size = term.col - dst;
    841 -	line = term.line[term.c.y];
    842 -
    843 -	memmove(&line[dst], &line[src], size * sizeof(Glyph));
    844 -	tclearregion(src, term.c.y, dst - 1, term.c.y);
    845 +	if (size > 0) { /* otherwise dst would point beyond the array */
    846 +		line = term.line[term.c.y];
    847 +		memmove(&line[dst], &line[src], size * sizeof(Glyph));
    848 +	}
    849 +	tclearregion(src, term.c.y, dst - 1, term.c.y, 1);
    850  }
    851  
    852  void
    853 @@ -1299,7 +1531,7 @@ void
    854  tdeleteline(int n)
    855  {
    856  	if (BETWEEN(term.c.y, term.top, term.bot))
    857 -		tscrollup(term.c.y, n);
    858 +		tscrollup(term.c.y, term.bot, n, SCROLL_NOSAVEHIST);
    859  }
    860  
    861  int32_t
    862 @@ -1473,7 +1705,7 @@ tsetscroll(int t, int b)
    863  void
    864  tsetmode(int priv, int set, const int *args, int narg)
    865  {
    866 -	int alt; const int *lim;
    867 +	const int *lim;
    868  
    869  	for (lim = args + narg; args < lim; ++args) {
    870  		if (priv) {
    871 @@ -1534,25 +1766,18 @@ tsetmode(int priv, int set, const int *args, int narg)
    872  				xsetmode(set, MODE_8BIT);
    873  				break;
    874  			case 1049: /* swap screen & set/restore cursor as xterm */
    875 -				if (!allowaltscreen)
    876 -					break;
    877 -				tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
    878 -				/* FALLTHROUGH */
    879  			case 47: /* swap screen */
    880 -			case 1047:
    881 +			case 1047: /* swap screen, clearing alternate screen */
    882  				if (!allowaltscreen)
    883  					break;
    884 -				alt = IS_SET(MODE_ALTSCREEN);
    885 -				if (alt) {
    886 -					tclearregion(0, 0, term.col-1,
    887 -							term.row-1);
    888 -				}
    889 -				if (set ^ alt) /* set is always 1 or 0 */
    890 -					tswapscreen();
    891 -				if (*args != 1049)
    892 -					break;
    893 -				/* FALLTHROUGH */
    894 +				if (set)
    895 +					tloadaltscreen(*args == 1049, *args == 1049);
    896 +				else
    897 +					tloaddefscreen(*args == 1047, *args == 1049);
    898 +				break;
    899  			case 1048:
    900 +				if (!allowaltscreen)
    901 +          break;
    902  				tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
    903  				break;
    904  			case 2004: /* 2004: bracketed paste mode */
    905 @@ -1604,7 +1829,7 @@ void
    906  csihandle(void)
    907  {
    908  	char buf[40];
    909 -	int len;
    910 +	int n, x;
    911  
    912  	switch (csiescseq.mode[0]) {
    913  	default:
    914 @@ -1702,20 +1927,30 @@ csihandle(void)
    915  	case 'J': /* ED -- Clear screen */
    916  		switch (csiescseq.arg[0]) {
    917  		case 0: /* below */
    918 -			tclearregion(term.c.x, term.c.y, term.col-1, term.c.y);
    919 +			tclearregion(term.c.x, term.c.y, term.col-1, term.c.y, 1);
    920  			if (term.c.y < term.row-1) {
    921 -				tclearregion(0, term.c.y+1, term.col-1,
    922 -						term.row-1);
    923 +				tclearregion(0, term.c.y+1, term.col-1, term.row-1, 1);
    924  			}
    925  			break;
    926  		case 1: /* above */
    927 -			if (term.c.y > 1)
    928 -				tclearregion(0, 0, term.col-1, term.c.y-1);
    929 -			tclearregion(0, term.c.y, term.c.x, term.c.y);
    930 +			if (term.c.y >= 1)
    931 +				tclearregion(0, 0, term.col-1, term.c.y-1, 1);
    932 +			tclearregion(0, term.c.y, term.c.x, term.c.y, 1);
    933  			break;
    934  		case 2: /* all */
    935 -			tclearregion(0, 0, term.col-1, term.row-1);
    936 -			break;
    937 +			if (IS_SET(MODE_ALTSCREEN)) {
    938 +  			tclearregion(0, 0, term.col-1, term.row-1, 1);
    939 +  			break;
    940 +      }
    941 +			/* vte does this:
    942 +			tscrollup(0, term.row-1, term.row, SCROLL_SAVEHIST); */
    943 +      
    944 +			/* alacritty does this: */
    945 +			for (n = term.row-1; n >= 0 && tlinelen(term.line[n]) == 0; n--);
    946 +			if (n >= 0)
    947 +				tscrollup(0, term.row-1, n+1, SCROLL_SAVEHIST);
    948 +			tscrollup(0, term.row-1, term.row-n-1, SCROLL_NOSAVEHIST);
    949 +      break;
    950  		default:
    951  			goto unknown;
    952  		}
    953 @@ -1723,20 +1958,20 @@ csihandle(void)
    954  	case 'K': /* EL -- Clear line */
    955  		switch (csiescseq.arg[0]) {
    956  		case 0: /* right */
    957 -			tclearregion(term.c.x, term.c.y, term.col-1,
    958 -					term.c.y);
    959 +			tclearregion(term.c.x, term.c.y, term.col-1, term.c.y, 1);
    960  			break;
    961  		case 1: /* left */
    962 -			tclearregion(0, term.c.y, term.c.x, term.c.y);
    963 +			tclearregion(0, term.c.y, term.c.x, term.c.y, 1);
    964  			break;
    965  		case 2: /* all */
    966 -			tclearregion(0, term.c.y, term.col-1, term.c.y);
    967 +			tclearregion(0, term.c.y, term.col-1, term.c.y, 1);
    968  			break;
    969  		}
    970  		break;
    971  	case 'S': /* SU -- Scroll <n> line up */
    972  		DEFAULT(csiescseq.arg[0], 1);
    973 -		tscrollup(term.top, csiescseq.arg[0]);
    974 +		/* xterm, urxvt, alacritty save this in history */
    975 +		tscrollup(term.top, term.bot, csiescseq.arg[0], SCROLL_SAVEHIST);
    976  		break;
    977  	case 'T': /* SD -- Scroll <n> line down */
    978  		DEFAULT(csiescseq.arg[0], 1);
    979 @@ -1754,9 +1989,11 @@ csihandle(void)
    980  		tdeleteline(csiescseq.arg[0]);
    981  		break;
    982  	case 'X': /* ECH -- Erase <n> char */
    983 +		if (csiescseq.arg[0] < 0)
    984 +			return;
    985  		DEFAULT(csiescseq.arg[0], 1);
    986 -		tclearregion(term.c.x, term.c.y,
    987 -				term.c.x + csiescseq.arg[0] - 1, term.c.y);
    988 +		x = MIN(term.c.x + csiescseq.arg[0], term.col) - 1;
    989 +		tclearregion(term.c.x, term.c.y, x, term.c.y, 1);
    990  		break;
    991  	case 'P': /* DCH -- Delete <n> char */
    992  		DEFAULT(csiescseq.arg[0], 1);
    993 @@ -1778,9 +2015,9 @@ csihandle(void)
    994  		break;
    995  	case 'n': /* DSR – Device Status Report (cursor position) */
    996  		if (csiescseq.arg[0] == 6) {
    997 -			len = snprintf(buf, sizeof(buf), "\033[%i;%iR",
    998 +			n = snprintf(buf, sizeof(buf), "\033[%i;%iR",
    999  					term.c.y+1, term.c.x+1);
   1000 -			ttywrite(buf, len, 0);
   1001 +			ttywrite(buf, n, 0);
   1002  		}
   1003  		break;
   1004  	case 'r': /* DECSTBM -- Set Scrolling Region */
   1005 @@ -1849,29 +2086,30 @@ strhandle(void)
   1006  	int j, narg, par;
   1007  
   1008  	term.esc &= ~(ESC_STR_END|ESC_STR);
   1009 -	strparse();
   1010 -	par = (narg = strescseq.narg) ? atoi(strescseq.args[0]) : 0;
   1011 +	strescseq.buf[strescseq.len] = '\0';
   1012  
   1013  	switch (strescseq.type) {
   1014  	case ']': /* OSC -- Operating System Command */
   1015 +		strparse();
   1016 +		par = (narg = strescseq.narg) ? atoi(STRESCARGJUST(0)) : 0;
   1017  		switch (par) {
   1018  		case 0:
   1019  			if (narg > 1) {
   1020 -				xsettitle(strescseq.args[1]);
   1021 -				xseticontitle(strescseq.args[1]);
   1022 +				xsettitle(STRESCARGREST(1));
   1023 +				xseticontitle(STRESCARGREST(1));
   1024  			}
   1025  			return;
   1026  		case 1:
   1027  			if (narg > 1)
   1028 -				xseticontitle(strescseq.args[1]);
   1029 +				xseticontitle(STRESCARGREST(1));
   1030  			return;
   1031  		case 2:
   1032  			if (narg > 1)
   1033 -				xsettitle(strescseq.args[1]);
   1034 +				xsettitle(STRESCARGREST(1));
   1035  			return;
   1036  		case 52:
   1037  			if (narg > 2 && allowwindowops) {
   1038 -				dec = base64dec(strescseq.args[2]);
   1039 +				dec = base64dec(STRESCARGREST(1));
   1040  				if (dec) {
   1041  					xsetsel(dec);
   1042  					xclipcopy();
   1043 @@ -1883,10 +2121,10 @@ strhandle(void)
   1044  		case 4: /* color set */
   1045  			if (narg < 3)
   1046  				break;
   1047 -			p = strescseq.args[2];
   1048 +			p = STRESCARGREST(2);
   1049  			/* FALLTHROUGH */
   1050  		case 104: /* color reset, here p = NULL */
   1051 -			j = (narg > 1) ? atoi(strescseq.args[1]) : -1;
   1052 +			j = (narg > 1) ? atoi(STRESCARGREST(1)) : -1;
   1053  			if (xsetcolorname(j, p)) {
   1054  				if (par == 104 && narg <= 1)
   1055  					return; /* color reset without parameter */
   1056 @@ -1903,7 +2141,7 @@ strhandle(void)
   1057  		}
   1058  		break;
   1059  	case 'k': /* old title set compatibility */
   1060 -		xsettitle(strescseq.args[0]);
   1061 +		xsettitle(STRESCARGREST(0));
   1062  		return;
   1063  	case 'P': /* DCS -- Device Control String */
   1064  	case '_': /* APC -- Application Program Command */
   1065 @@ -1922,19 +2160,18 @@ strparse(void)
   1066  	char *p = strescseq.buf;
   1067  
   1068  	strescseq.narg = 0;
   1069 -	strescseq.buf[strescseq.len] = '\0';
   1070  
   1071  	if (*p == '\0')
   1072  		return;
   1073  
   1074  	while (strescseq.narg < STR_ARG_SIZ) {
   1075 -		strescseq.args[strescseq.narg++] = p;
   1076  		while ((c = *p) != ';' && c != '\0')
   1077 -			++p;
   1078 +			p++;
   1079 +		strescseq.args[strescseq.narg++] = p;
   1080  		if (c == '\0')
   1081  			return;
   1082 -		*p++ = '\0';
   1083 -	}
   1084 +		p++;
   1085 +  }
   1086  }
   1087  
   1088  void
   1089 @@ -2022,16 +2259,8 @@ tdumpsel(void)
   1090  void
   1091  tdumpline(int n)
   1092  {
   1093 -	char buf[UTF_SIZ];
   1094 -	const Glyph *bp, *end;
   1095 -
   1096 -	bp = &term.line[n][0];
   1097 -	end = &bp[MIN(tlinelen(n), term.col) - 1];
   1098 -	if (bp != end || bp->u != ' ') {
   1099 -		for ( ; bp <= end; ++bp)
   1100 -			tprinter(buf, utf8encode(bp->u, buf));
   1101 -	}
   1102 -	tprinter("\n", 1);
   1103 +	char str[(term.col + 1) * UTF_SIZ];
   1104 +  tprinter(str, tgetline(str, &term.line[n][0]));
   1105  }
   1106  
   1107  void
   1108 @@ -2252,7 +2481,7 @@ eschandle(uchar ascii)
   1109  		return 0;
   1110  	case 'D': /* IND -- Linefeed */
   1111  		if (term.c.y == term.bot) {
   1112 -			tscrollup(term.top, 1);
   1113 +			tscrollup(term.top, term.bot, 1, SCROLL_SAVEHIST);
   1114  		} else {
   1115  			tmoveto(term.c.x, term.c.y+1);
   1116  		}
   1117 @@ -2405,7 +2634,8 @@ check_control_code:
   1118  		 */
   1119  		return;
   1120  	}
   1121 -	if (selected(term.c.x, term.c.y))
   1122 +	/* selected() takes relative coordinates */
   1123 +	if (selected(term.c.x + term.scr, term.c.y + term.scr))
   1124  		selclear();
   1125  
   1126  	gp = &term.line[term.c.y][term.c.x];
   1127 @@ -2436,6 +2666,7 @@ check_control_code:
   1128  	if (term.c.x+width < term.col) {
   1129  		tmoveto(term.c.x+width, term.c.y);
   1130  	} else {
   1131 +		term.wrapcwidth[IS_SET(MODE_ALTSCREEN)] = width;
   1132  		term.c.state |= CURSOR_WRAPNEXT;
   1133  	}
   1134  }
   1135 @@ -2473,85 +2704,275 @@ twrite(const char *buf, int buflen, int show_ctrl)
   1136  }
   1137  
   1138  void
   1139 -tresize(int col, int row)
   1140 +treflow(int col, int row)
   1141  {
   1142 -	int i;
   1143 -	int minrow = MIN(row, term.row);
   1144 -	int mincol = MIN(col, term.col);
   1145 -	int *bp;
   1146 -	TCursor c;
   1147 -
   1148 -	if (col < 1 || row < 1) {
   1149 -		fprintf(stderr,
   1150 -		        "tresize: error resizing to %dx%d\n", col, row);
   1151 -		return;
   1152 +	int i, j;
   1153 +	int oce, nce, bot, scr;
   1154 +	int ox = 0, oy = -term.histf, nx = 0, ny = -1, len;
   1155 +	int cy = -1; /* proxy for new y coordinate of cursor */
   1156 +	int nlines;
   1157 +	Line *buf, line;
   1158 +
   1159 +	/* y coordinate of cursor line end */
   1160 +	for (oce = term.c.y; oce < term.row - 1 &&
   1161 +	                     tiswrapped(term.line[oce]); oce++);
   1162 +
   1163 +	nlines = term.histf + oce + 1;
   1164 +	if (col < term.col) {
   1165 +		/* each line can take this many lines after reflow */
   1166 +		j = (term.col + col - 1) / col;
   1167 +		nlines = j * nlines;
   1168 +		if (nlines > HISTSIZE + RESIZEBUFFER + row) {
   1169 +			nlines = HISTSIZE + RESIZEBUFFER + row;
   1170 +			oy = -(nlines / j - oce - 1);
   1171 +		}
   1172  	}
   1173 +	buf = xmalloc(nlines * sizeof(Line));
   1174 +	do {
   1175 +		if (!nx)
   1176 +			buf[++ny] = xmalloc(col * sizeof(Glyph));
   1177 +		if (!ox) {
   1178 +			line = TLINEABS(oy);
   1179 +			len = tlinelen(line);
   1180 +		}
   1181 +		if (oy == term.c.y) {
   1182 +			if (!ox)
   1183 +				len = MAX(len, term.c.x + 1);
   1184 +			/* update cursor */
   1185 +			if (cy < 0 && term.c.x - ox < col - nx) {
   1186 +				term.c.x = nx + term.c.x - ox, cy = ny;
   1187 +				UPDATEWRAPNEXT(0, col);
   1188 +			}
   1189 +		}
   1190 +		/* get reflowed lines in buf */
   1191 +		if (col - nx > len - ox) {
   1192 +			memcpy(&buf[ny][nx], &line[ox], (len-ox) * sizeof(Glyph));
   1193 +			nx += len - ox;
   1194 +			if (len == 0 || !(line[len - 1].mode & ATTR_WRAP)) {
   1195 +				for (j = nx; j < col; j++)
   1196 +					tclearglyph(&buf[ny][j], 0);
   1197 +				nx = 0;
   1198 +			} else if (nx > 0) {
   1199 +				buf[ny][nx - 1].mode &= ~ATTR_WRAP;
   1200 +			}
   1201 +			ox = 0, oy++;
   1202 +		} else if (col - nx == len - ox) {
   1203 +			memcpy(&buf[ny][nx], &line[ox], (col-nx) * sizeof(Glyph));
   1204 +			ox = 0, oy++, nx = 0;
   1205 +		} else/* if (col - nx < len - ox) */ {
   1206 +			memcpy(&buf[ny][nx], &line[ox], (col-nx) * sizeof(Glyph));
   1207 +    	ox += col - nx;
   1208 +			buf[ny][col - 1].mode |= ATTR_WRAP;
   1209 +			nx = 0;
   1210 +		}
   1211 +	} while (oy <= oce);
   1212 +	if (nx)
   1213 +		for (j = nx; j < col; j++)
   1214 +			tclearglyph(&buf[ny][j], 0);
   1215  
   1216 -	/*
   1217 -	 * slide screen to keep cursor where we expect it -
   1218 -	 * tscrollup would work here, but we can optimize to
   1219 -	 * memmove because we're freeing the earlier lines
   1220 -	 */
   1221 -	for (i = 0; i <= term.c.y - row; i++) {
   1222 +	/* free extra lines */
   1223 +	for (i = row; i < term.row; i++)
   1224  		free(term.line[i]);
   1225 -		free(term.alt[i]);
   1226 +	/* resize to new height */
   1227 +	term.line = xrealloc(term.line, row * sizeof(Line));
   1228 +
   1229 +	bot = MIN(ny, row - 1);
   1230 +	scr = MAX(row - term.row, 0);
   1231 +	/* update y coordinate of cursor line end */
   1232 +	nce = MIN(oce + scr, bot);
   1233 +	/* update cursor y coordinate */
   1234 +	term.c.y = nce - (ny - cy);
   1235 +	if (term.c.y < 0) {
   1236 +		j = nce, nce = MIN(nce + -term.c.y, bot);
   1237 +		term.c.y += nce - j;
   1238 +		while (term.c.y < 0) {
   1239 +			free(buf[ny--]);
   1240 +			term.c.y++;
   1241 +		}
   1242  	}
   1243 -	/* ensure that both src and dst are not NULL */
   1244 -	if (i > 0) {
   1245 -		memmove(term.line, term.line + i, row * sizeof(Line));
   1246 -		memmove(term.alt, term.alt + i, row * sizeof(Line));
   1247 +	/* allocate new rows */
   1248 +	for (i = row - 1; i > nce; i--) {
   1249 +		term.line[i] = xmalloc(col * sizeof(Glyph));
   1250 +		for (j = 0; j < col; j++)
   1251 +			tclearglyph(&term.line[i][j], 0);
   1252  	}
   1253 -	for (i += row; i < term.row; i++) {
   1254 +	/* fill visible area */
   1255 +	for (/*i = nce */; i >= term.row; i--, ny--)
   1256 +		term.line[i] = buf[ny];
   1257 +	for (/*i = term.row - 1 */; i >= 0; i--, ny--) {
   1258  		free(term.line[i]);
   1259 -		free(term.alt[i]);
   1260 +		term.line[i] = buf[ny];
   1261  	}
   1262 +	/* fill lines in history buffer and update term.histf */
   1263 +	for (/*i = -1 */; ny >= 0 && i >= -HISTSIZE; i--, ny--) {
   1264 +		j = (term.histi + i + 1 + HISTSIZE) % HISTSIZE;
   1265 +		free(term.hist[j]);
   1266 +		term.hist[j] = buf[ny];
   1267 +	}
   1268 +	term.histf = -i - 1;
   1269 +	term.scr = MIN(term.scr, term.histf);
   1270 +	/* resize rest of the history lines */
   1271 +	for (/*i = -term.histf - 1 */; i >= -HISTSIZE; i--) {
   1272 +		j = (term.histi + i + 1 + HISTSIZE) % HISTSIZE;
   1273 +		term.hist[j] = xrealloc(term.hist[j], col * sizeof(Glyph));
   1274 +	}
   1275 +	free(buf);
   1276 +}
   1277  
   1278 -	/* resize to new height */
   1279 -	term.line = xrealloc(term.line, row * sizeof(Line));
   1280 -	term.alt  = xrealloc(term.alt,  row * sizeof(Line));
   1281 -	term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
   1282 -	term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
   1283 +void
   1284 +rscrolldown(int n)
   1285 +{
   1286 +	int i;
   1287 +	Line temp;
   1288  
   1289 -	/* resize each row to new width, zero-pad if needed */
   1290 -	for (i = 0; i < minrow; i++) {
   1291 -		term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
   1292 -		term.alt[i]  = xrealloc(term.alt[i],  col * sizeof(Glyph));
   1293 -	}
   1294 +	/* can never be true as of now
   1295 +	if (IS_SET(MODE_ALTSCREEN))
   1296 +		return; */
   1297  
   1298 -	/* allocate any new rows */
   1299 -	for (/* i = minrow */; i < row; i++) {
   1300 -		term.line[i] = xmalloc(col * sizeof(Glyph));
   1301 -		term.alt[i] = xmalloc(col * sizeof(Glyph));
   1302 +	if ((n = MIN(n, term.histf)) <= 0)
   1303 +		return;
   1304 +
   1305 +	for (i = term.c.y + n; i >= n; i--) {
   1306 +		temp = term.line[i];
   1307 +		term.line[i] = term.line[i-n];
   1308 +		term.line[i-n] = temp;
   1309  	}
   1310 +	for (/*i = n - 1 */; i >= 0; i--) {
   1311 +		temp = term.line[i];
   1312 +		term.line[i] = term.hist[term.histi];
   1313 +		term.hist[term.histi] = temp;
   1314 +		term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE;
   1315 +	}
   1316 +	term.c.y += n;
   1317 +	term.histf -= n;
   1318 +	if ((i = term.scr - n) >= 0) {
   1319 +		term.scr = i;
   1320 +	} else {
   1321 +		term.scr = 0;
   1322 +		if (sel.ob.x != -1 && !sel.alt)
   1323 +			selmove(-i);
   1324 +	}
   1325 +}
   1326 +
   1327 +void
   1328 +tresize(int col, int row)
   1329 +{
   1330 +	int *bp;
   1331 +
   1332 +	/* col and row are always MAX(_, 1)
   1333 +	if (col < 1 || row < 1) {
   1334 +		fprintf(stderr, "tresize: error resizing to %dx%d\n", col, row);
   1335 +		return;
   1336 +	} */
   1337 +
   1338 +	term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
   1339 +	term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
   1340  	if (col > term.col) {
   1341  		bp = term.tabs + term.col;
   1342 -
   1343  		memset(bp, 0, sizeof(*term.tabs) * (col - term.col));
   1344  		while (--bp > term.tabs && !*bp)
   1345  			/* nothing */ ;
   1346  		for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces)
   1347  			*bp = 1;
   1348  	}
   1349 -	/* update terminal size */
   1350 -	term.col = col;
   1351 -	term.row = row;
   1352 -	/* reset scrolling region */
   1353 -	tsetscroll(0, row-1);
   1354 -	/* make use of the LIMIT in tmoveto */
   1355 -	tmoveto(term.c.x, term.c.y);
   1356 -	/* Clearing both screens (it makes dirty all lines) */
   1357 -	c = term.c;
   1358 -	for (i = 0; i < 2; i++) {
   1359 -		if (mincol < col && 0 < minrow) {
   1360 -			tclearregion(mincol, 0, col - 1, minrow - 1);
   1361 +
   1362 +	if (IS_SET(MODE_ALTSCREEN))
   1363 +		tresizealt(col, row);
   1364 +	else
   1365 +		tresizedef(col, row);
   1366 +}
   1367 +
   1368 +void
   1369 +tresizedef(int col, int row)
   1370 +{
   1371 +	int i, j;
   1372 +
   1373 +	/* return if dimensions haven't changed */
   1374 +	if (term.col == col && term.row == row) {
   1375 +		tfulldirt();
   1376 +		return;
   1377 +	}
   1378 +	if (col != term.col) {
   1379 +		if (!sel.alt)
   1380 +			selremove();
   1381 +		treflow(col, row);
   1382 +	} else {
   1383 +		/* slide screen up if otherwise cursor would get out of the screen */
   1384 +		if (term.c.y >= row) {
   1385 +			tscrollup(0, term.row - 1, term.c.y - row + 1, SCROLL_RESIZE);
   1386 +			term.c.y = row - 1;
   1387  		}
   1388 -		if (0 < col && minrow < row) {
   1389 -			tclearregion(0, minrow, col - 1, row - 1);
   1390 +		for (i = row; i < term.row; i++)
   1391 +			free(term.line[i]);
   1392 +
   1393 +		/* resize to new height */
   1394 +		term.line = xrealloc(term.line, row * sizeof(Line));
   1395 +		/* allocate any new rows */
   1396 +		for (i = term.row; i < row; i++) {
   1397 +			term.line[i] = xmalloc(col * sizeof(Glyph));
   1398 +			for (j = 0; j < col; j++)
   1399 +				tclearglyph(&term.line[i][j], 0);
   1400  		}
   1401 -		tswapscreen();
   1402 -		tcursor(CURSOR_LOAD);
   1403 +		/* scroll down as much as height has increased */
   1404 +		rscrolldown(row - term.row);
   1405 +	}
   1406 +	/* update terminal size */
   1407 +	term.col = col, term.row = row;
   1408 +	/* reset scrolling region */
   1409 +	term.top = 0, term.bot = row - 1;
   1410 +	/* dirty all lines */
   1411 +	tfulldirt();
   1412 +}
   1413 +
   1414 +void
   1415 +tresizealt(int col, int row)
   1416 +{
   1417 +	int i, j;
   1418 +
   1419 +	/* return if dimensions haven't changed */
   1420 +	if (term.col == col && term.row == row) {
   1421 +		tfulldirt();
   1422 +		return;
   1423  	}
   1424 -	term.c = c;
   1425 +	if (sel.alt)
   1426 +		selremove();
   1427 +	/* slide screen up if otherwise cursor would get out of the screen */
   1428 +	for (i = 0; i <= term.c.y - row; i++)
   1429 +		free(term.line[i]);
   1430 +	if (i > 0) {
   1431 +		/* ensure that both src and dst are not NULL */
   1432 +		memmove(term.line, term.line + i, row * sizeof(Line));
   1433 +		term.c.y = row - 1;
   1434 +	}
   1435 +	for (i += row; i < term.row; i++)
   1436 +		free(term.line[i]);
   1437 +	/* resize to new height */
   1438 +	term.line = xrealloc(term.line, row * sizeof(Line));
   1439 +	/* resize to new width */
   1440 +	for (i = 0; i < MIN(row, term.row); i++) {
   1441 +		term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
   1442 +		for (j = term.col; j < col; j++)
   1443 +			tclearglyph(&term.line[i][j], 0);
   1444 +	}
   1445 +	/* allocate any new rows */
   1446 +	for (/*i = MIN(row, term.row) */; i < row; i++) {
   1447 +		term.line[i] = xmalloc(col * sizeof(Glyph));
   1448 +		for (j = 0; j < col; j++)
   1449 +			tclearglyph(&term.line[i][j], 0);
   1450 +	}
   1451 +	/* update cursor */
   1452 +	if (term.c.x >= col) {
   1453 +		term.c.state &= ~CURSOR_WRAPNEXT;
   1454 +		term.c.x = col - 1;
   1455 +	} else {
   1456 +		UPDATEWRAPNEXT(1, col);
   1457 +	}
   1458 +	/* update terminal size */
   1459 +	term.col = col, term.row = row;
   1460 +	/* reset scrolling region */
   1461 +	term.top = 0, term.bot = row - 1;
   1462 +	/* dirty all lines */
   1463 +	tfulldirt();
   1464  }
   1465  
   1466  void
   1467 @@ -2570,7 +2991,7 @@ drawregion(int x1, int y1, int x2, int y2)
   1468  			continue;
   1469  
   1470  		term.dirty[y] = 0;
   1471 -		xdrawline(term.line[y], x1, y, x2);
   1472 +		xdrawline(TLINE(y), x1, y, x2);
   1473  	}
   1474  }
   1475  
   1476 diff --git a/st.h b/st.h
   1477 index fa2eddf..5b3ea8c 100644
   1478 --- a/st.h
   1479 +++ b/st.h
   1480 @@ -21,18 +21,20 @@
   1481  #define IS_TRUECOL(x)		(1 << 24 & (x))
   1482  
   1483  enum glyph_attribute {
   1484 -	ATTR_NULL       = 0,
   1485 -	ATTR_BOLD       = 1 << 0,
   1486 -	ATTR_FAINT      = 1 << 1,
   1487 -	ATTR_ITALIC     = 1 << 2,
   1488 -	ATTR_UNDERLINE  = 1 << 3,
   1489 -	ATTR_BLINK      = 1 << 4,
   1490 -	ATTR_REVERSE    = 1 << 5,
   1491 -	ATTR_INVISIBLE  = 1 << 6,
   1492 -	ATTR_STRUCK     = 1 << 7,
   1493 -	ATTR_WRAP       = 1 << 8,
   1494 -	ATTR_WIDE       = 1 << 9,
   1495 -	ATTR_WDUMMY     = 1 << 10,
   1496 +  ATTR_NULL       = 0,
   1497 +  ATTR_SET        = 1 << 0,
   1498 +	ATTR_BOLD       = 1 << 1,
   1499 +	ATTR_FAINT      = 1 << 2,
   1500 +	ATTR_ITALIC     = 1 << 3,
   1501 +	ATTR_UNDERLINE  = 1 << 4,
   1502 +	ATTR_BLINK      = 1 << 5,
   1503 +	ATTR_REVERSE    = 1 << 6,
   1504 +	ATTR_INVISIBLE  = 1 << 7,
   1505 +	ATTR_STRUCK     = 1 << 8,
   1506 +	ATTR_WRAP       = 1 << 9,
   1507 +	ATTR_WIDE       = 1 << 10,
   1508 +	ATTR_WDUMMY     = 1 << 11,
   1509 +	ATTR_SELECTED   = 1 << 12,
   1510  	ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
   1511  };
   1512  
   1513 @@ -81,6 +83,8 @@ void die(const char *, ...);
   1514  void redraw(void);
   1515  void draw(void);
   1516  
   1517 +void kscrolldown(const Arg *);
   1518 +void kscrollup(const Arg *);
   1519  void printscreen(const Arg *);
   1520  void printsel(const Arg *);
   1521  void sendbreak(const Arg *);
   1522 @@ -88,6 +92,7 @@ void toggleprinter(const Arg *);
   1523  
   1524  int tattrset(int);
   1525  void tnew(int, int);
   1526 +int tisaltscreen(void);
   1527  void tresize(int, int);
   1528  void tsetdirtattr(int);
   1529  void ttyhangup(void);