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