| st-scrollback-ringbuffer-0.9.2.diff - sites - public wiki contents of suckless.… | |
| git clone git://git.suckless.org/sites | |
| Log | |
| Files | |
| Refs | |
| --- | |
| st-scrollback-ringbuffer-0.9.2.diff (19559B) | |
| --- | |
| 1 commit 0663bdf11a409961da5b1120741a69814da8ce65 | |
| 2 Author: Timo Röhling <[email protected]> | |
| 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 … | |
| 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 2cd740a..8b25d40 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 = … | |
| 23 { ShiftMask, XK_Insert, selpaste, {.i = … | |
| 24 { TERMMOD, XK_Num_Lock, numlock, {.i = … | |
| 25 + { ShiftMask, XK_Page_Up, kscrollup, {.i = -… | |
| 26 + { ShiftMask, XK_Page_Down, kscrolldown, {.i = -… | |
| 27 }; | |
| 28 | |
| 29 /* | |
| 30 diff --git a/st.c b/st.c | |
| 31 index b9f66e7..d9b163e 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… | |
| 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 @@ -203,6 +216,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 @@ -408,11 +423,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 @@ -521,7 +537,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 @@ -536,14 +552,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_WRA… | |
| 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 != prev… | |
| 118 || (delim && gp->u != prevgp->u… | |
| 119 @@ -564,14 +580,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 @@ -602,13 +618,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 @@ -949,12 +965,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 @@ -976,14 +995,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 @@ -996,27 +1018,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 @@ -1028,17 +1042,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(ter… | |
| 239 + clearline(term.screen[i].buffer[j], g, 0, term.… | |
| 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 = defaultb… | |
| 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 @@ -1046,14 +1080,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… | |
| 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 @@ -1062,15 +1124,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 @@ -1082,15 +1158,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 @@ -1194,6 +1284,7 @@ tsetchar(Rune u, const Glyph *attr, int x, int y) | |
| 385 "⎻", "─", "⎼", "⎽", "├", "┤", "â”´", "┬",… | |
| 386 "│", "≤", "≥", "Ï€", "≠", "£", "·", /* x - ~ … | |
| 387 }; | |
| 388 + Line line = TLINE(y); | |
| 389 | |
| 390 /* | |
| 391 * The table is proudly stolen from rxvt. | |
| 392 @@ -1202,25 +1293,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 @@ -1228,15 +1319,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 @@ -1244,6 +1336,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 @@ -1258,7 +1351,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 @@ -1275,7 +1368,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 @@ -2079,7 +2172,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 @@ -2466,11 +2559,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 @@ -2483,7 +2576,7 @@ check_control_code: | |
| 497 tnewline(1); | |
| 498 else | |
| 499 tmoveto(term.col - width, term.c.y); | |
| 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 @@ -2514,6 +2607,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 @@ -2540,56 +2638,85 @@ twrite(const char *buf, int buflen, int show_ctr… | |
| 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… | |
| 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(ter… | |
| 576 + clearline(term.screen[0].buffer[i], ter… | |
| 577 + } | |
| 578 + } | |
| 579 + for (i = 0; i < minrow; ++i) { | |
| 580 + term.screen[1].buffer[i] = xrealloc(term.screen… | |
| 581 + clearline(term.screen[1].buffer[i], term.c.attr… | |
| 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) %… | |
| 590 + { | |
| 591 + if (!term.screen[0].buffer[j]) { | |
| 592 + term.screen[0].buffer[j] = xmalloc(linelen * si… | |
| 593 + } | |
| 594 + if (i >= term.row) { | |
| 595 + clearline(term.screen[0].buffer[j], term.c.attr… | |
| 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 * s… | |
| 608 + for (i = term.row; i < row; ++i) { | |
| 609 + term.screen[1].buffer[i] = xmalloc(linelen * sizeof(Gly… | |
| 610 + clearline(term.screen[1].buffer[i], term.c.attr, 0, lin… | |
| 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(Glyp… | |
| 622 - term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyp… | |
| 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 @@ -2599,26 +2726,16 @@ tresize(int col, int row) | |
| 635 for (bp += tabspaces; bp < term.tabs + col; bp += tabsp… | |
| 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 @@ -2630,14 +2747,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 @@ -2652,14 +2770,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.oc… | |
| 700 + if (TSCREEN.off == 0) | |
| 701 + xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx], | |
| 702 + term.ocx, term.ocy, TLINE(term.ocy)[ter… | |
| 703 term.ocx = cx; | |
| 704 term.ocy = term.c.y; | |
| 705 xfinishdraw(); | |
| 706 diff --git a/st.h b/st.h | |
| 707 index fd3b0d8..3cea73b 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 bd23686..25785a6 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" |