dotfiles

My beautiful configs and dotfiles managed by Nix' home-manager
Log | Files | Refs | README | LICENSE

st-scrollback-ringbuffer.diff (18818B)


      1 diff --git a/config.def.h b/config.def.h
      2 index 2cd740a..8b25d40 100644
      3 --- a/config.def.h
      4 +++ b/config.def.h
      5 @@ -201,6 +201,8 @@ static Shortcut shortcuts[] = {
      6  	{ TERMMOD,              XK_Y,           selpaste,       {.i =  0} },
      7  	{ ShiftMask,            XK_Insert,      selpaste,       {.i =  0} },
      8  	{ TERMMOD,              XK_Num_Lock,    numlock,        {.i =  0} },
      9 +	{ ShiftMask,            XK_Page_Up,     kscrollup,      {.i = -1} },
     10 +	{ ShiftMask,            XK_Page_Down,   kscrolldown,    {.i = -1} },
     11  };
     12  
     13  /*
     14 diff --git a/st.c b/st.c
     15 index 57c6e96..a3b3c9d 100644
     16 --- a/st.c
     17 +++ b/st.c
     18 @@ -43,6 +43,10 @@
     19  #define ISCONTROL(c)		(ISCONTROLC0(c) || ISCONTROLC1(c))
     20  #define ISDELIM(u)		(u && wcschr(worddelimiters, u))
     21  
     22 +#define TSCREEN term.screen[IS_SET(MODE_ALTSCREEN)]
     23 +#define TLINEOFFSET(y) (((y) + TSCREEN.cur - TSCREEN.off + TSCREEN.size) % TSCREEN.size)
     24 +#define TLINE(y) (TSCREEN.buffer[TLINEOFFSET(y)])
     25 +
     26  enum term_mode {
     27  	MODE_WRAP        = 1 << 0,
     28  	MODE_INSERT      = 1 << 1,
     29 @@ -109,12 +113,21 @@ typedef struct {
     30  	int alt;
     31  } Selection;
     32  
     33 +/* Screen lines */
     34 +typedef struct {
     35 +	Line* buffer;  /* ring buffer */
     36 +	int size;      /* size of buffer */
     37 +	int cur;       /* start of active screen */
     38 +	int off;       /* scrollback line offset */
     39 +	TCursor sc;    /* saved cursor */
     40 +} LineBuffer;
     41 +
     42  /* Internal representation of the screen */
     43  typedef struct {
     44  	int row;      /* nb row */
     45  	int col;      /* nb col */
     46 -	Line *line;   /* screen */
     47 -	Line *alt;    /* alternate screen */
     48 +	LineBuffer screen[2]; /* screen and alternate screen */
     49 +	int linelen;  /* allocated line length */
     50  	int *dirty;   /* dirtyness of lines */
     51  	TCursor c;    /* cursor */
     52  	int ocx;      /* old cursor col */
     53 @@ -203,6 +216,8 @@ static void tdeftran(char);
     54  static void tstrsequence(uchar);
     55  
     56  static void drawregion(int, int, int, int);
     57 +static void clearline(Line, Glyph, int, int);
     58 +static Line ensureline(Line);
     59  
     60  static void selnormalize(void);
     61  static void selscroll(int, int);
     62 @@ -408,11 +423,12 @@ int
     63  tlinelen(int y)
     64  {
     65  	int i = term.col;
     66 +	Line line = TLINE(y);
     67  
     68 -	if (term.line[y][i - 1].mode & ATTR_WRAP)
     69 +	if (line[i - 1].mode & ATTR_WRAP)
     70  		return i;
     71  
     72 -	while (i > 0 && term.line[y][i - 1].u == ' ')
     73 +	while (i > 0 && line[i - 1].u == ' ')
     74  		--i;
     75  
     76  	return i;
     77 @@ -521,7 +537,7 @@ selsnap(int *x, int *y, int direction)
     78  		 * Snap around if the word wraps around at the end or
     79  		 * beginning of a line.
     80  		 */
     81 -		prevgp = &term.line[*y][*x];
     82 +		prevgp = &TLINE(*y)[*x];
     83  		prevdelim = ISDELIM(prevgp->u);
     84  		for (;;) {
     85  			newx = *x + direction;
     86 @@ -536,14 +552,14 @@ selsnap(int *x, int *y, int direction)
     87  					yt = *y, xt = *x;
     88  				else
     89  					yt = newy, xt = newx;
     90 -				if (!(term.line[yt][xt].mode & ATTR_WRAP))
     91 +				if (!(TLINE(yt)[xt].mode & ATTR_WRAP))
     92  					break;
     93  			}
     94  
     95  			if (newx >= tlinelen(newy))
     96  				break;
     97  
     98 -			gp = &term.line[newy][newx];
     99 +			gp = &TLINE(newy)[newx];
    100  			delim = ISDELIM(gp->u);
    101  			if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
    102  					|| (delim && gp->u != prevgp->u)))
    103 @@ -564,14 +580,14 @@ selsnap(int *x, int *y, int direction)
    104  		*x = (direction < 0) ? 0 : term.col - 1;
    105  		if (direction < 0) {
    106  			for (; *y > 0; *y += direction) {
    107 -				if (!(term.line[*y-1][term.col-1].mode
    108 +				if (!(TLINE(*y-1)[term.col-1].mode
    109  						& ATTR_WRAP)) {
    110  					break;
    111  				}
    112  			}
    113  		} else if (direction > 0) {
    114  			for (; *y < term.row-1; *y += direction) {
    115 -				if (!(term.line[*y][term.col-1].mode
    116 +				if (!(TLINE(*y)[term.col-1].mode
    117  						& ATTR_WRAP)) {
    118  					break;
    119  				}
    120 @@ -602,13 +618,13 @@ getsel(void)
    121  		}
    122  
    123  		if (sel.type == SEL_RECTANGULAR) {
    124 -			gp = &term.line[y][sel.nb.x];
    125 +			gp = &TLINE(y)[sel.nb.x];
    126  			lastx = sel.ne.x;
    127  		} else {
    128 -			gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0];
    129 +			gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0];
    130  			lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
    131  		}
    132 -		last = &term.line[y][MIN(lastx, linelen-1)];
    133 +		last = &TLINE(y)[MIN(lastx, linelen-1)];
    134  		while (last >= gp && last->u == ' ')
    135  			--last;
    136  
    137 @@ -949,12 +965,15 @@ int
    138  tattrset(int attr)
    139  {
    140  	int i, j;
    141 +	int y = TLINEOFFSET(0);
    142  
    143  	for (i = 0; i < term.row-1; i++) {
    144 +		Line line = TSCREEN.buffer[y];
    145  		for (j = 0; j < term.col-1; j++) {
    146 -			if (term.line[i][j].mode & attr)
    147 +			if (line[j].mode & attr)
    148  				return 1;
    149  		}
    150 +		y = (y+1) % TSCREEN.size;
    151  	}
    152  
    153  	return 0;
    154 @@ -976,14 +995,17 @@ void
    155  tsetdirtattr(int attr)
    156  {
    157  	int i, j;
    158 +	int y = TLINEOFFSET(0);
    159  
    160  	for (i = 0; i < term.row-1; i++) {
    161 +		Line line = TSCREEN.buffer[y];
    162  		for (j = 0; j < term.col-1; j++) {
    163 -			if (term.line[i][j].mode & attr) {
    164 +			if (line[j].mode & attr) {
    165  				tsetdirt(i, i);
    166  				break;
    167  			}
    168  		}
    169 +		y = (y+1) % TSCREEN.size;
    170  	}
    171  }
    172  
    173 @@ -996,27 +1018,19 @@ tfulldirt(void)
    174  void
    175  tcursor(int mode)
    176  {
    177 -	static TCursor c[2];
    178 -	int alt = IS_SET(MODE_ALTSCREEN);
    179 -
    180  	if (mode == CURSOR_SAVE) {
    181 -		c[alt] = term.c;
    182 +		TSCREEN.sc = term.c;
    183  	} else if (mode == CURSOR_LOAD) {
    184 -		term.c = c[alt];
    185 -		tmoveto(c[alt].x, c[alt].y);
    186 +		term.c = TSCREEN.sc;
    187 +		tmoveto(term.c.x, term.c.y);
    188  	}
    189  }
    190  
    191  void
    192  treset(void)
    193  {
    194 -	uint i;
    195 -
    196 -	term.c = (TCursor){{
    197 -		.mode = ATTR_NULL,
    198 -		.fg = defaultfg,
    199 -		.bg = defaultbg
    200 -	}, .x = 0, .y = 0, .state = CURSOR_DEFAULT};
    201 +	int i, j;
    202 +	Glyph g = (Glyph){ .fg = defaultfg, .bg = defaultbg};
    203  
    204  	memset(term.tabs, 0, term.col * sizeof(*term.tabs));
    205  	for (i = tabspaces; i < term.col; i += tabspaces)
    206 @@ -1028,17 +1042,37 @@ treset(void)
    207  	term.charset = 0;
    208  
    209  	for (i = 0; i < 2; i++) {
    210 -		tmoveto(0, 0);
    211 -		tcursor(CURSOR_SAVE);
    212 -		tclearregion(0, 0, term.col-1, term.row-1);
    213 -		tswapscreen();
    214 +		term.screen[i].sc = (TCursor){{
    215 +			.fg = defaultfg,
    216 +			.bg = defaultbg
    217 +		}};
    218 +		term.screen[i].cur = 0;
    219 +		term.screen[i].off = 0;
    220 +		for (j = 0; j < term.row; ++j) {
    221 +			if (term.col != term.linelen)
    222 +				term.screen[i].buffer[j] = xrealloc(term.screen[i].buffer[j], term.col * sizeof(Glyph));
    223 +			clearline(term.screen[i].buffer[j], g, 0, term.col);
    224 +		}
    225 +		for (j = term.row; j < term.screen[i].size; ++j) {
    226 +			free(term.screen[i].buffer[j]);
    227 +			term.screen[i].buffer[j] = NULL;
    228 +		}
    229  	}
    230 +	tcursor(CURSOR_LOAD);
    231 +	term.linelen = term.col;
    232 +	tfulldirt();
    233  }
    234  
    235  void
    236  tnew(int col, int row)
    237  {
    238 -	term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } };
    239 +	int i;
    240 +	term = (Term){};
    241 +	term.screen[0].buffer = xmalloc(HISTSIZE * sizeof(Line));
    242 +	term.screen[0].size = HISTSIZE;
    243 +	term.screen[1].buffer = NULL;
    244 +	for (i = 0; i < HISTSIZE; ++i) term.screen[0].buffer[i] = NULL;
    245 +
    246  	tresize(col, row);
    247  	treset();
    248  }
    249 @@ -1046,14 +1080,42 @@ tnew(int col, int row)
    250  void
    251  tswapscreen(void)
    252  {
    253 -	Line *tmp = term.line;
    254 -
    255 -	term.line = term.alt;
    256 -	term.alt = tmp;
    257  	term.mode ^= MODE_ALTSCREEN;
    258  	tfulldirt();
    259  }
    260  
    261 +void
    262 +kscrollup(const Arg *a)
    263 +{
    264 +	int n = a->i;
    265 +
    266 +	if (IS_SET(MODE_ALTSCREEN))
    267 +		return;
    268 +
    269 +	if (n < 0) n = (-n) * term.row;
    270 +	if (n > TSCREEN.size - term.row - TSCREEN.off) n = TSCREEN.size - term.row - TSCREEN.off;
    271 +	while (!TLINE(-n)) --n;
    272 +	TSCREEN.off += n;
    273 +	selscroll(0, n);
    274 +	tfulldirt();
    275 +}
    276 +
    277 +void
    278 +kscrolldown(const Arg *a)
    279 +{
    280 +
    281 +	int n = a->i;
    282 +
    283 +	if (IS_SET(MODE_ALTSCREEN))
    284 +		return;
    285 +
    286 +	if (n < 0) n = (-n) * term.row;
    287 +	if (n > TSCREEN.off) n = TSCREEN.off;
    288 +	TSCREEN.off -= n;
    289 +	selscroll(0, -n);
    290 +	tfulldirt();
    291 +}
    292 +
    293  void
    294  tscrolldown(int orig, int n)
    295  {
    296 @@ -1062,15 +1124,29 @@ tscrolldown(int orig, int n)
    297  
    298  	LIMIT(n, 0, term.bot-orig+1);
    299  
    300 -	tsetdirt(orig, term.bot-n);
    301 -	tclearregion(0, term.bot-n+1, term.col-1, term.bot);
    302 +	/* Ensure that lines are allocated */
    303 +	for (i = -n; i < 0; i++) {
    304 +		TLINE(i) = ensureline(TLINE(i));
    305 +	}
    306  
    307 -	for (i = term.bot; i >= orig+n; i--) {
    308 -		temp = term.line[i];
    309 -		term.line[i] = term.line[i-n];
    310 -		term.line[i-n] = temp;
    311 +	/* Shift non-scrolling areas in ring buffer */
    312 +	for (i = term.bot+1; i < term.row; i++) {
    313 +		temp = TLINE(i);
    314 +		TLINE(i) = TLINE(i-n);
    315 +		TLINE(i-n) = temp;
    316 +	}
    317 +	for (i = 0; i < orig; i++) {
    318 +		temp = TLINE(i);
    319 +		TLINE(i) = TLINE(i-n);
    320 +		TLINE(i-n) = temp;
    321  	}
    322  
    323 +	/* Scroll buffer */
    324 +	TSCREEN.cur = (TSCREEN.cur + TSCREEN.size - n) % TSCREEN.size;
    325 +	/* Clear lines that have entered the view */
    326 +	tclearregion(0, orig, term.linelen-1, orig+n-1);
    327 +	/* Redraw portion of the screen that has scrolled */
    328 +	tsetdirt(orig+n-1, term.bot);
    329  	selscroll(orig, n);
    330  }
    331  
    332 @@ -1082,15 +1158,29 @@ tscrollup(int orig, int n)
    333  
    334  	LIMIT(n, 0, term.bot-orig+1);
    335  
    336 -	tclearregion(0, orig, term.col-1, orig+n-1);
    337 -	tsetdirt(orig+n, term.bot);
    338 +	/* Ensure that lines are allocated */
    339 +	for (i = term.row; i < term.row + n; i++) {
    340 +		TLINE(i) = ensureline(TLINE(i));
    341 +	}
    342  
    343 -	for (i = orig; i <= term.bot-n; i++) {
    344 -		temp = term.line[i];
    345 -		term.line[i] = term.line[i+n];
    346 -		term.line[i+n] = temp;
    347 +	/* Shift non-scrolling areas in ring buffer */
    348 +	for (i = orig-1; i >= 0; i--) {
    349 +		temp = TLINE(i);
    350 +		TLINE(i) = TLINE(i+n);
    351 +		TLINE(i+n) = temp;
    352 +	}
    353 +	for (i = term.row-1; i >term.bot; i--) {
    354 +		temp = TLINE(i);
    355 +		TLINE(i) = TLINE(i+n);
    356 +		TLINE(i+n) = temp;
    357  	}
    358  
    359 +	/* Scroll buffer */
    360 +	TSCREEN.cur = (TSCREEN.cur + n) % TSCREEN.size;
    361 +	/* Clear lines that have entered the view */
    362 +	tclearregion(0, term.bot-n+1, term.linelen-1, term.bot);
    363 +	/* Redraw portion of the screen that has scrolled */
    364 +	tsetdirt(orig, term.bot-n+1);
    365  	selscroll(orig, -n);
    366  }
    367  
    368 @@ -1197,6 +1287,7 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
    369  		"⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */
    370  		"│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */
    371  	};
    372 +	Line line = TLINE(y);
    373  
    374  	/*
    375  	 * The table is proudly stolen from rxvt.
    376 @@ -1205,25 +1296,25 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
    377  	   BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41])
    378  		utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ);
    379  
    380 -	if (term.line[y][x].mode & ATTR_WIDE) {
    381 +	if (line[x].mode & ATTR_WIDE) {
    382  		if (x+1 < term.col) {
    383 -			term.line[y][x+1].u = ' ';
    384 -			term.line[y][x+1].mode &= ~ATTR_WDUMMY;
    385 +			line[x+1].u = ' ';
    386 +			line[x+1].mode &= ~ATTR_WDUMMY;
    387  		}
    388 -	} else if (term.line[y][x].mode & ATTR_WDUMMY) {
    389 -		term.line[y][x-1].u = ' ';
    390 -		term.line[y][x-1].mode &= ~ATTR_WIDE;
    391 +	} else if (line[x].mode & ATTR_WDUMMY) {
    392 +		line[x-1].u = ' ';
    393 +		line[x-1].mode &= ~ATTR_WIDE;
    394  	}
    395  
    396  	term.dirty[y] = 1;
    397 -	term.line[y][x] = *attr;
    398 -	term.line[y][x].u = u;
    399 +	line[x] = *attr;
    400 +	line[x].u = u;
    401  }
    402  
    403  void
    404  tclearregion(int x1, int y1, int x2, int y2)
    405  {
    406 -	int x, y, temp;
    407 +	int x, y, L, S, temp;
    408  	Glyph *gp;
    409  
    410  	if (x1 > x2)
    411 @@ -1231,15 +1322,16 @@ tclearregion(int x1, int y1, int x2, int y2)
    412  	if (y1 > y2)
    413  		temp = y1, y1 = y2, y2 = temp;
    414  
    415 -	LIMIT(x1, 0, term.col-1);
    416 -	LIMIT(x2, 0, term.col-1);
    417 +	LIMIT(x1, 0, term.linelen-1);
    418 +	LIMIT(x2, 0, term.linelen-1);
    419  	LIMIT(y1, 0, term.row-1);
    420  	LIMIT(y2, 0, term.row-1);
    421  
    422 +	L = TLINEOFFSET(y1);
    423  	for (y = y1; y <= y2; y++) {
    424  		term.dirty[y] = 1;
    425  		for (x = x1; x <= x2; x++) {
    426 -			gp = &term.line[y][x];
    427 +			gp = &TSCREEN.buffer[L][x];
    428  			if (selected(x, y))
    429  				selclear();
    430  			gp->fg = term.c.attr.fg;
    431 @@ -1247,6 +1339,7 @@ tclearregion(int x1, int y1, int x2, int y2)
    432  			gp->mode = 0;
    433  			gp->u = ' ';
    434  		}
    435 +		L = (L + 1) % TSCREEN.size;
    436  	}
    437  }
    438  
    439 @@ -1261,7 +1354,7 @@ tdeletechar(int n)
    440  	dst = term.c.x;
    441  	src = term.c.x + n;
    442  	size = term.col - src;
    443 -	line = term.line[term.c.y];
    444 +	line = TLINE(term.c.y);
    445  
    446  	memmove(&line[dst], &line[src], size * sizeof(Glyph));
    447  	tclearregion(term.col-n, term.c.y, term.col-1, term.c.y);
    448 @@ -1278,7 +1371,7 @@ tinsertblank(int n)
    449  	dst = term.c.x + n;
    450  	src = term.c.x;
    451  	size = term.col - dst;
    452 -	line = term.line[term.c.y];
    453 +	line = TLINE(term.c.y);
    454  
    455  	memmove(&line[dst], &line[src], size * sizeof(Glyph));
    456  	tclearregion(src, term.c.y, dst - 1, term.c.y);
    457 @@ -2082,7 +2175,7 @@ tdumpline(int n)
    458  	char buf[UTF_SIZ];
    459  	const Glyph *bp, *end;
    460  
    461 -	bp = &term.line[n][0];
    462 +	bp = &TLINE(n)[0];
    463  	end = &bp[MIN(tlinelen(n), term.col) - 1];
    464  	if (bp != end || bp->u != ' ') {
    465  		for ( ; bp <= end; ++bp)
    466 @@ -2469,11 +2562,11 @@ check_control_code:
    467  	if (selected(term.c.x, term.c.y))
    468  		selclear();
    469  
    470 -	gp = &term.line[term.c.y][term.c.x];
    471 +	gp = &TLINE(term.c.y)[term.c.x];
    472  	if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) {
    473  		gp->mode |= ATTR_WRAP;
    474  		tnewline(1);
    475 -		gp = &term.line[term.c.y][term.c.x];
    476 +		gp = &TLINE(term.c.y)[term.c.x];
    477  	}
    478  
    479  	if (IS_SET(MODE_INSERT) && term.c.x+width < term.col) {
    480 @@ -2486,7 +2579,7 @@ check_control_code:
    481  			tnewline(1);
    482  		else
    483  			tmoveto(term.col - width, term.c.y);
    484 -		gp = &term.line[term.c.y][term.c.x];
    485 +		gp = &TLINE(term.c.y)[term.c.x];
    486  	}
    487  
    488  	tsetchar(u, &term.c.attr, term.c.x, term.c.y);
    489 @@ -2517,6 +2610,11 @@ twrite(const char *buf, int buflen, int show_ctrl)
    490  	Rune u;
    491  	int n;
    492  
    493 +	if (TSCREEN.off) {
    494 +		TSCREEN.off = 0;
    495 +		tfulldirt();
    496 +	}
    497 +
    498  	for (n = 0; n < buflen; n += charsize) {
    499  		if (IS_SET(MODE_UTF8)) {
    500  			/* process a complete utf8 char */
    501 @@ -2543,56 +2641,85 @@ twrite(const char *buf, int buflen, int show_ctrl)
    502  }
    503  
    504  void
    505 -tresize(int col, int row)
    506 +clearline(Line line, Glyph g, int x, int xend)
    507  {
    508  	int i;
    509 +	g.mode = 0;
    510 +	g.u = ' ';
    511 +	for (i = x; i < xend; ++i) {
    512 +		line[i] = g;
    513 +	}
    514 +}
    515 +
    516 +Line
    517 +ensureline(Line line)
    518 +{
    519 +	if (!line) {
    520 +		line = xmalloc(term.linelen * sizeof(Glyph));
    521 +	}
    522 +	return line;
    523 +}
    524 +
    525 +void
    526 +tresize(int col, int row)
    527 +{
    528 +	int i, j;
    529  	int minrow = MIN(row, term.row);
    530  	int mincol = MIN(col, term.col);
    531 +	int linelen = MAX(col, term.linelen);
    532  	int *bp;
    533 -	TCursor c;
    534  
    535 -	if (col < 1 || row < 1) {
    536 +	if (col < 1 || row < 1 || row > HISTSIZE) {
    537  		fprintf(stderr,
    538  		        "tresize: error resizing to %dx%d\n", col, row);
    539  		return;
    540  	}
    541  
    542 -	/*
    543 -	 * slide screen to keep cursor where we expect it -
    544 -	 * tscrollup would work here, but we can optimize to
    545 -	 * memmove because we're freeing the earlier lines
    546 -	 */
    547 -	for (i = 0; i <= term.c.y - row; i++) {
    548 -		free(term.line[i]);
    549 -		free(term.alt[i]);
    550 +	/* Shift buffer to keep the cursor where we expect it */
    551 +	if (row <= term.c.y) {
    552 +		term.screen[0].cur = (term.screen[0].cur - row + term.c.y + 1) % term.screen[0].size;
    553 +	}
    554 +
    555 +	/* Resize and clear line buffers as needed */
    556 +	if (linelen > term.linelen) {
    557 +		for (i = 0; i < term.screen[0].size; ++i) {
    558 +			if (term.screen[0].buffer[i]) {
    559 +				term.screen[0].buffer[i] = xrealloc(term.screen[0].buffer[i], linelen * sizeof(Glyph));
    560 +				clearline(term.screen[0].buffer[i], term.c.attr, term.linelen, linelen);
    561 +			}
    562 +		}
    563 +		for (i = 0; i < minrow; ++i) {
    564 +			term.screen[1].buffer[i] = xrealloc(term.screen[1].buffer[i], linelen * sizeof(Glyph));
    565 +			clearline(term.screen[1].buffer[i], term.c.attr, term.linelen, linelen);
    566 +		}
    567  	}
    568 -	/* ensure that both src and dst are not NULL */
    569 -	if (i > 0) {
    570 -		memmove(term.line, term.line + i, row * sizeof(Line));
    571 -		memmove(term.alt, term.alt + i, row * sizeof(Line));
    572 +	/* Allocate all visible lines for regular line buffer */
    573 +	for (j = term.screen[0].cur, i = 0; i < row; ++i, j = (j + 1) % term.screen[0].size)
    574 +	{
    575 +		if (!term.screen[0].buffer[j]) {
    576 +			term.screen[0].buffer[j] = xmalloc(linelen * sizeof(Glyph));
    577 +		}
    578 +		if (i >= term.row) {
    579 +			clearline(term.screen[0].buffer[j], term.c.attr, 0, linelen);
    580 +		}
    581  	}
    582 -	for (i += row; i < term.row; i++) {
    583 -		free(term.line[i]);
    584 -		free(term.alt[i]);
    585 +	/* Resize alt screen */
    586 +	term.screen[1].cur = 0;
    587 +	term.screen[1].size = row;
    588 +	for (i = row; i < term.row; ++i) {
    589 +		free(term.screen[1].buffer[i]);
    590 +	}
    591 +	term.screen[1].buffer = xrealloc(term.screen[1].buffer, row * sizeof(Line));
    592 +	for (i = term.row; i < row; ++i) {
    593 +		term.screen[1].buffer[i] = xmalloc(linelen * sizeof(Glyph));
    594 +		clearline(term.screen[1].buffer[i], term.c.attr, 0, linelen);
    595  	}
    596  
    597  	/* resize to new height */
    598 -	term.line = xrealloc(term.line, row * sizeof(Line));
    599 -	term.alt  = xrealloc(term.alt,  row * sizeof(Line));
    600  	term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
    601  	term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
    602  
    603 -	/* resize each row to new width, zero-pad if needed */
    604 -	for (i = 0; i < minrow; i++) {
    605 -		term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
    606 -		term.alt[i]  = xrealloc(term.alt[i],  col * sizeof(Glyph));
    607 -	}
    608 -
    609 -	/* allocate any new rows */
    610 -	for (/* i = minrow */; i < row; i++) {
    611 -		term.line[i] = xmalloc(col * sizeof(Glyph));
    612 -		term.alt[i] = xmalloc(col * sizeof(Glyph));
    613 -	}
    614 +	/* fix tabstops */
    615  	if (col > term.col) {
    616  		bp = term.tabs + term.col;
    617  
    618 @@ -2602,26 +2729,16 @@ tresize(int col, int row)
    619  		for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces)
    620  			*bp = 1;
    621  	}
    622 +
    623  	/* update terminal size */
    624  	term.col = col;
    625  	term.row = row;
    626 +	term.linelen = linelen;
    627  	/* reset scrolling region */
    628  	tsetscroll(0, row-1);
    629  	/* make use of the LIMIT in tmoveto */
    630  	tmoveto(term.c.x, term.c.y);
    631 -	/* Clearing both screens (it makes dirty all lines) */
    632 -	c = term.c;
    633 -	for (i = 0; i < 2; i++) {
    634 -		if (mincol < col && 0 < minrow) {
    635 -			tclearregion(mincol, 0, col - 1, minrow - 1);
    636 -		}
    637 -		if (0 < col && minrow < row) {
    638 -			tclearregion(0, minrow, col - 1, row - 1);
    639 -		}
    640 -		tswapscreen();
    641 -		tcursor(CURSOR_LOAD);
    642 -	}
    643 -	term.c = c;
    644 +	tfulldirt();
    645  }
    646  
    647  void
    648 @@ -2633,14 +2750,15 @@ resettitle(void)
    649  void
    650  drawregion(int x1, int y1, int x2, int y2)
    651  {
    652 -	int y;
    653 +	int y, L;
    654  
    655 +	L = TLINEOFFSET(y1);
    656  	for (y = y1; y < y2; y++) {
    657 -		if (!term.dirty[y])
    658 -			continue;
    659 -
    660 -		term.dirty[y] = 0;
    661 -		xdrawline(term.line[y], x1, y, x2);
    662 +		if (term.dirty[y]) {
    663 +			term.dirty[y] = 0;
    664 +			xdrawline(TSCREEN.buffer[L], x1, y, x2);
    665 +		}
    666 +		L = (L + 1) % TSCREEN.size;
    667  	}
    668  }
    669  
    670 @@ -2655,14 +2773,15 @@ draw(void)
    671  	/* adjust cursor position */
    672  	LIMIT(term.ocx, 0, term.col-1);
    673  	LIMIT(term.ocy, 0, term.row-1);
    674 -	if (term.line[term.ocy][term.ocx].mode & ATTR_WDUMMY)
    675 +	if (TLINE(term.ocy)[term.ocx].mode & ATTR_WDUMMY)
    676  		term.ocx--;
    677 -	if (term.line[term.c.y][cx].mode & ATTR_WDUMMY)
    678 +	if (TLINE(term.c.y)[cx].mode & ATTR_WDUMMY)
    679  		cx--;
    680  
    681  	drawregion(0, 0, term.col, term.row);
    682 -	xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
    683 -			term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
    684 +	if (TSCREEN.off == 0)
    685 +		xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx],
    686 +				term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]);
    687  	term.ocx = cx;
    688  	term.ocy = term.c.y;
    689  	xfinishdraw();
    690 diff --git a/st.h b/st.h
    691 index fd3b0d8..3cea73b 100644
    692 --- a/st.h
    693 +++ b/st.h
    694 @@ -19,6 +19,7 @@
    695  
    696  #define TRUECOLOR(r,g,b)	(1 << 24 | (r) << 16 | (g) << 8 | (b))
    697  #define IS_TRUECOL(x)		(1 << 24 & (x))
    698 +#define HISTSIZE            2000
    699  
    700  enum glyph_attribute {
    701  	ATTR_NULL       = 0,
    702 diff --git a/x.c b/x.c
    703 index bd23686..25785a6 100644
    704 --- a/x.c
    705 +++ b/x.c
    706 @@ -59,6 +59,8 @@ static void zoom(const Arg *);
    707  static void zoomabs(const Arg *);
    708  static void zoomreset(const Arg *);
    709  static void ttysend(const Arg *);
    710 +void kscrollup(const Arg *);
    711 +void kscrolldown(const Arg *);
    712  
    713  /* config.h for applying patches and the configuration. */
    714  #include "config.h"