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