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