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