commit d09a9b001447ef1eb5ab898188032fdf59742ba8
Author: Cary W Reams <[email protected]>
Date:   Sat May 4 04:49:03 2024 -0500

    [st][patch][scrollback] scrollback and reflow patches for st-0.9.2

diff --git a/st.suckless.org/patches/scrollback/index.md 
b/st.suckless.org/patches/scrollback/index.md
index 0c15f55d..661db1eb 100644
--- a/st.suckless.org/patches/scrollback/index.md
+++ b/st.suckless.org/patches/scrollback/index.md
@@ -11,6 +11,7 @@ Download
 * [st-scrollback-0.8.4.diff](st-scrollback-0.8.4.diff)
 * [st-scrollback-20201205-4ef0cbd.diff](st-scrollback-20201205-4ef0cbd.diff)
 * [st-scrollback-20210507-4536f46.diff](st-scrollback-20210507-4536f46.diff)
+* [st-scrollback-0.9.2.diff](st-scrollback-0.9.2.diff)
 
 Alternative implementation that uses a ring buffer for more
 efficient scrolling:
@@ -23,6 +24,7 @@ Apply the following patch on top of the previous to allow 
column and row reflow.
 * [st-scrollback-reflow-0.8.5.diff](st-scrollback-reflow-0.8.5.diff)
 * [st-scrollback-reflow-0.9.diff](st-scrollback-reflow-0.9.diff)
 * 
[st-scrollback-reflow-20230607-211964d.diff](st-scrollback-reflow-20230607-211964d.diff)
+* [st-scrollback-reflow-0.9.2.diff](st-scrollback-reflow-0.9.2.diff)
 
 Apply the following patch on top of the previous to allow scrolling
 using `Shift+MouseWheel`.
diff --git a/st.suckless.org/patches/scrollback/st-scrollback-0.9.2.diff 
b/st.suckless.org/patches/scrollback/st-scrollback-0.9.2.diff
new file mode 100644
index 00000000..f9782e8d
--- /dev/null
+++ b/st.suckless.org/patches/scrollback/st-scrollback-0.9.2.diff
@@ -0,0 +1,351 @@
+diff --git a/config.def.h b/config.def.h
+index 2cd740a..40b7d93 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -201,6 +201,8 @@ static Shortcut shortcuts[] = {
+       { TERMMOD,              XK_Y,           selpaste,       {.i =  0} },
+       { ShiftMask,            XK_Insert,      selpaste,       {.i =  0} },
+       { TERMMOD,              XK_Num_Lock,    numlock,        {.i =  0} },
++      { ShiftMask,            XK_Page_Up,     kscrollup,      {.i = -1} },
++    { ShiftMask,            XK_Page_Down,   kscrolldown,    {.i = -1} },
+ };
+ 
+ /*
+diff --git a/st.c b/st.c
+index b9f66e7..2478942 100644
+--- a/st.c
++++ b/st.c
+@@ -35,6 +35,7 @@
+ #define ESC_ARG_SIZ   16
+ #define STR_BUF_SIZ   ESC_BUF_SIZ
+ #define STR_ARG_SIZ   ESC_ARG_SIZ
++#define HISTSIZE      2000
+ 
+ /* macros */
+ #define IS_SET(flag)          ((term.mode & (flag)) != 0)
+@@ -42,6 +43,9 @@
+ #define ISCONTROLC1(c)                (BETWEEN(c, 0x80, 0x9f))
+ #define ISCONTROL(c)          (ISCONTROLC0(c) || ISCONTROLC1(c))
+ #define ISDELIM(u)            (u && wcschr(worddelimiters, u))
++#define TLINE(y)              ((y) < term.scr ? term.hist[((y) + term.histi - 
\
++            term.scr + HISTSIZE + 1) % HISTSIZE] : \
++            term.line[(y) - term.scr])
+ 
+ enum term_mode {
+       MODE_WRAP        = 1 << 0,
+@@ -115,6 +119,9 @@ typedef struct {
+       int col;      /* nb col */
+       Line *line;   /* screen */
+       Line *alt;    /* alternate screen */
++      Line hist[HISTSIZE]; /* history buffer */
++      int histi;    /* history index */
++      int scr;      /* scroll back */
+       int *dirty;   /* dirtyness of lines */
+       TCursor c;    /* cursor */
+       int ocx;      /* old cursor col */
+@@ -185,8 +192,8 @@ static void tnewline(int);
+ static void tputtab(int);
+ static void tputc(Rune);
+ static void treset(void);
+-static void tscrollup(int, int);
+-static void tscrolldown(int, int);
++static void tscrollup(int, int, int);
++static void tscrolldown(int, int, int);
+ static void tsetattr(const int *, int);
+ static void tsetchar(Rune, const Glyph *, int, int);
+ static void tsetdirt(int, int);
+@@ -409,10 +416,10 @@ tlinelen(int y)
+ {
+       int i = term.col;
+ 
+-      if (term.line[y][i - 1].mode & ATTR_WRAP)
++      if (TLINE(y)[i - 1].mode & ATTR_WRAP)
+               return i;
+ 
+-      while (i > 0 && term.line[y][i - 1].u == ' ')
++      while (i > 0 && TLINE(y)[i - 1].u == ' ')
+               --i;
+ 
+       return i;
+@@ -521,7 +528,7 @@ selsnap(int *x, int *y, int direction)
+                * Snap around if the word wraps around at the end or
+                * beginning of a line.
+                */
+-              prevgp = &term.line[*y][*x];
++              prevgp = &TLINE(*y)[*x];
+               prevdelim = ISDELIM(prevgp->u);
+               for (;;) {
+                       newx = *x + direction;
+@@ -536,14 +543,14 @@ selsnap(int *x, int *y, int direction)
+                                       yt = *y, xt = *x;
+                               else
+                                       yt = newy, xt = newx;
+-                              if (!(term.line[yt][xt].mode & ATTR_WRAP))
++                              if (!(TLINE(yt)[xt].mode & ATTR_WRAP))
+                                       break;
+                       }
+ 
+                       if (newx >= tlinelen(newy))
+                               break;
+ 
+-                      gp = &term.line[newy][newx];
++                      gp = &TLINE(newy)[newx];
+                       delim = ISDELIM(gp->u);
+                       if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
+                                       || (delim && gp->u != prevgp->u)))
+@@ -564,14 +571,14 @@ selsnap(int *x, int *y, int direction)
+               *x = (direction < 0) ? 0 : term.col - 1;
+               if (direction < 0) {
+                       for (; *y > 0; *y += direction) {
+-                              if (!(term.line[*y-1][term.col-1].mode
++                              if (!(TLINE(*y-1)[term.col-1].mode
+                                               & ATTR_WRAP)) {
+                                       break;
+                               }
+                       }
+               } else if (direction > 0) {
+                       for (; *y < term.row-1; *y += direction) {
+-                              if (!(term.line[*y][term.col-1].mode
++                              if (!(TLINE(*y)[term.col-1].mode
+                                               & ATTR_WRAP)) {
+                                       break;
+                               }
+@@ -602,13 +609,13 @@ getsel(void)
+               }
+ 
+               if (sel.type == SEL_RECTANGULAR) {
+-                      gp = &term.line[y][sel.nb.x];
++                      gp = &TLINE(y)[sel.nb.x];
+                       lastx = sel.ne.x;
+               } else {
+-                      gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0];
++                      gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0];
+                       lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
+               }
+-              last = &term.line[y][MIN(lastx, linelen-1)];
++              last = &TLINE(y)[MIN(lastx, linelen-1)];
+               while (last >= gp && last->u == ' ')
+                       --last;
+ 
+@@ -844,6 +851,9 @@ void
+ ttywrite(const char *s, size_t n, int may_echo)
+ {
+       const char *next;
++      Arg arg = (Arg) { .i = term.scr };
++
++      kscrolldown(&arg);
+ 
+       if (may_echo && IS_SET(MODE_ECHO))
+               twrite(s, n, 1);
+@@ -1055,13 +1065,53 @@ tswapscreen(void)
+ }
+ 
+ void
+-tscrolldown(int orig, int n)
++kscrolldown(const Arg* a)
++{
++      int n = a->i;
++
++      if (n < 0)
++              n = term.row + n;
++
++      if (n > term.scr)
++              n = term.scr;
++
++      if (term.scr > 0) {
++              term.scr -= n;
++              selscroll(0, -n);
++              tfulldirt();
++      }
++}
++
++void
++kscrollup(const Arg* a)
++{
++      int n = a->i;
++
++      if (n < 0)
++              n = term.row + n;
++
++      if (term.scr <= HISTSIZE-n) {
++              term.scr += n;
++              selscroll(0, n);
++              tfulldirt();
++      }
++}
++
++void
++tscrolldown(int orig, int n, int copyhist)
+ {
+       int i;
+       Line temp;
+ 
+       LIMIT(n, 0, term.bot-orig+1);
+ 
++      if (copyhist) {
++              term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE;
++              temp = term.hist[term.histi];
++              term.hist[term.histi] = term.line[term.bot];
++              term.line[term.bot] = temp;
++      }
++
+       tsetdirt(orig, term.bot-n);
+       tclearregion(0, term.bot-n+1, term.col-1, term.bot);
+ 
+@@ -1071,17 +1121,28 @@ tscrolldown(int orig, int n)
+               term.line[i-n] = temp;
+       }
+ 
+-      selscroll(orig, n);
++      if (term.scr == 0)
++              selscroll(orig, n);
+ }
+ 
+ void
+-tscrollup(int orig, int n)
++tscrollup(int orig, int n, int copyhist)
+ {
+       int i;
+       Line temp;
+ 
+       LIMIT(n, 0, term.bot-orig+1);
+ 
++      if (copyhist) {
++              term.histi = (term.histi + 1) % HISTSIZE;
++              temp = term.hist[term.histi];
++              term.hist[term.histi] = term.line[orig];
++              term.line[orig] = temp;
++      }
++
++      if (term.scr > 0 && term.scr < HISTSIZE)
++              term.scr = MIN(term.scr + n, HISTSIZE-1);
++
+       tclearregion(0, orig, term.col-1, orig+n-1);
+       tsetdirt(orig+n, term.bot);
+ 
+@@ -1091,7 +1152,8 @@ tscrollup(int orig, int n)
+               term.line[i+n] = temp;
+       }
+ 
+-      selscroll(orig, -n);
++      if (term.scr == 0)
++              selscroll(orig, -n);
+ }
+ 
+ void
+@@ -1120,7 +1182,7 @@ tnewline(int first_col)
+       int y = term.c.y;
+ 
+       if (y == term.bot) {
+-              tscrollup(term.top, 1);
++              tscrollup(term.top, 1, 1);
+       } else {
+               y++;
+       }
+@@ -1285,14 +1347,14 @@ void
+ tinsertblankline(int n)
+ {
+       if (BETWEEN(term.c.y, term.top, term.bot))
+-              tscrolldown(term.c.y, n);
++              tscrolldown(term.c.y, n, 0);
+ }
+ 
+ void
+ tdeleteline(int n)
+ {
+       if (BETWEEN(term.c.y, term.top, term.bot))
+-              tscrollup(term.c.y, n);
++              tscrollup(term.c.y, n, 0);
+ }
+ 
+ int32_t
+@@ -1730,11 +1792,11 @@ csihandle(void)
+       case 'S': /* SU -- Scroll <n> line up */
+               if (csiescseq.priv) break;
+               DEFAULT(csiescseq.arg[0], 1);
+-              tscrollup(term.top, csiescseq.arg[0]);
++              tscrollup(term.top, csiescseq.arg[0], 0);
+               break;
+       case 'T': /* SD -- Scroll <n> line down */
+               DEFAULT(csiescseq.arg[0], 1);
+-              tscrolldown(term.top, csiescseq.arg[0]);
++              tscrolldown(term.top, csiescseq.arg[0], 0);
+               break;
+       case 'L': /* IL -- Insert <n> blank lines */
+               DEFAULT(csiescseq.arg[0], 1);
+@@ -2306,7 +2368,7 @@ eschandle(uchar ascii)
+               return 0;
+       case 'D': /* IND -- Linefeed */
+               if (term.c.y == term.bot) {
+-                      tscrollup(term.top, 1);
++                      tscrollup(term.top, 1, 1);
+               } else {
+                       tmoveto(term.c.x, term.c.y+1);
+               }
+@@ -2319,7 +2381,7 @@ eschandle(uchar ascii)
+               break;
+       case 'M': /* RI -- Reverse index */
+               if (term.c.y == term.top) {
+-                      tscrolldown(term.top, 1);
++                      tscrolldown(term.top, 1, 1);
+               } else {
+                       tmoveto(term.c.x, term.c.y-1);
+               }
+@@ -2542,7 +2604,7 @@ twrite(const char *buf, int buflen, int show_ctrl)
+ void
+ tresize(int col, int row)
+ {
+-      int i;
++      int i, j;
+       int minrow = MIN(row, term.row);
+       int mincol = MIN(col, term.col);
+       int *bp;
+@@ -2579,6 +2641,14 @@ tresize(int col, int row)
+       term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
+       term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
+ 
++      for (i = 0; i < HISTSIZE; i++) {
++              term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph));
++              for (j = mincol; j < col; j++) {
++                      term.hist[i][j] = term.c.attr;
++                      term.hist[i][j].u = ' ';
++              }
++      }
++
+       /* resize each row to new width, zero-pad if needed */
+       for (i = 0; i < minrow; i++) {
+               term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
+@@ -2637,7 +2707,7 @@ drawregion(int x1, int y1, int x2, int y2)
+                       continue;
+ 
+               term.dirty[y] = 0;
+-              xdrawline(term.line[y], x1, y, x2);
++              xdrawline(TLINE(y), x1, y, x2);
+       }
+ }
+ 
+@@ -2658,8 +2728,9 @@ draw(void)
+               cx--;
+ 
+       drawregion(0, 0, term.col, term.row);
+-      xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
+-                      term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
++      if (term.scr == 0)
++              xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
++                              term.ocx, term.ocy, 
term.line[term.ocy][term.ocx]);
+       term.ocx = cx;
+       term.ocy = term.c.y;
+       xfinishdraw();
+diff --git a/st.h b/st.h
+index fd3b0d8..818a6f8 100644
+--- a/st.h
++++ b/st.h
+@@ -81,6 +81,8 @@ void die(const char *, ...);
+ void redraw(void);
+ void draw(void);
+ 
++void kscrolldown(const Arg *);
++void kscrollup(const Arg *);
+ void printscreen(const Arg *);
+ void printsel(const Arg *);
+ void sendbreak(const Arg *);
diff --git a/st.suckless.org/patches/scrollback/st-scrollback-reflow-0.9.2.diff 
b/st.suckless.org/patches/scrollback/st-scrollback-reflow-0.9.2.diff
new file mode 100644
index 00000000..bfd3d0de
--- /dev/null
+++ b/st.suckless.org/patches/scrollback/st-scrollback-reflow-0.9.2.diff
@@ -0,0 +1,1626 @@
+diff --git a/st.c b/st.c
+index 2478942..2b86d23 100644
+--- a/st.c
++++ b/st.c
+@@ -36,6 +36,7 @@
+ #define STR_BUF_SIZ   ESC_BUF_SIZ
+ #define STR_ARG_SIZ   ESC_ARG_SIZ
+ #define HISTSIZE      2000
++#define RESIZEBUFFER  1000
+ 
+ /* macros */
+ #define IS_SET(flag)          ((term.mode & (flag)) != 0)
+@@ -43,9 +44,21 @@
+ #define ISCONTROLC1(c)                (BETWEEN(c, 0x80, 0x9f))
+ #define ISCONTROL(c)          (ISCONTROLC0(c) || ISCONTROLC1(c))
+ #define ISDELIM(u)            (u && wcschr(worddelimiters, u))
+-#define TLINE(y)              ((y) < term.scr ? term.hist[((y) + term.histi - 
\
+-            term.scr + HISTSIZE + 1) % HISTSIZE] : \
+-            term.line[(y) - term.scr])
++#define TLINE(y) ( \
++      (y) < term.scr ? term.hist[(term.histi + (y) - term.scr + 1 + HISTSIZE) 
% HISTSIZE] \
++                     : term.line[(y) - term.scr] \
++)
++
++#define TLINEABS(y) ( \
++      (y) < 0 ? term.hist[(term.histi + (y) + 1 + HISTSIZE) % HISTSIZE] : 
term.line[(y)] \
++)
++
++#define UPDATEWRAPNEXT(alt, col) do { \
++      if ((term.c.state & CURSOR_WRAPNEXT) && term.c.x + term.wrapcwidth[alt] 
< col) { \
++              term.c.x += term.wrapcwidth[alt]; \
++              term.c.state &= ~CURSOR_WRAPNEXT; \
++      } \
++} while (0);
+ 
+ enum term_mode {
+       MODE_WRAP        = 1 << 0,
+@@ -57,6 +70,12 @@ enum term_mode {
+       MODE_UTF8        = 1 << 6,
+ };
+ 
++enum scroll_mode {
++      SCROLL_RESIZE = -1,
++      SCROLL_NOSAVEHIST = 0,
++      SCROLL_SAVEHIST = 1
++};
++
+ enum cursor_movement {
+       CURSOR_SAVE,
+       CURSOR_LOAD
+@@ -118,10 +137,11 @@ typedef struct {
+       int row;      /* nb row */
+       int col;      /* nb col */
+       Line *line;   /* screen */
+-      Line *alt;    /* alternate screen */
+       Line hist[HISTSIZE]; /* history buffer */
+-      int histi;    /* history index */
+-      int scr;      /* scroll back */
++      int histi;           /* history index */
++      int histf;           /* nb history available */
++      int scr;             /* scroll back */
++      int wrapcwidth[2];   /* used in updating WRAPNEXT when resizing */
+       int *dirty;   /* dirtyness of lines */
+       TCursor c;    /* cursor */
+       int ocx;      /* old cursor col */
+@@ -179,26 +199,37 @@ static void tprinter(char *, size_t);
+ static void tdumpsel(void);
+ static void tdumpline(int);
+ static void tdump(void);
+-static void tclearregion(int, int, int, int);
++static void tclearregion(int, int, int, int, int);
+ static void tcursor(int);
++static void tclearglyph(Glyph *, int);
++static void tresetcursor(void);
+ static void tdeletechar(int);
+ static void tdeleteline(int);
+ static void tinsertblank(int);
+ static void tinsertblankline(int);
+-static int tlinelen(int);
++static int tlinelen(Line len);
++static int tiswrapped(Line line);
++static char *tgetglyphs(char *, const Glyph *, const Glyph *);
++static size_t tgetline(char *, const Glyph *);
+ static void tmoveto(int, int);
+ static void tmoveato(int, int);
+ static void tnewline(int);
+ static void tputtab(int);
+ static void tputc(Rune);
+ static void treset(void);
+-static void tscrollup(int, int, int);
+-static void tscrolldown(int, int, int);
++static void tscrollup(int, int, int, int);
++static void tscrolldown(int, int);
++static void treflow(int, int);
++static void rscrolldown(int);
++static void tresizedef(int, int);
++static void tresizealt(int, int);
+ static void tsetattr(const int *, int);
+ static void tsetchar(Rune, const Glyph *, int, int);
+ static void tsetdirt(int, int);
+ static void tsetscroll(int, int);
+ static void tswapscreen(void);
++static void tloaddefscreen(int, int);
++static void tloadaltscreen(int, int);
+ static void tsetmode(int, int, const int *, int);
+ static int twrite(const char *, int, int);
+ static void tfulldirt(void);
+@@ -212,7 +243,10 @@ static void tstrsequence(uchar);
+ static void drawregion(int, int, int, int);
+ 
+ static void selnormalize(void);
+-static void selscroll(int, int);
++static void selscroll(int, int, int);
++static void selmove(int);
++static void selremove(void);
++static int regionselected(int, int, int, int);
+ static void selsnap(int *, int *, int);
+ 
+ static size_t utf8decode(const char *, Rune *, size_t);
+@@ -412,17 +446,46 @@ selinit(void)
+ }
+ 
+ int
+-tlinelen(int y)
++tlinelen(Line line)
+ {
+-      int i = term.col;
++      int i = term.col - 1;
+ 
+-      if (TLINE(y)[i - 1].mode & ATTR_WRAP)
+-              return i;
++      for (; i >= 0 && !(line[i].mode & (ATTR_SET | ATTR_WRAP)); i--);
++      return i + 1;
++}
+ 
+-      while (i > 0 && TLINE(y)[i - 1].u == ' ')
+-              --i;
++int
++tiswrapped(Line line)
++{
++      int len = tlinelen(line);
+ 
+-      return i;
++      return len > 0 && (line[len - 1].mode & ATTR_WRAP);
++}
++
++char *
++tgetglyphs(char *buf, const Glyph *gp, const Glyph *lgp)
++{
++      while (gp <= lgp)
++              if (gp->mode & ATTR_WDUMMY) {
++                      gp++;
++              } else {
++                      buf += utf8encode((gp++)->u, buf);
++              }
++      return buf;
++}
++
++size_t
++tgetline(char *buf, const Glyph *fgp)
++{
++      char *ptr;
++      const Glyph *lgp = &fgp[term.col - 1];
++
++      while (lgp > fgp && !(lgp->mode & (ATTR_SET | ATTR_WRAP)))
++              lgp--;
++      ptr = tgetglyphs(buf, fgp, lgp);
++      if (!(lgp->mode & ATTR_WRAP))
++              *(ptr++) = '
';
++      return ptr - buf;
+ }
+ 
+ void
+@@ -462,10 +525,11 @@ selextend(int col, int row, int type, int done)
+ 
+       sel.oe.x = col;
+       sel.oe.y = row;
+-      selnormalize();
+       sel.type = type;
++      selnormalize();
+ 
+-      if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || 
sel.mode == SEL_EMPTY)
++      if (oldey != sel.oe.y || oldex != sel.oe.x ||
++          oldtype != sel.type || sel.mode == SEL_EMPTY)
+               tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey));
+ 
+       sel.mode = done ? SEL_IDLE : SEL_READY;
+@@ -474,54 +538,62 @@ selextend(int col, int row, int type, int done)
+ void
+ selnormalize(void)
+ {
+-      int i;
++    int i;
+ 
+-      if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) {
+-              sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x;
+-              sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x;
+-      } else {
+-              sel.nb.x = MIN(sel.ob.x, sel.oe.x);
+-              sel.ne.x = MAX(sel.ob.x, sel.oe.x);
+-      }
+-      sel.nb.y = MIN(sel.ob.y, sel.oe.y);
+-      sel.ne.y = MAX(sel.ob.y, sel.oe.y);
++    if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) {
++        sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x;
++        sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x;
++    } else {
++        sel.nb.x = MIN(sel.ob.x, sel.oe.x);
++        sel.ne.x = MAX(sel.ob.x, sel.oe.x);
++    }
++    sel.nb.y = MIN(sel.ob.y, sel.oe.y);
++    sel.ne.y = MAX(sel.ob.y, sel.oe.y);
+ 
+-      selsnap(&sel.nb.x, &sel.nb.y, -1);
+-      selsnap(&sel.ne.x, &sel.ne.y, +1);
++    selsnap(&sel.nb.x, &sel.nb.y, -1);
++    selsnap(&sel.ne.x, &sel.ne.y, +1);
+ 
+-      /* expand selection over line breaks */
+-      if (sel.type == SEL_RECTANGULAR)
+-              return;
+-      i = tlinelen(sel.nb.y);
+-      if (i < sel.nb.x)
+-              sel.nb.x = i;
+-      if (tlinelen(sel.ne.y) <= sel.ne.x)
+-              sel.ne.x = term.col - 1;
++    /* expand selection over line breaks */
++    if (sel.type == SEL_RECTANGULAR)
++        return;
++
++    i = tlinelen(TLINE(sel.nb.y));
++    if (sel.nb.x > i)
++        sel.nb.x = i;
++    if (sel.ne.x >= tlinelen(TLINE(sel.ne.y)))
++        sel.ne.x = term.col - 1;
+ }
+ 
+-int
+-selected(int x, int y)
++    int
++regionselected(int x1, int y1, int x2, int y2)
+ {
+-      if (sel.mode == SEL_EMPTY || sel.ob.x == -1 ||
+-                      sel.alt != IS_SET(MODE_ALTSCREEN))
+-              return 0;
++    if (sel.ob.x == -1 || sel.mode == SEL_EMPTY ||
++            sel.alt != IS_SET(MODE_ALTSCREEN) || sel.nb.y > y2 || sel.ne.y < 
y1)
++        return 0;
+ 
+-      if (sel.type == SEL_RECTANGULAR)
+-              return BETWEEN(y, sel.nb.y, sel.ne.y)
+-                  && BETWEEN(x, sel.nb.x, sel.ne.x);
++    return (sel.type == SEL_RECTANGULAR) ? sel.nb.x <= x2 && sel.ne.x >= x1
++        : (sel.nb.y != y2 || sel.nb.x <= x2) &&
++        (sel.ne.y != y1 || sel.ne.x >= x1);
++}
+ 
+-      return BETWEEN(y, sel.nb.y, sel.ne.y)
+-          && (y != sel.nb.y || x >= sel.nb.x)
+-          && (y != sel.ne.y || x <= sel.ne.x);
++    int
++selected(int x, int y)
++{
++    return regionselected(x, y, x, y);
+ }
+ 
++
+ void
+ selsnap(int *x, int *y, int direction)
+ {
+       int newx, newy, xt, yt;
++      int rtop = 0, rbot = term.row - 1;
+       int delim, prevdelim;
+       const Glyph *gp, *prevgp;
+ 
++      if (!IS_SET(MODE_ALTSCREEN))
++              rtop += -term.histf + term.scr, rbot += term.scr;
++
+       switch (sel.snap) {
+       case SNAP_WORD:
+               /*
+@@ -536,7 +608,7 @@ selsnap(int *x, int *y, int direction)
+                       if (!BETWEEN(newx, 0, term.col - 1)) {
+                               newy += direction;
+                               newx = (newx + term.col) % term.col;
+-                              if (!BETWEEN(newy, 0, term.row - 1))
++                              if (!BETWEEN(newy, rtop, rbot))
+                                       break;
+ 
+                               if (direction > 0)
+@@ -547,13 +619,13 @@ selsnap(int *x, int *y, int direction)
+                                       break;
+                       }
+ 
+-                      if (newx >= tlinelen(newy))
++            if (newx >= tlinelen(TLINE(newy)))
+                               break;
+ 
+                       gp = &TLINE(newy)[newx];
+                       delim = ISDELIM(gp->u);
+-                      if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
+-                                      || (delim && gp->u != prevgp->u)))
++                      if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim ||
++                          (delim && !(gp->u == ' ' && prevgp->u == ' '))))
+                               break;
+ 
+                       *x = newx;
+@@ -568,20 +640,16 @@ selsnap(int *x, int *y, int direction)
+                * has set ATTR_WRAP at its end. Then the whole next or
+                * previous line will be selected.
+                */
+-              *x = (direction < 0) ? 0 : term.col - 1;
+-              if (direction < 0) {
+-                      for (; *y > 0; *y += direction) {
+-                              if (!(TLINE(*y-1)[term.col-1].mode
+-                                              & ATTR_WRAP)) {
+-                                      break;
+-                              }
++        *x = (direction < 0) ? 0 : term.col - 1;
++        if (direction < 0) {
++            for (; *y > rtop; *y -= 1) {
++                if (!tiswrapped(TLINE(*y-1)))
++                        break;
+                       }
+               } else if (direction > 0) {
+-                      for (; *y < term.row-1; *y += direction) {
+-                              if (!(TLINE(*y)[term.col-1].mode
+-                                              & ATTR_WRAP)) {
++                      for (; *y < rbot; *y += 1) {
++                              if (!tiswrapped(TLINE(*y)))
+                                       break;
+-                              }
+                       }
+               }
+               break;
+@@ -592,39 +660,34 @@ char *
+ getsel(void)
+ {
+       char *str, *ptr;
+-      int y, bufsize, lastx, linelen;
+-      const Glyph *gp, *last;
++      int y, lastx, linelen;
++      const Glyph *gp, *lgp;
+ 
+-      if (sel.ob.x == -1)
++      if (sel.ob.x == -1 || sel.alt != IS_SET(MODE_ALTSCREEN))
+               return NULL;
+ 
+-      bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ;
+-      ptr = str = xmalloc(bufsize);
++      str = xmalloc((term.col + 1) * (sel.ne.y - sel.nb.y + 1) * UTF_SIZ);
++      ptr = str;
+ 
+       /* append every set & selected glyph to the selection */
+       for (y = sel.nb.y; y <= sel.ne.y; y++) {
+-              if ((linelen = tlinelen(y)) == 0) {
++              Line line = TLINE(y);
++
++              if ((linelen = tlinelen(line)) == 0) {
+                       *ptr++ = '
';
+                       continue;
+               }
+ 
+               if (sel.type == SEL_RECTANGULAR) {
+-                      gp = &TLINE(y)[sel.nb.x];
++                      gp = &line[sel.nb.x];
+                       lastx = sel.ne.x;
+               } else {
+-                      gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0];
++                      gp = &line[sel.nb.y == y ? sel.nb.x : 0];
+                       lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
+               }
+-              last = &TLINE(y)[MIN(lastx, linelen-1)];
+-              while (last >= gp && last->u == ' ')
+-                      --last;
++              lgp = &line[MIN(lastx, linelen-1)];
+ 
+-              for ( ; gp <= last; ++gp) {
+-                      if (gp->mode & ATTR_WDUMMY)
+-                              continue;
+-
+-                      ptr += utf8encode(gp->u, ptr);
+-              }
++              ptr = tgetglyphs(ptr, gp, lgp);
+ 
+               /*
+                * Copy and pasting of line endings is inconsistent
+@@ -636,10 +699,10 @@ getsel(void)
+                * FIXME: Fix the computer world.
+                */
+               if ((y < sel.ne.y || lastx >= linelen) &&
+-                  (!(last->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR))
++                  (!(lgp->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR))
+                       *ptr++ = '
';
+       }
+-      *ptr = 0;
++      *ptr = '+       return str;
+ }
+ 
+@@ -648,9 +711,15 @@ selclear(void)
+ {
+       if (sel.ob.x == -1)
+               return;
++      selremove();
++      tsetdirt(sel.nb.y, sel.ne.y);
++}
++
++void
++selremove(void)
++{
+       sel.mode = SEL_IDLE;
+       sel.ob.x = -1;
+-      tsetdirt(sel.nb.y, sel.ne.y);
+ }
+ 
+ void
+@@ -851,9 +920,8 @@ void
+ ttywrite(const char *s, size_t n, int may_echo)
+ {
+       const char *next;
+-      Arg arg = (Arg) { .i = term.scr };
+ 
+-      kscrolldown(&arg);
++    kscrolldown(&((Arg){ .i = term.scr }));
+ 
+       if (may_echo && IS_SET(MODE_ECHO))
+               twrite(s, n, 1);
+@@ -990,7 +1058,7 @@ tsetdirtattr(int attr)
+       for (i = 0; i < term.row-1; i++) {
+               for (j = 0; j < term.col-1; j++) {
+                       if (term.line[i][j].mode & attr) {
+-                              tsetdirt(i, i);
++                              term.dirty[i] = 1;
+                               break;
+                       }
+               }
+@@ -1000,7 +1068,8 @@ tsetdirtattr(int attr)
+ void
+ tfulldirt(void)
+ {
+-      tsetdirt(0, term.row-1);
++    for (int i = 0; i < term.row; i++)
++        term.dirty[i] = 1;
+ }
+ 
+ void
+@@ -1017,162 +1086,261 @@ tcursor(int mode)
+       }
+ }
+ 
++void
++tresetcursor(void)
++{
++      term.c = (TCursor){ { .mode = ATTR_NULL, .fg = defaultfg, .bg = 
defaultbg },
++                          .x = 0, .y = 0, .state = CURSOR_DEFAULT };
++}
++
+ void
+ treset(void)
+ {
+       uint i;
++    int x, y;
+ 
+-      term.c = (TCursor){{
+-              .mode = ATTR_NULL,
+-              .fg = defaultfg,
+-              .bg = defaultbg
+-      }, .x = 0, .y = 0, .state = CURSOR_DEFAULT};
++      tresetcursor();
+ 
+       memset(term.tabs, 0, term.col * sizeof(*term.tabs));
+       for (i = tabspaces; i < term.col; i += tabspaces)
+               term.tabs[i] = 1;
+       term.top = 0;
++      term.histf = 0;
++      term.scr = 0;
+       term.bot = term.row - 1;
+       term.mode = MODE_WRAP|MODE_UTF8;
+       memset(term.trantbl, CS_USA, sizeof(term.trantbl));
+       term.charset = 0;
+ 
++    selremove();
+       for (i = 0; i < 2; i++) {
+-              tmoveto(0, 0);
+-              tcursor(CURSOR_SAVE);
+-              tclearregion(0, 0, term.col-1, term.row-1);
+-              tswapscreen();
++        tcursor(CURSOR_SAVE); /* reset saved cursor */
++        for (y = 0; y < term.row; y++)
++            for (x = 0; x < term.col; x++)
++                tclearglyph(&term.line[y][x], 0);
++        tswapscreen();
+       }
++    tfulldirt();
+ }
+ 
+ void
+ tnew(int col, int row)
+ {
+-      term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } };
+-      tresize(col, row);
+-      treset();
++    int i, j;
++
++    for (i = 0; i < 2; i++) {
++        term.line = xmalloc(row * sizeof(Line));
++        for (j = 0; j < row; j++)
++            term.line[j] = xmalloc(col * sizeof(Glyph));
++        term.col = col, term.row = row;
++        tswapscreen();
++    }
++    term.dirty = xmalloc(row * sizeof(*term.dirty));
++    term.tabs = xmalloc(col * sizeof(*term.tabs));
++    for (i = 0; i < HISTSIZE; i++)
++        term.hist[i] = xmalloc(col * sizeof(Glyph));
++    treset();
+ }
+ 
++/* handle it with care */
+ void
+ tswapscreen(void)
+ {
+-      Line *tmp = term.line;
++      static Line *altline;
++      static int altcol, altrow;
++      Line *tmpline = term.line;
++      int tmpcol = term.col, tmprow = term.row;
+ 
+-      term.line = term.alt;
+-      term.alt = tmp;
++      term.line = altline;
++      term.col = altcol, term.row = altrow;
++      altline = tmpline;
++      altcol = tmpcol, altrow = tmprow;
+       term.mode ^= MODE_ALTSCREEN;
+-      tfulldirt();
+ }
+ 
+ void
+-kscrolldown(const Arg* a)
++tloaddefscreen(int clear, int loadcursor)
+ {
+-      int n = a->i;
++      int col, row, alt = IS_SET(MODE_ALTSCREEN);
+ 
+-      if (n < 0)
+-              n = term.row + n;
++      if (alt) {
++              if (clear)
++                      tclearregion(0, 0, term.col-1, term.row-1, 1);
++              col = term.col, row = term.row;
++              tswapscreen();
++      }
++      if (loadcursor)
++              tcursor(CURSOR_LOAD);
++      if (alt)
++              tresizedef(col, row);
++}
+ 
+-      if (n > term.scr)
+-              n = term.scr;
++void
++tloadaltscreen(int clear, int savecursor)
++{
++      int col, row, def = !IS_SET(MODE_ALTSCREEN);
+ 
+-      if (term.scr > 0) {
+-              term.scr -= n;
+-              selscroll(0, -n);
+-              tfulldirt();
++      if (savecursor)
++              tcursor(CURSOR_SAVE);
++      if (def) {
++              col = term.col, row = term.row;
++              tswapscreen();
++              term.scr = 0;
++              tresizealt(col, row);
+       }
++      if (clear)
++              tclearregion(0, 0, term.col-1, term.row-1, 1);
+ }
+ 
++int
++tisaltscreen(void)
++{
++      return IS_SET(MODE_ALTSCREEN);
++}
++
++
+ void
+-kscrollup(const Arg* a)
++kscrolldown(const Arg* a)
+ {
+-      int n = a->i;
++    int n = a->i;
+ 
+-      if (n < 0)
+-              n = term.row + n;
++    if (!term.scr || IS_SET(MODE_ALTSCREEN))
++        return;
+ 
+-      if (term.scr <= HISTSIZE-n) {
+-              term.scr += n;
+-              selscroll(0, n);
+-              tfulldirt();
+-      }
++    if (n < 0)
++        n = MAX(term.row / -n, 1);
++
++    if (n <= term.scr) {
++        term.scr -= n;
++    } else {
++        n = term.scr;
++        term.scr = 0;
++    }
++      if (sel.ob.x != -1 && !sel.alt)
++              selmove(-n); /* negate change in term.scr */
++      tfulldirt();
+ }
+ 
++
++
+ void
+-tscrolldown(int orig, int n, int copyhist)
++kscrollup(const Arg* a)
+ {
+-      int i;
+-      Line temp;
++    int n = a->i;
+ 
+-      LIMIT(n, 0, term.bot-orig+1);
++    if (!term.histf || IS_SET(MODE_ALTSCREEN))
++        return;
+ 
+-      if (copyhist) {
+-              term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE;
+-              temp = term.hist[term.histi];
+-              term.hist[term.histi] = term.line[term.bot];
+-              term.line[term.bot] = temp;
+-      }
++    if (n < 0)
++        n = MAX(term.row / -n, 1);
+ 
+-      tsetdirt(orig, term.bot-n);
+-      tclearregion(0, term.bot-n+1, term.col-1, term.bot);
++    if (term.scr + n <= term.histf) {
++        term.scr += n;
++    } else {
++        n = term.histf - term.scr;
++        term.scr = term.histf;
++    }
+ 
+-      for (i = term.bot; i >= orig+n; i--) {
+-              temp = term.line[i];
+-              term.line[i] = term.line[i-n];
+-              term.line[i-n] = temp;
+-      }
++    if (sel.ob.x != -1 && !sel.alt)
++        selmove(n); /* negate change in term.scr */
++    tfulldirt();
+ 
+-      if (term.scr == 0)
+-              selscroll(orig, n);
+ }
+ 
+ void
+-tscrollup(int orig, int n, int copyhist)
++tscrolldown(int top, int n)
+ {
+-      int i;
+-      Line temp;
++    int i, bot = term.bot;
++    Line temp;
+ 
+-      LIMIT(n, 0, term.bot-orig+1);
++    if (n <= 0)
++        return;
++    n = MIN(n, bot-top+1);
+ 
+-      if (copyhist) {
+-              term.histi = (term.histi + 1) % HISTSIZE;
+-              temp = term.hist[term.histi];
+-              term.hist[term.histi] = term.line[orig];
+-              term.line[orig] = temp;
+-      }
++    tsetdirt(top, bot-n);
++    tclearregion(0, bot-n+1, term.col-1, bot, 1);
+ 
+-      if (term.scr > 0 && term.scr < HISTSIZE)
+-              term.scr = MIN(term.scr + n, HISTSIZE-1);
++    for (i = bot; i >= top+n; i--) {
++        temp = term.line[i];
++        term.line[i] = term.line[i-n];
++        term.line[i-n] = temp;
++    }
+ 
+-      tclearregion(0, orig, term.col-1, orig+n-1);
+-      tsetdirt(orig+n, term.bot);
++    if (sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN))
++        selscroll(top, bot, n);
++}
+ 
+-      for (i = orig; i <= term.bot-n; i++) {
+-              temp = term.line[i];
+-              term.line[i] = term.line[i+n];
+-              term.line[i+n] = temp;
+-      }
++void
++tscrollup(int top, int bot, int n, int mode)
++{
++    int i, j, s;
++    int alt = IS_SET(MODE_ALTSCREEN);
++    int savehist = !alt && top == 0 && mode != SCROLL_NOSAVEHIST;
++    Line temp;
++
++    if (n <= 0)
++        return;
++    n = MIN(n, bot-top+1);
+ 
+-      if (term.scr == 0)
+-              selscroll(orig, -n);
++    if (savehist) {
++        for (i = 0; i < n; i++) {
++            term.histi = (term.histi + 1) % HISTSIZE;
++            temp = term.hist[term.histi];
++            for (j = 0; j < term.col; j++)
++                tclearglyph(&temp[j], 1);
++            term.hist[term.histi] = term.line[i];
++            term.line[i] = temp;
++        }
++        term.histf = MIN(term.histf + n, HISTSIZE);
++        s = n;
++        if (term.scr) {
++            j = term.scr;
++            term.scr = MIN(j + n, HISTSIZE);
++            s = j + n - term.scr;
++        }
++        if (mode != SCROLL_RESIZE)
++            tfulldirt();
++    } else {
++        tclearregion(0, top, term.col-1, top+n-1, 1);
++        tsetdirt(top+n, bot);
++    }
++
++    for (i = top; i <= bot-n; i++) {
++        temp = term.line[i];
++        term.line[i] = term.line[i+n];
++        term.line[i+n] = temp;
++    }
++
++    if (sel.ob.x != -1 && sel.alt == alt) {
++        if (!savehist) {
++            selscroll(top, bot, -n);
++        } else if (s > 0) {
++            selmove(-s);
++            if (-term.scr + sel.nb.y < -term.histf)
++                selremove();
++        }
++    }
+ }
+ 
+ void
+-selscroll(int orig, int n)
++selmove(int n)
++ {
++      sel.ob.y += n, sel.nb.y += n;
++      sel.oe.y += n, sel.ne.y += n;
++}
++
++void
++selscroll(int top, int bot, int n)
+ {
+-      if (sel.ob.x == -1 || sel.alt != IS_SET(MODE_ALTSCREEN))
+-              return;
++    /* turn absolute coordinates into relative */
++    top += term.scr, bot += term.scr;
+ 
+-      if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, 
term.bot)) {
+-              selclear();
+-      } else if (BETWEEN(sel.nb.y, orig, term.bot)) {
+-              sel.ob.y += n;
+-              sel.oe.y += n;
+-              if (sel.ob.y < term.top || sel.ob.y > term.bot ||
+-                  sel.oe.y < term.top || sel.oe.y > term.bot) {
+-                      selclear();
+-              } else {
+-                      selnormalize();
+-              }
++    if (BETWEEN(sel.nb.y, top, bot) != BETWEEN(sel.ne.y, top, bot)) {
++        selclear();
++    } else if (BETWEEN(sel.nb.y, top, bot)) {
++        selmove(n);
++        if (sel.nb.y < top || sel.ne.y > bot)
++            selclear();
+       }
+ }
+ 
+@@ -1182,7 +1350,7 @@ tnewline(int first_col)
+       int y = term.c.y;
+ 
+       if (y == term.bot) {
+-              tscrollup(term.top, 1, 1);
++              tscrollup(term.top, term.bot, 1, SCROLL_SAVEHIST);
+       } else {
+               y++;
+       }
+@@ -1246,115 +1414,126 @@ tmoveto(int x, int y)
+ void
+ tsetchar(Rune u, const Glyph *attr, int x, int y)
+ {
+-      static const char *vt100_0[62] = { /* 0x41 - 0x7e */
+-              "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */
+-              0, 0, 0, 0, 0, 0, 0, 0, /* H - O */
+-              0, 0, 0, 0, 0, 0, 0, 0, /* P - W */
+-              0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */
+-              "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */
+-              "␤", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */
+-              "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */
+-              "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */
+-      };
++    static const char *vt100_0[62] = { /* 0x41 - 0x7e */
++        "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */
++        0, 0, 0, 0, 0, 0, 0, 0, /* H - O */
++        0, 0, 0, 0, 0, 0, 0, 0, /* P - W */
++        0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */
++        "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */
++        "␤", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */
++        "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */
++        "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */
++    };
+ 
+-      /*
+-       * The table is proudly stolen from rxvt.
+-       */
+-      if (term.trantbl[term.charset] == CS_GRAPHIC0 &&
+-         BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41])
+-              utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ);
+-
+-      if (term.line[y][x].mode & ATTR_WIDE) {
+-              if (x+1 < term.col) {
+-                      term.line[y][x+1].u = ' ';
+-                      term.line[y][x+1].mode &= ~ATTR_WDUMMY;
+-              }
+-      } else if (term.line[y][x].mode & ATTR_WDUMMY) {
+-              term.line[y][x-1].u = ' ';
+-              term.line[y][x-1].mode &= ~ATTR_WIDE;
+-      }
++    /*
++     * The table is proudly stolen from rxvt.
++     */
++    if (term.trantbl[term.charset] == CS_GRAPHIC0 &&
++            BETWEEN(u, 0x41, 0x7e) && vt100_0[u - 0x41])
++        utf8decode(vt100_0[u - 0x41], &u, UTF_SIZ);
++
++    if (term.line[y][x].mode & ATTR_WIDE) {
++        if (x+1 < term.col) {
++            term.line[y][x+1].u = ' ';
++            term.line[y][x+1].mode &= ~ATTR_WDUMMY;
++        }
++    } else if (term.line[y][x].mode & ATTR_WDUMMY) {
++        term.line[y][x-1].u = ' ';
++        term.line[y][x-1].mode &= ~ATTR_WIDE;
++    }
+ 
+-      term.dirty[y] = 1;
+-      term.line[y][x] = *attr;
+-      term.line[y][x].u = u;
++    term.dirty[y] = 1;
++    term.line[y][x] = *attr;
++    term.line[y][x].u = u;
++    term.line[y][x].mode |= ATTR_SET;
+ }
+ 
++
++
+ void
+-tclearregion(int x1, int y1, int x2, int y2)
++tclearglyph(Glyph *gp, int usecurattr)
+ {
+-      int x, y, temp;
+-      Glyph *gp;
++      if (usecurattr) {
++              gp->fg = term.c.attr.fg;
++              gp->bg = term.c.attr.bg;
++      } else {
++              gp->fg = defaultfg;
++              gp->bg = defaultbg;
++      }
++      gp->mode = ATTR_NULL;
++      gp->u = ' ';
++}
+ 
+-      if (x1 > x2)
+-              temp = x1, x1 = x2, x2 = temp;
+-      if (y1 > y2)
+-              temp = y1, y1 = y2, y2 = temp;
+ 
+-      LIMIT(x1, 0, term.col-1);
+-      LIMIT(x2, 0, term.col-1);
+-      LIMIT(y1, 0, term.row-1);
+-      LIMIT(y2, 0, term.row-1);
+ 
+-      for (y = y1; y <= y2; y++) {
++void
++tclearregion(int x1, int y1, int x2, int y2, int usecurattr)
++{
++      int x, y;
++      /* regionselected() takes relative coordinates */
++      if (regionselected(x1+term.scr, y1+term.scr, x2+term.scr, y2+term.scr))
++              selremove();
++
++    for (y = y1; y <= y2; y++) {
+               term.dirty[y] = 1;
+-              for (x = x1; x <= x2; x++) {
+-                      gp = &term.line[y][x];
+-                      if (selected(x, y))
+-                              selclear();
+-                      gp->fg = term.c.attr.fg;
+-                      gp->bg = term.c.attr.bg;
+-                      gp->mode = 0;
+-                      gp->u = ' ';
+-              }
++              for (x = x1; x <= x2; x++)
++                      tclearglyph(&term.line[y][x], usecurattr);
+       }
+ }
+ 
+ void
+ tdeletechar(int n)
+ {
+-      int dst, src, size;
+-      Glyph *line;
+-
+-      LIMIT(n, 0, term.col - term.c.x);
++    int src, dst, size;
++    Line line;
+ 
+-      dst = term.c.x;
+-      src = term.c.x + n;
+-      size = term.col - src;
+-      line = term.line[term.c.y];
++    if (n <= 0)
++        return;
+ 
+-      memmove(&line[dst], &line[src], size * sizeof(Glyph));
+-      tclearregion(term.col-n, term.c.y, term.col-1, term.c.y);
++    dst = term.c.x;
++    src = MIN(term.c.x + n, term.col);
++    size = term.col - src;
++    if (size > 0) {
++        /*
++         * otherwise src would point beyond the array
++         * https://stackoverflow.com/questions/29844298
++         */
++        line = term.line[term.c.y];
++        memmove(&line[dst], &line[src], size * sizeof(Glyph));
++    }
++    tclearregion(dst + size, term.c.y, term.col - 1, term.c.y, 1);
+ }
+ 
+ void
+ tinsertblank(int n)
+ {
+-      int dst, src, size;
+-      Glyph *line;
+-
+-      LIMIT(n, 0, term.col - term.c.x);
+-
+-      dst = term.c.x + n;
+-      src = term.c.x;
+-      size = term.col - dst;
+-      line = term.line[term.c.y];
++    int src, dst, size;
++    Line line;
+ 
+-      memmove(&line[dst], &line[src], size * sizeof(Glyph));
+-      tclearregion(src, term.c.y, dst - 1, term.c.y);
++    if (n <= 0)
++        return;
++    dst = MIN(term.c.x + n, term.col);
++    src = term.c.x;
++    size = term.col - dst;
++    if (size > 0) { /* otherwise dst would point beyond the array */
++        line = term.line[term.c.y];
++        memmove(&line[dst], &line[src], size * sizeof(Glyph));
++    }
++    tclearregion(src, term.c.y, dst - 1, term.c.y, 1);
+ }
+ 
+ void
+ tinsertblankline(int n)
+ {
+       if (BETWEEN(term.c.y, term.top, term.bot))
+-              tscrolldown(term.c.y, n, 0);
++              tscrolldown(term.c.y, n);
+ }
+ 
+ void
+ tdeleteline(int n)
+ {
+       if (BETWEEN(term.c.y, term.top, term.bot))
+-              tscrollup(term.c.y, n, 0);
++              tscrollup(term.c.y, term.bot, n, SCROLL_NOSAVEHIST);
+ }
+ 
+ int32_t
+@@ -1528,7 +1707,7 @@ tsetscroll(int t, int b)
+ void
+ tsetmode(int priv, int set, const int *args, int narg)
+ {
+-      int alt; const int *lim;
++      const int *lim;
+ 
+       for (lim = args + narg; args < lim; ++args) {
+               if (priv) {
+@@ -1589,26 +1768,20 @@ tsetmode(int priv, int set, const int *args, int narg)
+                               xsetmode(set, MODE_8BIT);
+                               break;
+                       case 1049: /* swap screen & set/restore cursor as xterm 
*/
+-                              if (!allowaltscreen)
+-                                      break;
+-                              tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
+-                              /* FALLTHROUGH */
+                       case 47: /* swap screen */
+-                      case 1047:
++                      case 1047: /*swap screen, clearing alternate screen */
+                               if (!allowaltscreen)
+                                       break;
+-                              alt = IS_SET(MODE_ALTSCREEN);
+-                              if (alt) {
+-                                      tclearregion(0, 0, term.col-1,
+-                                                      term.row-1);
+-                              }
+-                              if (set ^ alt) /* set is always 1 or 0 */
+-                                      tswapscreen();
+-                              if (*args != 1049)
+-                                      break;
++                              if (set)
++                                      tloadaltscreen(*args == 1049, *args == 
1049);
++                              else
++                                      tloaddefscreen(*args == 1047, *args == 
1049);
++                              break;
+                               /* FALLTHROUGH */
+-                      case 1048:
+-                              tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
++            case 1048:
++                if (!allowaltscreen)
++                    break;
++                tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD);
+                               break;
+                       case 2004: /* 2004: bracketed paste mode */
+                               xsetmode(set, MODE_BRCKTPASTE);
+@@ -1659,7 +1832,7 @@ void
+ csihandle(void)
+ {
+       char buf[40];
+-      int len;
++      int n, x;
+ 
+       switch (csiescseq.mode[0]) {
+       default:
+@@ -1757,19 +1930,29 @@ csihandle(void)
+       case 'J': /* ED -- Clear screen */
+               switch (csiescseq.arg[0]) {
+               case 0: /* below */
+-                      tclearregion(term.c.x, term.c.y, term.col-1, term.c.y);
++                      tclearregion(term.c.x, term.c.y, term.col-1, term.c.y, 
1);
+                       if (term.c.y < term.row-1) {
+-                              tclearregion(0, term.c.y+1, term.col-1,
+-                                              term.row-1);
++                              tclearregion(0, term.c.y+1, term.col-1, 
term.row-1, 1);
+                       }
+                       break;
+               case 1: /* above */
+-                      if (term.c.y > 1)
+-                              tclearregion(0, 0, term.col-1, term.c.y-1);
+-                      tclearregion(0, term.c.y, term.c.x, term.c.y);
++                      if (term.c.y >= 1)
++                              tclearregion(0, 0, term.col-1, term.c.y-1, 1);
++                      tclearregion(0, term.c.y, term.c.x, term.c.y, 1);
+                       break;
+-              case 2: /* all */
+-                      tclearregion(0, 0, term.col-1, term.row-1);
++        case 2: /* all */
++            if (IS_SET(MODE_ALTSCREEN)) {
++                tclearregion(0, 0, term.col-1, term.row-1, 1);
++                break;
++            }
++            /* vte does this:
++               tscrollup(0, term.row-1, term.row, SCROLL_SAVEHIST); */
++
++            /* alacritty does this: */
++            for (n = term.row-1; n >= 0 && tlinelen(term.line[n]) == 0; n--);
++            if (n >= 0)
++                tscrollup(0, term.row-1, n+1, SCROLL_SAVEHIST);
++            tscrollup(0, term.row-1, term.row-n-1, SCROLL_NOSAVEHIST);
+                       break;
+               default:
+                       goto unknown;
+@@ -1778,25 +1961,25 @@ csihandle(void)
+       case 'K': /* EL -- Clear line */
+               switch (csiescseq.arg[0]) {
+               case 0: /* right */
+-                      tclearregion(term.c.x, term.c.y, term.col-1,
+-                                      term.c.y);
++                      tclearregion(term.c.x, term.c.y, term.col-1, term.c.y, 
1);
+                       break;
+               case 1: /* left */
+-                      tclearregion(0, term.c.y, term.c.x, term.c.y);
++                      tclearregion(0, term.c.y, term.c.x, term.c.y, 1);
+                       break;
+               case 2: /* all */
+-                      tclearregion(0, term.c.y, term.col-1, term.c.y);
++                      tclearregion(0, term.c.y, term.col-1, term.c.y, 1);
+                       break;
+               }
+               break;
+       case 'S': /* SU -- Scroll <n> line up */
+               if (csiescseq.priv) break;
+               DEFAULT(csiescseq.arg[0], 1);
+-              tscrollup(term.top, csiescseq.arg[0], 0);
++              /* xterm, urxvt, alacritty save this in history */
++              tscrollup(term.top, term.bot, csiescseq.arg[0], 
SCROLL_SAVEHIST);
+               break;
+       case 'T': /* SD -- Scroll <n> line down */
+               DEFAULT(csiescseq.arg[0], 1);
+-              tscrolldown(term.top, csiescseq.arg[0], 0);
++              tscrolldown(term.top, csiescseq.arg[0]);
+               break;
+       case 'L': /* IL -- Insert <n> blank lines */
+               DEFAULT(csiescseq.arg[0], 1);
+@@ -1810,9 +1993,11 @@ csihandle(void)
+               tdeleteline(csiescseq.arg[0]);
+               break;
+       case 'X': /* ECH -- Erase <n> char */
++              if (csiescseq.arg[0] < 0)
++                      return;
+               DEFAULT(csiescseq.arg[0], 1);
+-              tclearregion(term.c.x, term.c.y,
+-                              term.c.x + csiescseq.arg[0] - 1, term.c.y);
++              x = MIN(term.c.x + csiescseq.arg[0], term.col) - 1;
++              tclearregion(term.c.x, term.c.y, x, term.c.y, 1);
+               break;
+       case 'P': /* DCH -- Delete <n> char */
+               DEFAULT(csiescseq.arg[0], 1);
+@@ -1838,9 +2023,9 @@ csihandle(void)
+                       ttywrite("", sizeof("") - 1, 0);
+                       break;
+               case 6: /* Report Cursor Position (CPR) "<row>;<column>R" */
+-                      len = snprintf(buf, sizeof(buf), "[%i;%iR",
++                      n = snprintf(buf, sizeof(buf), "[%i;%iR",
+                                      term.c.y+1, term.c.x+1);
+-                      ttywrite(buf, len, 0);
++                      ttywrite(buf, n, 0);
+                       break;
+               default:
+                       goto unknown;
+@@ -2138,16 +2323,8 @@ tdumpsel(void)
+ void
+ tdumpline(int n)
+ {
+-      char buf[UTF_SIZ];
+-      const Glyph *bp, *end;
+-
+-      bp = &term.line[n][0];
+-      end = &bp[MIN(tlinelen(n), term.col) - 1];
+-      if (bp != end || bp->u != ' ') {
+-              for ( ; bp <= end; ++bp)
+-                      tprinter(buf, utf8encode(bp->u, buf));
+-      }
+-      tprinter("
", 1);
++    char str[(term.col + 1) * UTF_SIZ];
++    tprinter(str, tgetline(str, &term.line[n][0]));
+ }
+ 
+ void
+@@ -2368,7 +2545,7 @@ eschandle(uchar ascii)
+               return 0;
+       case 'D': /* IND -- Linefeed */
+               if (term.c.y == term.bot) {
+-                      tscrollup(term.top, 1, 1);
++                      tscrollup(term.top, term.bot, 1, SCROLL_SAVEHIST);
+               } else {
+                       tmoveto(term.c.x, term.c.y+1);
+               }
+@@ -2381,7 +2558,7 @@ eschandle(uchar ascii)
+               break;
+       case 'M': /* RI -- Reverse index */
+               if (term.c.y == term.top) {
+-                      tscrolldown(term.top, 1, 1);
++                      tscrolldown(term.top, 1);
+               } else {
+                       tmoveto(term.c.x, term.c.y-1);
+               }
+@@ -2525,7 +2702,9 @@ check_control_code:
+                */
+               return;
+       }
+-      if (selected(term.c.x, term.c.y))
++
++    /* selected() takes relative coordinates */
++      if (selected(term.c.x + term.scr, term.c.y + term.scr))
+               selclear();
+ 
+       gp = &term.line[term.c.y][term.c.x];
+@@ -2565,6 +2744,7 @@ check_control_code:
+       if (term.c.x+width < term.col) {
+               tmoveto(term.c.x+width, term.c.y);
+       } else {
++              term.wrapcwidth[IS_SET(MODE_ALTSCREEN)] = width;
+               term.c.state |= CURSOR_WRAPNEXT;
+       }
+ }
+@@ -2601,94 +2781,285 @@ twrite(const char *buf, int buflen, int show_ctrl)
+       return n;
+ }
+ 
++void
++rscrolldown(int n)
++{
++    int i;
++    Line temp;
++
++    /* can never be true as of now
++       if (IS_SET(MODE_ALTSCREEN))
++       return; */
++
++    if ((n = MIN(n, term.histf)) <= 0)
++        return;
++
++    for (i = term.c.y + n; i >= n; i--) {
++        temp = term.line[i];
++        term.line[i] = term.line[i-n];
++        term.line[i-n] = temp;
++    }
++    for (/*i = n - 1 */; i >= 0; i--) {
++        temp = term.line[i];
++        term.line[i] = term.hist[term.histi];
++        term.hist[term.histi] = temp;
++        term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE;
++    }
++    term.c.y += n;
++    term.histf -= n;
++    if ((i = term.scr - n) >= 0) {
++        term.scr = i;
++    } else {
++        term.scr = 0;
++        if (sel.ob.x != -1 && !sel.alt)
++            selmove(-i);
++    }
++}
++
++
++
+ void
+ tresize(int col, int row)
+ {
+-      int i, j;
+-      int minrow = MIN(row, term.row);
+-      int mincol = MIN(col, term.col);
+       int *bp;
+-      TCursor c;
+ 
++      /* col and row are always MAX(_, 1)
+       if (col < 1 || row < 1) {
+-              fprintf(stderr,
+-                      "tresize: error resizing to %dx%d
", col, row);
++              fprintf(stderr, "tresize: error resizing to %dx%d
", col, row);
+               return;
+-      }
++      } */
+ 
+-      /*
+-       * slide screen to keep cursor where we expect it -
+-       * tscrollup would work here, but we can optimize to
+-       * memmove because we're freeing the earlier lines
+-       */
+-      for (i = 0; i <= term.c.y - row; i++) {
+-              free(term.line[i]);
+-              free(term.alt[i]);
+-      }
+-      /* ensure that both src and dst are not NULL */
+-      if (i > 0) {
+-              memmove(term.line, term.line + i, row * sizeof(Line));
+-              memmove(term.alt, term.alt + i, row * sizeof(Line));
+-      }
+-      for (i += row; i < term.row; i++) {
+-              free(term.line[i]);
+-              free(term.alt[i]);
+-      }
+-
+-      /* resize to new height */
+-      term.line = xrealloc(term.line, row * sizeof(Line));
+-      term.alt  = xrealloc(term.alt,  row * sizeof(Line));
+       term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
+       term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
++      if (col > term.col) {
++              bp = term.tabs + term.col;
++              memset(bp, 0, sizeof(*term.tabs) * (col - term.col));
++              while (--bp > term.tabs && !*bp)
++                      /* nothing */ ;
++              for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces)
++                      *bp = 1;
++      }
+ 
+-      for (i = 0; i < HISTSIZE; i++) {
+-              term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph));
+-              for (j = mincol; j < col; j++) {
+-                      term.hist[i][j] = term.c.attr;
+-                      term.hist[i][j].u = ' ';
+-              }
+-      }
++      if (IS_SET(MODE_ALTSCREEN))
++              tresizealt(col, row);
++      else
++              tresizedef(col, row);
++}
+ 
+-      /* resize each row to new width, zero-pad if needed */
+-      for (i = 0; i < minrow; i++) {
+-              term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
+-              term.alt[i]  = xrealloc(term.alt[i],  col * sizeof(Glyph));
+-      }
+ 
+-      /* allocate any new rows */
+-      for (/* i = minrow */; i < row; i++) {
+-              term.line[i] = xmalloc(col * sizeof(Glyph));
+-              term.alt[i] = xmalloc(col * sizeof(Glyph));
++void
++tresizedef(int col, int row)
++{
++      int i, j;
++
++      /* return if dimensions haven't changed */
++      if (term.col == col && term.row == row) {
++              tfulldirt();
++              return;
+       }
+-      if (col > term.col) {
+-              bp = term.tabs + term.col;
+-
+-              memset(bp, 0, sizeof(*term.tabs) * (col - term.col));
+-              while (--bp > term.tabs && !*bp)
+-                      /* nothing */ ;
+-              for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces)
+-                      *bp = 1;
++      if (col != term.col) {
++              if (!sel.alt)
++                      selremove();
++              treflow(col, row);
++      } else {
++              /* slide screen up if otherwise cursor would get out of the 
screen */
++              if (term.c.y >= row) {
++                      tscrollup(0, term.row - 1, term.c.y - row + 1, 
SCROLL_RESIZE);
++                      term.c.y = row - 1;
++              }
++              for (i = row; i < term.row; i++)
++                      free(term.line[i]);
++
++              /* resize to new height */
++              term.line = xrealloc(term.line, row * sizeof(Line));
++              /* allocate any new rows */
++              for (i = term.row; i < row; i++) {
++                      term.line[i] = xmalloc(col * sizeof(Glyph));
++                      for (j = 0; j < col; j++)
++                              tclearglyph(&term.line[i][j], 0);
++              }
++              /* scroll down as much as height has increased */
++              rscrolldown(row - term.row);
+       }
+       /* update terminal size */
+-      term.col = col;
+-      term.row = row;
++      term.col = col, term.row = row;
+       /* reset scrolling region */
+-      tsetscroll(0, row-1);
+-      /* make use of the LIMIT in tmoveto */
+-      tmoveto(term.c.x, term.c.y);
+-      /* Clearing both screens (it makes dirty all lines) */
+-      c = term.c;
+-      for (i = 0; i < 2; i++) {
+-              if (mincol < col && 0 < minrow) {
+-                      tclearregion(mincol, 0, col - 1, minrow - 1);
+-              }
+-              if (0 < col && minrow < row) {
+-                      tclearregion(0, minrow, col - 1, row - 1);
+-              }
+-              tswapscreen();
+-              tcursor(CURSOR_LOAD);
+-      }
+-      term.c = c;
++      term.top = 0, term.bot = row - 1;
++      /* dirty all lines */
++      tfulldirt();
++}
++
++
++
++void
++tresizealt(int col, int row)
++{
++    int i, j;
++
++    /* return if dimensions haven't changed */
++    if (term.col == col && term.row == row) {
++        tfulldirt();
++        return;
++    }
++    if (sel.alt)
++        selremove();
++    /* slide screen up if otherwise cursor would get out of the screen */
++    for (i = 0; i <= term.c.y - row; i++)
++        free(term.line[i]);
++    if (i > 0) {
++        /* ensure that both src and dst are not NULL */
++        memmove(term.line, term.line + i, row * sizeof(Line));
++        term.c.y = row - 1;
++    }
++    for (i += row; i < term.row; i++)
++        free(term.line[i]);
++    /* resize to new height */
++    term.line = xrealloc(term.line, row * sizeof(Line));
++    /* resize to new width */
++    for (i = 0; i < MIN(row, term.row); i++) {
++        term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
++        for (j = term.col; j < col; j++)
++            tclearglyph(&term.line[i][j], 0);
++    }
++    /* allocate any new rows */
++    for (/*i = MIN(row, term.row) */; i < row; i++) {
++        term.line[i] = xmalloc(col * sizeof(Glyph));
++        for (j = 0; j < col; j++)
++            tclearglyph(&term.line[i][j], 0);
++    }
++    /* update cursor */
++    if (term.c.x >= col) {
++        term.c.state &= ~CURSOR_WRAPNEXT;
++        term.c.x = col - 1;
++    } else {
++        UPDATEWRAPNEXT(1, col);
++    }
++    /* update terminal size */
++    term.col = col, term.row = row;
++    /* reset scrolling region */
++    term.top = 0, term.bot = row - 1;
++    /* dirty all lines */
++    tfulldirt();
++ }
++
++
++
++
++
++void
++treflow(int col, int row)
++{
++    int i, j;
++    int oce, nce, bot, scr;
++    int ox = 0, oy = -term.histf, nx = 0, ny = -1, len;
++    int cy = -1; /* proxy for new y coordinate of cursor */
++    int nlines;
++    Line *buf, line;
++
++    /* y coordinate of cursor line end */
++    for (oce = term.c.y; oce < term.row - 1 &&
++            tiswrapped(term.line[oce]); oce++);
++
++    nlines = term.histf + oce + 1;
++    if (col < term.col) {
++        /* each line can take this many lines after reflow */
++        j = (term.col + col - 1) / col;
++        nlines = j * nlines;
++        if (nlines > HISTSIZE + RESIZEBUFFER + row) {
++            nlines = HISTSIZE + RESIZEBUFFER + row;
++            oy = -(nlines / j - oce - 1);
++        }
++    }
++    buf = xmalloc(nlines * sizeof(Line));
++    do {
++        if (!nx)
++            buf[++ny] = xmalloc(col * sizeof(Glyph));
++        if (!ox) {
++            line = TLINEABS(oy);
++            len = tlinelen(line);
++        }
++        if (oy == term.c.y) {
++            if (!ox)
++                len = MAX(len, term.c.x + 1);
++            /* update cursor */
++            if (cy < 0 && term.c.x - ox < col - nx) {
++                term.c.x = nx + term.c.x - ox, cy = ny;
++                UPDATEWRAPNEXT(0, col);
++            }
++        }
++        /* get reflowed lines in buf */
++        if (col - nx > len - ox) {
++            memcpy(&buf[ny][nx], &line[ox], (len-ox) * sizeof(Glyph));
++            nx += len - ox;
++            if (len == 0 || !(line[len - 1].mode & ATTR_WRAP)) {
++                for (j = nx; j < col; j++)
++                    tclearglyph(&buf[ny][j], 0);
++                nx = 0;
++            } else if (nx > 0) {
++                buf[ny][nx - 1].mode &= ~ATTR_WRAP;
++            }
++            ox = 0, oy++;
++        } else if (col - nx == len - ox) {
++            memcpy(&buf[ny][nx], &line[ox], (col-nx) * sizeof(Glyph));
++            ox = 0, oy++, nx = 0;
++        } else/* if (col - nx < len - ox) */ {
++            memcpy(&buf[ny][nx], &line[ox], (col-nx) * sizeof(Glyph));
++            ox += col - nx;
++            buf[ny][col - 1].mode |= ATTR_WRAP;
++            nx = 0;
++        }
++    } while (oy <= oce);
++    if (nx)
++        for (j = nx; j < col; j++)
++            tclearglyph(&buf[ny][j], 0);
++
++    /* free extra lines */
++    for (i = row; i < term.row; i++)
++        free(term.line[i]);
++    /* resize to new height */
++    term.line = xrealloc(term.line, row * sizeof(Line));
++
++    bot = MIN(ny, row - 1);
++    scr = MAX(row - term.row, 0);
++    /* update y coordinate of cursor line end */
++    nce = MIN(oce + scr, bot);
++    /* update cursor y coordinate */
++    term.c.y = nce - (ny - cy);
++    if (term.c.y < 0) {
++        j = nce, nce = MIN(nce + -term.c.y, bot);
++        term.c.y += nce - j;
++        while (term.c.y < 0) {
++            free(buf[ny--]);
++            term.c.y++;
++        }
++    }
++    /* allocate new rows */
++    for (i = row - 1; i > nce; i--) {
++        term.line[i] = xmalloc(col * sizeof(Glyph));
++        for (j = 0; j < col; j++)
++            tclearglyph(&term.line[i][j], 0);
++    }
++    /* fill visible area */
++    for (/*i = nce */; i >= term.row; i--, ny--)
++        term.line[i] = buf[ny];
++    for (/*i = term.row - 1 */; i >= 0; i--, ny--) {
++        free(term.line[i]);
++        term.line[i] = buf[ny];
++    }
++    /* fill lines in history buffer and update term.histf */
++    for (/*i = -1 */; ny >= 0 && i >= -HISTSIZE; i--, ny--) {
++        j = (term.histi + i + 1 + HISTSIZE) % HISTSIZE;
++        free(term.hist[j]);
++        term.hist[j] = buf[ny];
++    }
++    term.histf = -i - 1;
++    term.scr = MIN(term.scr, term.histf);
++    /* resize rest of the history lines */
++    for (/*i = -term.histf - 1 */; i >= -HISTSIZE; i--) {
++        j = (term.histi + i + 1 + HISTSIZE) % HISTSIZE;
++        term.hist[j] = xrealloc(term.hist[j], col * sizeof(Glyph));
++    }
++    free(buf);
+ }
+ 
+ void
+@@ -2728,9 +3099,8 @@ draw(void)
+               cx--;
+ 
+       drawregion(0, 0, term.col, term.row);
+-      if (term.scr == 0)
+-              xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
+-                              term.ocx, term.ocy, 
term.line[term.ocy][term.ocx]);
++      xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
++                      term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
+       term.ocx = cx;
+       term.ocy = term.c.y;
+       xfinishdraw();
+diff --git a/st.h b/st.h
+index 818a6f8..514ec08 100644
+--- a/st.h
++++ b/st.h
+@@ -22,17 +22,19 @@
+ 
+ enum glyph_attribute {
+       ATTR_NULL       = 0,
+-      ATTR_BOLD       = 1 << 0,
+-      ATTR_FAINT      = 1 << 1,
+-      ATTR_ITALIC     = 1 << 2,
+-      ATTR_UNDERLINE  = 1 << 3,
+-      ATTR_BLINK      = 1 << 4,
+-      ATTR_REVERSE    = 1 << 5,
+-      ATTR_INVISIBLE  = 1 << 6,
+-      ATTR_STRUCK     = 1 << 7,
+-      ATTR_WRAP       = 1 << 8,
+-      ATTR_WIDE       = 1 << 9,
+-      ATTR_WDUMMY     = 1 << 10,
++      ATTR_SET        = 1 << 0,
++      ATTR_BOLD       = 1 << 1,
++      ATTR_FAINT      = 1 << 2,
++      ATTR_ITALIC     = 1 << 3,
++      ATTR_UNDERLINE  = 1 << 4,
++      ATTR_BLINK      = 1 << 5,
++      ATTR_REVERSE    = 1 << 6,
++      ATTR_INVISIBLE  = 1 << 7,
++      ATTR_STRUCK     = 1 << 8,
++      ATTR_WRAP       = 1 << 9,
++      ATTR_WIDE       = 1 << 10,
++      ATTR_WDUMMY     = 1 << 11,
++      ATTR_SELECTED   = 1 << 12,
+       ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
+ };
+ 
+@@ -90,6 +92,7 @@ void toggleprinter(const Arg *);
+ 
+ int tattrset(int);
+ void tnew(int, int);
++int tisaltscreen(void);
+ void tresize(int, int);
+ void tsetdirtattr(int);
+ void ttyhangup(void);


Reply via email to