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