st-scrollback-reflow-0.9.diff - sites - public wiki contents of suckless.org
 (HTM) git clone git://git.suckless.org/sites
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
       st-scrollback-reflow-0.9.diff (37402B)
       ---
            1 diff --git a/st.c b/st.c
            2 index 79ee9ba..5170cd4 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 -
          845 -        LIMIT(n, 0, term.col - term.c.x);
          846 +        int src, dst, size;
          847 +        Line line;
          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 diff --git a/st.h b/st.h
         1425 index 818a6f8..514ec08 100644
         1426 --- a/st.h
         1427 +++ b/st.h
         1428 @@ -22,17 +22,19 @@
         1429  
         1430  enum glyph_attribute {
         1431          ATTR_NULL       = 0,
         1432 -        ATTR_BOLD       = 1 << 0,
         1433 -        ATTR_FAINT      = 1 << 1,
         1434 -        ATTR_ITALIC     = 1 << 2,
         1435 -        ATTR_UNDERLINE  = 1 << 3,
         1436 -        ATTR_BLINK      = 1 << 4,
         1437 -        ATTR_REVERSE    = 1 << 5,
         1438 -        ATTR_INVISIBLE  = 1 << 6,
         1439 -        ATTR_STRUCK     = 1 << 7,
         1440 -        ATTR_WRAP       = 1 << 8,
         1441 -        ATTR_WIDE       = 1 << 9,
         1442 -        ATTR_WDUMMY     = 1 << 10,
         1443 +        ATTR_SET        = 1 << 0,
         1444 +        ATTR_BOLD       = 1 << 1,
         1445 +        ATTR_FAINT      = 1 << 2,
         1446 +        ATTR_ITALIC     = 1 << 3,
         1447 +        ATTR_UNDERLINE  = 1 << 4,
         1448 +        ATTR_BLINK      = 1 << 5,
         1449 +        ATTR_REVERSE    = 1 << 6,
         1450 +        ATTR_INVISIBLE  = 1 << 7,
         1451 +        ATTR_STRUCK     = 1 << 8,
         1452 +        ATTR_WRAP       = 1 << 9,
         1453 +        ATTR_WIDE       = 1 << 10,
         1454 +        ATTR_WDUMMY     = 1 << 11,
         1455 +        ATTR_SELECTED   = 1 << 12,
         1456          ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
         1457  };
         1458  
         1459 @@ -90,6 +92,7 @@ void toggleprinter(const Arg *);
         1460  
         1461  int tattrset(int);
         1462  void tnew(int, int);
         1463 +int tisaltscreen(void);
         1464  void tresize(int, int);
         1465  void tsetdirtattr(int);
         1466  void ttyhangup(void);