changeset 30880:82336c3b679d v9.0.0774

patch 9.0.0774: the libvterm code is outdated Commit: https://github.com/vim/vim/commit/6a12d26f3404e45ce25cf9152857e355b28f392a Author: Bram Moolenaar <Bram@vim.org> Date: Sun Oct 16 19:26:52 2022 +0100 patch 9.0.0774: the libvterm code is outdated Problem: The libvterm code is outdated. Solution: Include libvterm changes from revision 802 to 817. Revert some changes made for C89.
author Bram Moolenaar <Bram@vim.org>
date Sun, 16 Oct 2022 20:30:07 +0200
parents 2db86b9d26df
children 170bf60c726d
files src/libvterm/CONTRIBUTING src/libvterm/Makefile src/libvterm/README src/libvterm/doc/seqs.txt src/libvterm/include/vterm.h src/libvterm/src/encoding.c src/libvterm/src/keyboard.c src/libvterm/src/parser.c src/libvterm/src/pen.c src/libvterm/src/screen.c src/libvterm/src/state.c src/libvterm/src/unicode.c src/libvterm/src/vterm.c src/libvterm/src/vterm_internal.h src/libvterm/t/10state_putglyph.test src/libvterm/t/13state_edit.test src/libvterm/t/26state_query.test src/libvterm/t/30state_pen.test src/libvterm/t/64screen_pen.test src/libvterm/t/69screen_reflow.test src/libvterm/t/harness.c src/libvterm/t/run-test.pl src/terminal.c src/version.c
diffstat 24 files changed, 656 insertions(+), 255 deletions(-) [+]
line wrap: on
line diff
--- a/src/libvterm/CONTRIBUTING
+++ b/src/libvterm/CONTRIBUTING
@@ -6,8 +6,8 @@ The main resources for this library are:
   Launchpad
     https://launchpad.net/libvterm
 
-  Freenode:
-    ##tty or #tickit on irc.freenode.net
+  IRC:
+    ##tty or #tickit on irc.libera.chat
 
   Email:
     Paul "LeoNerd" Evans <leonerd@leonerd.org.uk>
--- a/src/libvterm/Makefile
+++ b/src/libvterm/Makefile
@@ -37,7 +37,7 @@ INCFILES=$(TBLFILES:.tbl=.inc)
 HFILES_INT=$(sort $(wildcard src/*.h)) $(HFILES)
 
 VERSION_MAJOR=0
-VERSION_MINOR=2
+VERSION_MINOR=3
 
 VERSION_CURRENT=0
 VERSION_REVISION=0
--- a/src/libvterm/README
+++ b/src/libvterm/README
@@ -7,8 +7,11 @@ The original can be found:
 	https://github.com/neovim/libvterm
 
 Modifications:
-- Add a .gitignore file.
-- Convert some code from C99 to C90.
+- revisions up to 817 have been included
+- Added a .gitignore file.
+- use TRUE and FALSE instead of true and false
+- use int or unsigned int instead of bool
+- Converted some code from C99 to C90.
 - Other changes to support embedding in Vim.
 
 To get the latest version of libvterm you need the "bzr" command and do:
--- a/src/libvterm/doc/seqs.txt
+++ b/src/libvterm/doc/seqs.txt
@@ -122,6 +122,7 @@ 123x   CSI ? h          = DECSM, DEC set
 123x   CSI l            = RM, Reset mode
 123x   CSI ? l          = DECRM, DEC reset mode
 123x   CSI m            = SGR, Set Graphic Rendition
+       CSI ? m          = DECSGR, private Set Graphic Rendition
 123x   CSI   n          = DSR, Device Status Report
  23x       5            =   operating status
  23x       6            =   CPR = cursor position
@@ -198,6 +199,9 @@ 123x   SGR 7            = Reverse on
    x   SGR 40-47        = Background ANSI
    x   SGR 48           = Background alternative palette
    x   SGR 49           = Background default
+       SGR 73           = Superscript on
+       SGR 74           = Subscript on
+       SGR 75           = Superscript/subscript off
    x   SGR 90-97        = Foreground ANSI high-intensity
    x   SGR 100-107      = Background ANSI high-intensity
 
--- a/src/libvterm/include/vterm.h
+++ b/src/libvterm/include/vterm.h
@@ -12,16 +12,17 @@ extern "C" {
 
 #include "vterm_keycodes.h"
 
+// VIM: use TRUE and FALSE instead of true and false
 #define TRUE 1
 #define FALSE 0
 
-// from stdint.h
+// VIM: from stdint.h
 typedef unsigned char		uint8_t;
 typedef unsigned short		uint16_t;
 typedef unsigned int		uint32_t;
 
 #define VTERM_VERSION_MAJOR 0
-#define VTERM_VERSION_MINOR 2
+#define VTERM_VERSION_MINOR 3
 
 #define VTERM_CHECK_VERSION \
         vterm_check_version(VTERM_VERSION_MAJOR, VTERM_VERSION_MINOR)
@@ -131,7 +132,7 @@ typedef enum {
   VTERM_COLOR_DEFAULT_MASK = 0x06,
 
   /**
-   * If set, indicates that the color is invalid.
+   * VIM: If set, indicates that the color is invalid.
    */
   VTERM_COLOR_INVALID = 0x08
 } VTermColorType;
@@ -172,6 +173,7 @@ typedef enum {
  */
 #define VTERM_COLOR_IS_INVALID(col) (!!((col)->type & VTERM_COLOR_INVALID))
 
+// VIM: this was a union, but that doesn't always work.
 typedef struct {
   /**
    * Tag indicating which member is actually valid.
@@ -237,6 +239,8 @@ typedef enum {
   VTERM_ATTR_FONT,       // number: 10-19
   VTERM_ATTR_FOREGROUND, // color:  30-39 90-97
   VTERM_ATTR_BACKGROUND, // color:  40-49 100-107
+  VTERM_ATTR_SMALL,      // bool:   73, 74, 75
+  VTERM_ATTR_BASELINE,   // number: 73, 74, 75
 
   VTERM_N_ATTRS
 } VTermAttr;
@@ -251,7 +255,7 @@ typedef enum {
   VTERM_PROP_REVERSE,           // bool
   VTERM_PROP_CURSORSHAPE,       // number
   VTERM_PROP_MOUSE,             // number
-  VTERM_PROP_CURSORCOLOR,       // string
+  VTERM_PROP_CURSORCOLOR,       // VIM - string
 
   VTERM_N_PROPS
 } VTermProp;
@@ -312,7 +316,6 @@ typedef struct {
   void  (*free)(void *ptr, void *allocdata);
 } VTermAllocatorFunctions;
 
-/* A convenient shortcut for default cases */
 void vterm_check_version(int major, int minor);
 
 struct VTermBuilder {
@@ -333,7 +336,6 @@ VTerm *vterm_build(const struct VTermBui
 /* A convenient shortcut for default cases */
 // Allocate and initialize a new terminal with default allocators.
 VTerm *vterm_new(int rows, int cols);
-
 /* These shortcuts are generally discouraged in favour of just using vterm_build() */
 
 // Allocate and initialize a new terminal with specified allocators.
@@ -396,6 +398,7 @@ void vterm_mouse_button(VTerm *vt, int b
 #define CSI_ARG(a)          ((a) & CSI_ARG_MASK)
 
 /* Can't use -1 to indicate a missing argument; use this instead */
+// VIM: changed 31 to 30 to avoid an overflow warning
 #define CSI_ARG_MISSING ((1<<30)-1)
 
 #define CSI_ARG_IS_MISSING(a) (CSI_ARG(a) == CSI_ARG_MISSING)
@@ -436,8 +439,10 @@ typedef struct {
   int (*bell)(void *user);
   int (*resize)(int rows, int cols, VTermStateFields *fields, void *user);
   int (*setlineinfo)(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user);
+  int (*sb_clear)(void *user);
 } VTermStateCallbacks;
 
+// VIM: added
 typedef struct {
   VTermPos pos;
   int	   buttons;
@@ -478,6 +483,7 @@ void *vterm_state_get_unrecognised_fbdat
 void vterm_state_reset(VTermState *state, int hard);
 
 void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos);
+// VIM: added
 void vterm_state_get_mousestate(const VTermState *state, VTermMouseState *mousestate);
 void vterm_state_get_default_colors(const VTermState *state, VTermColor *default_fg, VTermColor *default_bg);
 void vterm_state_get_palette_color(const VTermState *state, int index, VTermColor *col);
@@ -522,6 +528,8 @@ typedef struct {
     unsigned int font      : 4; /* 0 to 9 */
     unsigned int dwl       : 1; /* On a DECDWL or DECDHL line */
     unsigned int dhl       : 2; /* On a DECDHL line (1=top 2=bottom) */
+    unsigned int small     : 1;
+    unsigned int baseline  : 2;
 } VTermScreenCellAttrs;
 
 enum {
@@ -531,6 +539,12 @@ enum {
   VTERM_UNDERLINE_CURLY,
 };
 
+enum {
+  VTERM_BASELINE_NORMAL,
+  VTERM_BASELINE_RAISE,
+  VTERM_BASELINE_LOWER,
+};
+
 typedef struct {
   uint32_t chars[VTERM_MAX_CHARS_PER_CELL];
   char     width;
@@ -551,6 +565,7 @@ typedef struct {
   // Return value is unused.
   int (*sb_pushline)(int cols, const VTermScreenCell *cells, void *user);
   int (*sb_popline)(int cols, VTermScreenCell *cells, void *user);
+  int (*sb_clear)(void* user);
 } VTermScreenCallbacks;
 
 // Return the screen of the vterm.
@@ -566,6 +581,11 @@ void *vterm_screen_get_cbdata(VTermScree
 void  vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermStateFallbacks *fallbacks, void *user);
 void *vterm_screen_get_unrecognised_fbdata(VTermScreen *screen);
 
+void vterm_screen_enable_reflow(VTermScreen *screen, int reflow);
+
+// Back-compat alias for the brief time it was in 0.3-RC1
+#define vterm_screen_set_reflow  vterm_screen_enable_reflow
+
 // Enable support for using the alternate screen if "altscreen" is non-zero.
 // Before that switching to the alternate screen won't work.
 // Calling with "altscreen" zero has no effect.
@@ -606,8 +626,10 @@ typedef enum {
   VTERM_ATTR_FOREGROUND_MASK = 1 << 7,
   VTERM_ATTR_BACKGROUND_MASK = 1 << 8,
   VTERM_ATTR_CONCEAL_MASK    = 1 << 9,
+  VTERM_ATTR_SMALL_MASK      = 1 << 10,
+  VTERM_ATTR_BASELINE_MASK   = 1 << 11,
 
-  VTERM_ALL_ATTRS_MASK = (1 << 10) - 1
+  VTERM_ALL_ATTRS_MASK = (1 << 12) - 1
 } VTermAttrMask;
 
 int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs);
--- a/src/libvterm/src/encoding.c
+++ b/src/libvterm/src/encoding.c
@@ -49,6 +49,7 @@ static void decode_utf8(VTermEncoding *e
       if(data->bytes_remaining) {
         data->bytes_remaining = 0;
         cp[(*cpi)++] = UNICODE_INVALID;
+	// VIM: avoid going over the end
 	if (*cpi >= cplen)
 	  break;
       }
@@ -226,8 +227,7 @@ encodings[] = {
 /* This ought to be INTERNAL but isn't because it's used by unit testing */
 VTermEncoding *vterm_lookup_encoding(VTermEncodingType type, char designation)
 {
-  int i;
-  for(i = 0; encodings[i].designation; i++)
+  for(int i = 0; encodings[i].designation; i++)
     if(encodings[i].type == type && encodings[i].designation == designation)
       return encodings[i].enc;
   return NULL;
--- a/src/libvterm/src/keyboard.c
+++ b/src/libvterm/src/keyboard.c
@@ -4,6 +4,7 @@
 
 #include "utf8.h"
 
+// VIM: added
 int vterm_is_modify_other_keys(VTerm *vt)
 {
   return vt->state->mode.modify_other_keys;
@@ -12,8 +13,7 @@ int vterm_is_modify_other_keys(VTerm *vt
 
 void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod)
 {
-  int needs_CSIu;
-
+  // VIM: added modifyOtherKeys support
   if (vt->state->mode.modify_other_keys && mod != 0) {
     vterm_push_output_sprintf_ctrl(vt, C1_CSI, "27;%d;%d~", mod+1, c);
     return;
@@ -33,6 +33,7 @@ void vterm_keyboard_unichar(VTerm *vt, u
     return;
   }
 
+  int needs_CSIu;
   switch(c) {
     /* Special Ctrl- letters that can't be represented elsewise */
     case 'i': case 'j': case 'm': case '[':
@@ -93,12 +94,12 @@ static keycodes_s keycodes[] = {
   { KEYCODE_CSI_CURSOR, 'D', 0 }, // LEFT
   { KEYCODE_CSI_CURSOR, 'C', 0 }, // RIGHT
 
-  { KEYCODE_CSINUM,     '~', 2 }, // INS
-  { KEYCODE_CSINUM,     '~', 3 }, // DEL
+  { KEYCODE_CSINUM, '~', 2 },  // INS
+  { KEYCODE_CSINUM, '~', 3 },  // DEL
   { KEYCODE_CSI_CURSOR, 'H', 0 }, // HOME
   { KEYCODE_CSI_CURSOR, 'F', 0 }, // END
-  { KEYCODE_CSINUM,     '~', 5 }, // PAGEUP
-  { KEYCODE_CSINUM,     '~', 6 }, // PAGEDOWN
+  { KEYCODE_CSINUM, '~', 5 },  // PAGEUP
+  { KEYCODE_CSINUM, '~', 6 },  // PAGEDOWN
 };
 
 static keycodes_s keycodes_fn[] = {
@@ -107,14 +108,14 @@ static keycodes_s keycodes_fn[] = {
   { KEYCODE_SS3,	'Q', 0 }, // F2
   { KEYCODE_SS3,	'R', 0 }, // F3
   { KEYCODE_SS3,	'S', 0 }, // F4
-  { KEYCODE_CSINUM,     '~', 15 }, // F5
-  { KEYCODE_CSINUM,     '~', 17 }, // F6
-  { KEYCODE_CSINUM,     '~', 18 }, // F7
-  { KEYCODE_CSINUM,     '~', 19 }, // F8
-  { KEYCODE_CSINUM,     '~', 20 }, // F9
-  { KEYCODE_CSINUM,     '~', 21 }, // F10
-  { KEYCODE_CSINUM,     '~', 23 }, // F11
-  { KEYCODE_CSINUM,     '~', 24 }, // F12
+  { KEYCODE_CSINUM, '~', 15 }, // F5
+  { KEYCODE_CSINUM, '~', 17 }, // F6
+  { KEYCODE_CSINUM, '~', 18 }, // F7
+  { KEYCODE_CSINUM, '~', 19 }, // F8
+  { KEYCODE_CSINUM, '~', 20 }, // F9
+  { KEYCODE_CSINUM, '~', 21 }, // F10
+  { KEYCODE_CSINUM, '~', 23 }, // F11
+  { KEYCODE_CSINUM, '~', 24 }, // F12
 };
 
 static keycodes_s keycodes_kp[] = {
@@ -140,11 +141,10 @@ static keycodes_s keycodes_kp[] = {
 
 void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod)
 {
-  keycodes_s k;
-
   if(key == VTERM_KEY_NONE)
     return;
 
+  keycodes_s k;
   if(key < VTERM_KEY_FUNCTION_0) {
     if(key >= sizeof(keycodes)/sizeof(keycodes[0]))
       return;
--- a/src/libvterm/src/parser.c
+++ b/src/libvterm/src/parser.c
@@ -142,7 +142,6 @@ size_t vterm_input_write(VTerm *vt, cons
   for( ; pos < len; pos++) {
     unsigned char c = bytes[pos];
     int c1_allowed = !vt->mode.utf8;
-    size_t string_len;
 
     if(c == 0x00 || c == 0x7f) { // NUL, DEL
       if(IS_STRING_STATE()) {
@@ -187,7 +186,7 @@ size_t vterm_input_write(VTerm *vt, cons
     }
     // else fallthrough
 
-    string_len = bytes + pos - string_start;
+    size_t string_len = bytes + pos - string_start;
 
     if(vt->parser.in_esc) {
       // Hoist an ESC letter into a C1 if we're not in a string mode
@@ -247,7 +246,7 @@ size_t vterm_input_write(VTerm *vt, cons
       vt->parser.v.csi.argi++;
       vt->parser.intermedlen = 0;
       vt->parser.state = CSI_INTERMED;
-      // fallthrough
+      // FALLTHROUGH
     case CSI_INTERMED:
       if(is_intermed(c)) {
         if(vt->parser.intermedlen < INTERMED_MAX-1)
--- a/src/libvterm/src/pen.c
+++ b/src/libvterm/src/pen.c
@@ -44,6 +44,7 @@ static int ramp24[] = {
 
 static void lookup_default_colour_ansi(long idx, VTermColor *col)
 {
+  // VIM: store both RGB color and index
   vterm_color_rgb(
       col,
       ansi_colors[idx].red, ansi_colors[idx].green, ansi_colors[idx].blue);
@@ -161,27 +162,27 @@ static void set_pen_col_ansi(VTermState 
 
 INTERNAL void vterm_state_newpen(VTermState *state)
 {
-  int col;
-
   // 90% grey so that pure white is brighter
   vterm_color_rgb(&state->default_fg, 240, 240, 240);
   vterm_color_rgb(&state->default_bg, 0, 0, 0);
   vterm_state_set_default_colors(state, &state->default_fg, &state->default_bg);
 
-  for(col = 0; col < 16; col++)
+  for(int col = 0; col < 16; col++)
     lookup_default_colour_ansi(col, &state->colors[col]);
 }
 
 INTERNAL void vterm_state_resetpen(VTermState *state)
 {
   state->pen.bold = 0;      setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
-  state->pen.underline = 0; setpenattr_int( state, VTERM_ATTR_UNDERLINE, 0);
+  state->pen.underline = 0; setpenattr_int (state, VTERM_ATTR_UNDERLINE, 0);
   state->pen.italic = 0;    setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
   state->pen.blink = 0;     setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
   state->pen.reverse = 0;   setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
   state->pen.conceal = 0;   setpenattr_bool(state, VTERM_ATTR_CONCEAL, 0);
   state->pen.strike = 0;    setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
-  state->pen.font = 0;      setpenattr_int( state, VTERM_ATTR_FONT, 0);
+  state->pen.font = 0;      setpenattr_int (state, VTERM_ATTR_FONT, 0);
+  state->pen.small = 0;     setpenattr_bool(state, VTERM_ATTR_SMALL, 0);
+  state->pen.baseline = 0;  setpenattr_int (state, VTERM_ATTR_BASELINE, 0);
 
   state->pen.fg = state->default_fg;  setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->default_fg);
   state->pen.bg = state->default_bg;  setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->default_bg);
@@ -195,14 +196,17 @@ INTERNAL void vterm_state_savepen(VTermS
   else {
     state->pen = state->saved.pen;
 
-    setpenattr_bool(state, VTERM_ATTR_BOLD,       state->pen.bold);
-    setpenattr_int( state, VTERM_ATTR_UNDERLINE,  state->pen.underline);
-    setpenattr_bool(state, VTERM_ATTR_ITALIC,     state->pen.italic);
-    setpenattr_bool(state, VTERM_ATTR_BLINK,      state->pen.blink);
-    setpenattr_bool(state, VTERM_ATTR_REVERSE,    state->pen.reverse);
-    setpenattr_bool(state, VTERM_ATTR_CONCEAL,    state->pen.conceal);
-    setpenattr_bool(state, VTERM_ATTR_STRIKE,     state->pen.strike);
-    setpenattr_int( state, VTERM_ATTR_FONT,       state->pen.font);
+    setpenattr_bool(state, VTERM_ATTR_BOLD,      state->pen.bold);
+    setpenattr_int (state, VTERM_ATTR_UNDERLINE, state->pen.underline);
+    setpenattr_bool(state, VTERM_ATTR_ITALIC,    state->pen.italic);
+    setpenattr_bool(state, VTERM_ATTR_BLINK,     state->pen.blink);
+    setpenattr_bool(state, VTERM_ATTR_REVERSE,   state->pen.reverse);
+    setpenattr_bool(state, VTERM_ATTR_CONCEAL,   state->pen.conceal);
+    setpenattr_bool(state, VTERM_ATTR_STRIKE,    state->pen.strike);
+    setpenattr_int (state, VTERM_ATTR_FONT,      state->pen.font);
+    setpenattr_bool(state, VTERM_ATTR_SMALL,     state->pen.small);
+    setpenattr_int (state, VTERM_ATTR_BASELINE,  state->pen.baseline);
+
     setpenattr_col( state, VTERM_ATTR_FOREGROUND, state->pen.fg);
     setpenattr_col( state, VTERM_ATTR_BACKGROUND, state->pen.bg);
   }
@@ -447,6 +451,18 @@ INTERNAL void vterm_state_setpen(VTermSt
       setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
       break;
 
+    case 73: // Superscript
+    case 74: // Subscript
+    case 75: // Superscript/subscript off
+      state->pen.small = (arg != 75);
+      state->pen.baseline =
+        (arg == 73) ? VTERM_BASELINE_RAISE :
+        (arg == 74) ? VTERM_BASELINE_LOWER :
+                      VTERM_BASELINE_NORMAL;
+      setpenattr_bool(state, VTERM_ATTR_SMALL,    state->pen.small);
+      setpenattr_int (state, VTERM_ATTR_BASELINE, state->pen.baseline);
+      break;
+
     case 90: case 91: case 92: case 93:
     case 94: case 95: case 96: case 97: // Foreground colour high-intensity palette
       value = CSI_ARG(args[argi]) - 90 + 8;
@@ -544,6 +560,13 @@ INTERNAL int vterm_state_getpen(VTermSta
 
   argi = vterm_state_getpen_color(&state->pen.bg, argi, args, FALSE);
 
+  if(state->pen.small) {
+    if(state->pen.baseline == VTERM_BASELINE_RAISE)
+      args[argi++] = 73;
+    else if(state->pen.baseline == VTERM_BASELINE_LOWER)
+      args[argi++] = 74;
+  }
+
   return argi;
 }
 
@@ -590,6 +613,14 @@ int vterm_state_get_penattr(const VTermS
     val->color = state->pen.bg;
     return 1;
 
+  case VTERM_ATTR_SMALL:
+    val->boolean = state->pen.small;
+    return 1;
+
+  case VTERM_ATTR_BASELINE:
+    val->number = state->pen.baseline;
+    return 1;
+
   case VTERM_N_ATTRS:
     return 0;
   }
--- a/src/libvterm/src/screen.c
+++ b/src/libvterm/src/screen.c
@@ -10,6 +10,8 @@
 #define UNICODE_SPACE 0x20
 #define UNICODE_LINEFEED 0x0a
 
+#undef DEBUG_REFLOW
+
 /* State of the pen at some moment in time, also used in a cell */
 typedef struct
 {
@@ -24,6 +26,8 @@ typedef struct
   unsigned int conceal   : 1;
   unsigned int strike    : 1;
   unsigned int font      : 4; /* 0 to 9 */
+  unsigned int small     : 1;
+  unsigned int baseline  : 2;
 
   /* Extra state storage that isn't strictly pen-related */
   unsigned int protected_cell : 1;
@@ -54,7 +58,9 @@ struct VTermScreen
 
   int rows;
   int cols;
-  int global_reverse;
+
+  unsigned int global_reverse : 1;
+  unsigned int reflow : 1;
 
   /* Primary and Altscreen. buffers[1] is lazily allocated as needed */
   ScreenCell *buffers[2];
@@ -88,11 +94,9 @@ static ScreenCell *getcell(const VTermSc
 static ScreenCell *alloc_buffer(VTermScreen *screen, int rows, int cols)
 {
   ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt, sizeof(ScreenCell) * rows * cols);
-  int row;
-  int col;
 
-  for(row = 0; row < rows; row++) {
-    for(col = 0; col < cols; col++) {
+  for(int row = 0; row < rows; row++) {
+    for(int col = 0; col < cols; col++) {
       clearcell(screen, &new_buffer[row * cols + col]);
     }
   }
@@ -168,16 +172,13 @@ static void damagescreen(VTermScreen *sc
 
 static int putglyph(VTermGlyphInfo *info, VTermPos pos, void *user)
 {
-  int i;
-  int col;
-  VTermRect rect;
-
   VTermScreen *screen = user;
   ScreenCell *cell = getcell(screen, pos.row, pos.col);
 
   if(!cell)
     return 0;
 
+  int i;
   for(i = 0; i < VTERM_MAX_CHARS_PER_CELL && info->chars[i]; i++) {
     cell->chars[i] = info->chars[i];
     cell->pen = screen->pen;
@@ -185,7 +186,7 @@ static int putglyph(VTermGlyphInfo *info
   if(i < VTERM_MAX_CHARS_PER_CELL)
     cell->chars[i] = 0;
 
-  for(col = 1; col < info->width; col++)
+  for(int col = 1; col < info->width; col++)
   {
     ScreenCell *onecell = getcell(screen, pos.row, pos.col + col);
     if (onecell == NULL)
@@ -193,6 +194,7 @@ static int putglyph(VTermGlyphInfo *info
     onecell->chars[0] = (uint32_t)-1;
   }
 
+  VTermRect rect;
   rect.start_row = pos.row;
   rect.end_row   = pos.row+1;
   rect.start_col = pos.col;
@@ -225,33 +227,29 @@ static int moverect_internal(VTermRect d
      dest.start_row == 0 && dest.start_col == 0 &&        // starts top-left corner
      dest.end_col == screen->cols &&                      // full width
      screen->buffer == screen->buffers[BUFIDX_PRIMARY]) { // not altscreen
-    int row;
-    for(row = 0; row < src.start_row; row++)
+    for(int row = 0; row < src.start_row; row++)
       sb_pushline_from_row(screen, row);
   }
 
-  {
-    int cols = src.end_col - src.start_col;
-    int downward = src.start_row - dest.start_row;
-    int init_row, test_row, inc_row;
-    int row;
+  int cols = src.end_col - src.start_col;
+  int downward = src.start_row - dest.start_row;
 
-    if(downward < 0) {
-      init_row = dest.end_row - 1;
-      test_row = dest.start_row - 1;
-      inc_row  = -1;
-    }
-    else {
-      init_row = dest.start_row;
-      test_row = dest.end_row;
-      inc_row  = +1;
-    }
+  int init_row, test_row, inc_row;
+  if(downward < 0) {
+    init_row = dest.end_row - 1;
+    test_row = dest.start_row - 1;
+    inc_row  = -1;
+  }
+  else {
+    init_row = dest.start_row;
+    test_row = dest.end_row;
+    inc_row  = +1;
+  }
 
-    for(row = init_row; row != test_row; row += inc_row)
-      memmove(getcell(screen, row, dest.start_col),
-	      getcell(screen, row + downward, src.start_col),
-	      cols * sizeof(ScreenCell));
-  }
+  for(int row = init_row; row != test_row; row += inc_row)
+    memmove(getcell(screen, row, dest.start_col),
+            getcell(screen, row + downward, src.start_col),
+            cols * sizeof(ScreenCell));
 
   return 1;
 }
@@ -277,12 +275,11 @@ static int moverect_user(VTermRect dest,
 static int erase_internal(VTermRect rect, int selective, void *user)
 {
   VTermScreen *screen = user;
-  int row, col;
 
-  for(row = rect.start_row; row < screen->state->rows && row < rect.end_row; row++) {
+  for(int row = rect.start_row; row < screen->state->rows && row < rect.end_row; row++) {
     const VTermLineInfo *info = vterm_state_get_lineinfo(screen->state, row);
 
-    for(col = rect.start_col; col < rect.end_col; col++) {
+    for(int col = rect.start_col; col < rect.end_col; col++) {
       ScreenCell *cell = getcell(screen, row, col);
 
       if (cell == NULL)
@@ -448,6 +445,12 @@ static int setpenattr(VTermAttr attr, VT
   case VTERM_ATTR_BACKGROUND:
     screen->pen.bg = val->color;
     return 1;
+  case VTERM_ATTR_SMALL:
+    screen->pen.small = val->boolean;
+    return 1;
+  case VTERM_ATTR_BASELINE:
+    screen->pen.baseline = val->number;
+    return 1;
 
   case VTERM_N_ATTRS:
     return 0;
@@ -496,6 +499,18 @@ static int bell(void *user)
   return 0;
 }
 
+/* How many cells are non-blank
+ * Returns the position of the first blank cell in the trailing blank end */
+static int line_popcount(ScreenCell *buffer, int row, int rows UNUSED, int cols)
+{
+  int col = cols - 1;
+  while(col >= 0 && buffer[row * cols + col].chars[0] == 0)
+    col--;
+  return col + 1;
+}
+
+#define REFLOW (screen->reflow)
+
 static void resize_buffer(VTermScreen *screen, int bufidx, int new_rows, int new_cols, int active, VTermStateFields *statefields)
 {
   int old_rows = screen->rows;
@@ -510,33 +525,150 @@ static void resize_buffer(VTermScreen *s
   // Find the final row of old buffer content
   int old_row = old_rows - 1;
   int new_row = new_rows - 1;
-  int col;
+
+  VTermPos old_cursor = statefields->pos;
+  VTermPos new_cursor = { -1, -1 };
+
+#ifdef DEBUG_REFLOW
+  fprintf(stderr, "Resizing from %dx%d to %dx%d; cursor was at (%d,%d)\n",
+      old_cols, old_rows, new_cols, new_rows, old_cursor.col, old_cursor.row);
+#endif
+
+  /* Keep track of the final row that is knonw to be blank, so we know what
+   * spare space we have for scrolling into
+   */
+  int final_blank_row = new_rows;
+
+  while(old_row >= 0) {
+    int old_row_end = old_row;
+    /* TODO: Stop if dwl or dhl */
+    while(REFLOW && old_lineinfo && old_row >= 0 && old_lineinfo[old_row].continuation)
+      old_row--;
+    int old_row_start = old_row;
+
+    int width = 0;
+    for(int row = old_row_start; row <= old_row_end; row++) {
+      if(REFLOW && row < (old_rows - 1) && old_lineinfo[row + 1].continuation)
+        width += old_cols;
+      else
+        width += line_popcount(old_buffer, row, old_rows, old_cols);
+    }
+
+    if(final_blank_row == (new_row + 1) && width == 0)
+      final_blank_row = new_row;
 
-  while(new_row >= 0 && old_row >= 0) {
-    for(col = 0; col < old_cols && col < new_cols; col++)
-      new_buffer[new_row * new_cols + col] = old_buffer[old_row * old_cols + col];
-    for( ; col < new_cols; col++)
-      clearcell(screen, &new_buffer[new_row * new_cols + col]);
+    int new_height = REFLOW
+      ? width ? (width + new_cols - 1) / new_cols : 1
+      : 1;
+
+    int new_row_end = new_row;
+    int new_row_start = new_row - new_height + 1;
+
+    old_row = old_row_start;
+    int old_col = 0;
+
+    int spare_rows = new_rows - final_blank_row;
 
-    new_lineinfo[new_row] = old_lineinfo[old_row];
+    if(new_row_start < 0 && /* we'd fall off the top */
+        spare_rows >= 0 && /* we actually have spare rows */
+        (!active || new_cursor.row == -1 || (new_cursor.row - new_row_start) < new_rows))
+    {
+      /* Attempt to scroll content down into the blank rows at the bottom to
+       * make it fit
+       */
+      int downwards = -new_row_start;
+      if(downwards > spare_rows)
+        downwards = spare_rows;
+      int rowcount = new_rows - downwards;
+
+#ifdef DEBUG_REFLOW
+      fprintf(stderr, "  scroll %d rows +%d downwards\n", rowcount, downwards);
+#endif
+
+      memmove(&new_buffer[downwards * new_cols], &new_buffer[0],   rowcount * new_cols * sizeof(ScreenCell));
+      memmove(&new_lineinfo[downwards],          &new_lineinfo[0], rowcount            * sizeof(new_lineinfo[0]));
+
+      new_row += downwards;
+      new_row_start += downwards;
+      new_row_end += downwards;
+
+      if(new_cursor.row >= 0)
+        new_cursor.row += downwards;
 
-    old_row--;
-    new_row--;
+      final_blank_row += downwards;
+    }
+
+#ifdef DEBUG_REFLOW
+    fprintf(stderr, "  rows [%d..%d] <- [%d..%d] width=%d\n",
+        new_row_start, new_row_end, old_row_start, old_row_end, width);
+#endif
+
+    if(new_row_start < 0)
+      break;
+
+    for(new_row = new_row_start, old_row = old_row_start; new_row <= new_row_end; new_row++) {
+      int count = width >= new_cols ? new_cols : width;
+      width -= count;
+
+      int new_col = 0;
+
+      while(count) {
+        /* TODO: This could surely be done a lot faster by memcpy()'ing the entire range */
+        new_buffer[new_row * new_cols + new_col] = old_buffer[old_row * old_cols + old_col];
+
+        if(old_cursor.row == old_row && old_cursor.col == old_col)
+          new_cursor.row = new_row, new_cursor.col = new_col;
+
+        old_col++;
+        if(old_col == old_cols) {
+          old_row++;
+
+          if(!REFLOW) {
+            new_col++;
+            break;
+          }
+          old_col = 0;
+        }
 
-    if(new_row < 0 && old_row >= 0 &&
-        new_buffer[(new_rows - 1) * new_cols].chars[0] == 0 &&
-        (!active || statefields->pos.row < (new_rows - 1))) {
-      int moverows = new_rows - 1;
-      memmove(&new_buffer[1 * new_cols], &new_buffer[0], moverows * new_cols * sizeof(ScreenCell));
+        new_col++;
+        count--;
+      }
+
+      if(old_cursor.row == old_row && old_cursor.col >= old_col) {
+        new_cursor.row = new_row, new_cursor.col = (old_cursor.col - old_col + new_col);
+        if(new_cursor.col >= new_cols)
+          new_cursor.col = new_cols-1;
+      }
+
+      while(new_col < new_cols) {
+        clearcell(screen, &new_buffer[new_row * new_cols + new_col]);
+        new_col++;
+      }
 
-      new_row++;
+      new_lineinfo[new_row].continuation = (new_row > new_row_start);
     }
+
+    old_row = old_row_start - 1;
+    new_row = new_row_start - 1;
+  }
+
+  if(old_cursor.row <= old_row) {
+    /* cursor would have moved entirely off the top of the screen; lets just
+     * bring it within range */
+    new_cursor.row = 0, new_cursor.col = old_cursor.col;
+    if(new_cursor.col >= new_cols)
+      new_cursor.col = new_cols-1;
+  }
+
+  /* We really expect the cursor position to be set by now */
+  if(active && (new_cursor.row == -1 || new_cursor.col == -1)) {
+    fprintf(stderr, "screen_resize failed to update cursor position\n");
+    abort();
   }
 
   if(old_row >= 0 && bufidx == BUFIDX_PRIMARY) {
     /* Push spare lines to scrollback buffer */
-    int row;
-    for(row = 0; row <= old_row; row++)
+    for(int row = 0; row <= old_row; row++)
       sb_pushline_from_row(screen, row);
     if(active)
       statefields->pos.row -= (old_row + 1);
@@ -553,9 +685,8 @@ static void resize_buffer(VTermScreen *s
       for(pos.col = 0; pos.col < old_cols && pos.col < new_cols; pos.col += screen->sb_buffer[pos.col].width) {
         VTermScreenCell *src = &screen->sb_buffer[pos.col];
         ScreenCell *dst = &new_buffer[pos.row * new_cols + pos.col];
-	int i;
 
-        for(i = 0; i < VTERM_MAX_CHARS_PER_CELL; i++) {
+        for(int i = 0; i < VTERM_MAX_CHARS_PER_CELL; i++) {
           dst->chars[i] = src->chars[i];
           if(!src->chars[i])
             break;
@@ -569,6 +700,8 @@ static void resize_buffer(VTermScreen *s
         dst->pen.conceal   = src->attrs.conceal;
         dst->pen.strike    = src->attrs.strike;
         dst->pen.font      = src->attrs.font;
+        dst->pen.small     = src->attrs.small;
+        dst->pen.baseline  = src->attrs.baseline;
 
         dst->pen.fg = src->fg;
         dst->pen.bg = src->bg;
@@ -584,15 +717,16 @@ static void resize_buffer(VTermScreen *s
         statefields->pos.row++;
     }
   }
-
   if(new_row >= 0) {
     /* Scroll new rows back up to the top and fill in blanks at the bottom */
     int moverows = new_rows - new_row - 1;
     memmove(&new_buffer[0], &new_buffer[(new_row + 1) * new_cols], moverows * new_cols * sizeof(ScreenCell));
     memmove(&new_lineinfo[0], &new_lineinfo[new_row + 1], moverows * sizeof(new_lineinfo[0]));
 
+    new_cursor.row -= (new_row + 1);
+
     for(new_row = moverows; new_row < new_rows; new_row++) {
-      for(col = 0; col < new_cols; col++)
+      for(int col = 0; col < new_cols; col++)
         clearcell(screen, &new_buffer[new_row * new_cols + col]);
       new_lineinfo[new_row] = (VTermLineInfo){ 0 };
     }
@@ -604,11 +738,10 @@ static void resize_buffer(VTermScreen *s
   vterm_allocator_free(screen->vt, old_lineinfo);
   statefields->lineinfos[bufidx] = new_lineinfo;
 
-  return;
+  if(active)
+    statefields->pos = new_cursor;
 
-  /* REFLOW TODO:
-   *   Handle delta. Probably needs to be a full cursorpos that we edit
-   */
+  return;
 }
 
 static int resize(int new_rows, int new_cols, VTermStateFields *fields, void *user)
@@ -667,12 +800,10 @@ static int resize(int new_rows, int new_
 static int setlineinfo(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user)
 {
   VTermScreen *screen = user;
-  int col;
-  VTermRect rect;
 
   if(newinfo->doublewidth != oldinfo->doublewidth ||
      newinfo->doubleheight != oldinfo->doubleheight) {
-    for(col = 0; col < screen->cols; col++) {
+    for(int col = 0; col < screen->cols; col++) {
       ScreenCell *cell = getcell(screen, row, col);
       if (cell == NULL)
       {
@@ -684,6 +815,7 @@ static int setlineinfo(int row, const VT
       cell->pen.dhl = newinfo->doubleheight;
     }
 
+    VTermRect rect;
     rect.start_row = row;
     rect.end_row   = row + 1;
     rect.start_col = 0;
@@ -701,6 +833,16 @@ static int setlineinfo(int row, const VT
   return 1;
 }
 
+static int sb_clear(void *user) {
+  VTermScreen *screen = user;
+
+  if(screen->callbacks && screen->callbacks->sb_clear)
+    if((*screen->callbacks->sb_clear)(screen->cbdata))
+      return 1;
+
+  return 0;
+}
+
 static VTermStateCallbacks state_cbs = {
   &putglyph, // putglyph
   &movecursor, // movecursor
@@ -712,7 +854,8 @@ static VTermStateCallbacks state_cbs = {
   &settermprop, // settermprop
   &bell, // bell
   &resize, // resize
-  &setlineinfo // setlineinfo
+  &setlineinfo, // setlineinfo
+  &sb_clear, //sb_clear
 };
 
 /*
@@ -722,14 +865,13 @@ static VTermStateCallbacks state_cbs = {
 static VTermScreen *screen_new(VTerm *vt)
 {
   VTermState *state = vterm_obtain_state(vt);
-  VTermScreen *screen;
-  int rows, cols;
+  if(!state)
+    return NULL;
 
-  if (state == NULL)
-    return NULL;
-  screen = vterm_allocator_malloc(vt, sizeof(VTermScreen));
+  VTermScreen *screen = vterm_allocator_malloc(vt, sizeof(VTermScreen));
   if (screen == NULL)
     return NULL;
+  int rows, cols;
 
   vterm_get_size(vt, &rows, &cols);
 
@@ -743,6 +885,9 @@ static VTermScreen *screen_new(VTerm *vt
   screen->rows = rows;
   screen->cols = cols;
 
+  screen->global_reverse = FALSE;
+  screen->reflow = FALSE;
+
   screen->callbacks = NULL;
   screen->cbdata    = NULL;
 
@@ -785,7 +930,6 @@ static size_t _get_chars(const VTermScre
 {
   size_t outpos = 0;
   int padding = 0;
-  int row, col;
 
 #define PUT(c)                                             \
   if(utf8) {                                               \
@@ -802,10 +946,9 @@ static size_t _get_chars(const VTermScre
       outpos++;                                            \
   }
 
-  for(row = rect.start_row; row < rect.end_row; row++) {
-    for(col = rect.start_col; col < rect.end_col; col++) {
+  for(int row = rect.start_row; row < rect.end_row; row++) {
+    for(int col = rect.start_col; col < rect.end_col; col++) {
       ScreenCell *cell = getcell(screen, row, col);
-      int i;
 
       if (cell == NULL)
       {
@@ -824,7 +967,7 @@ static size_t _get_chars(const VTermScre
           PUT(UNICODE_SPACE);
           padding--;
         }
-        for(i = 0; i < VTERM_MAX_CHARS_PER_CELL && cell->chars[i]; i++) {
+        for(int i = 0; i < VTERM_MAX_CHARS_PER_CELL && cell->chars[i]; i++) {
           PUT(cell->chars[i]);
         }
       }
@@ -853,12 +996,11 @@ size_t vterm_screen_get_text(const VTerm
 int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCell *cell)
 {
   ScreenCell *intcell = getcell(screen, pos.row, pos.col);
-  int i;
 
   if(!intcell)
     return 0;
 
-  for(i = 0; i < VTERM_MAX_CHARS_PER_CELL; i++) {
+  for(int i = 0; i < VTERM_MAX_CHARS_PER_CELL; i++) {
     cell->chars[i] = intcell->chars[i];
     if(!intcell->chars[i])
       break;
@@ -872,6 +1014,8 @@ int vterm_screen_get_cell(const VTermScr
   cell->attrs.conceal   = intcell->pen.conceal;
   cell->attrs.strike    = intcell->pen.strike;
   cell->attrs.font      = intcell->pen.font;
+  cell->attrs.small     = intcell->pen.small;
+  cell->attrs.baseline  = intcell->pen.baseline;
 
   cell->attrs.dwl = intcell->pen.dwl;
   cell->attrs.dhl = intcell->pen.dhl;
@@ -919,11 +1063,25 @@ int vterm_screen_is_eol(const VTermScree
 
 VTermScreen *vterm_obtain_screen(VTerm *vt)
 {
-  if(!vt->screen)
-    vt->screen = screen_new(vt);
+  if(vt->screen)
+    return vt->screen;
+
+  vt->screen = screen_new(vt);
   return vt->screen;
 }
 
+void vterm_screen_enable_reflow(VTermScreen *screen, int reflow)
+{
+  screen->reflow = reflow;
+}
+
+// Removed, causes a compiler warning and isn't used
+// #undef vterm_screen_set_reflow
+// void vterm_screen_set_reflow(VTermScreen *screen, int reflow)
+// {
+//   vterm_screen_enable_reflow(screen, reflow);
+// }
+
 void vterm_screen_enable_altscreen(VTermScreen *screen, int altscreen)
 {
   if(!screen->buffers[BUFIDX_ALTSCREEN] && altscreen) {
@@ -1000,14 +1158,16 @@ static int attrs_differ(VTermAttrMask at
     return 1;
   if((attrs & VTERM_ATTR_BACKGROUND_MASK) && !vterm_color_is_equal(&a->pen.bg, &b->pen.bg))
     return 1;
+  if((attrs & VTERM_ATTR_SMALL_MASK)    && (a->pen.small != b->pen.small))
+    return 1;
+  if((attrs & VTERM_ATTR_BASELINE_MASK)    && (a->pen.baseline != b->pen.baseline))
+    return 1;
 
   return 0;
 }
 
 int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs)
 {
-  int col;
-
   ScreenCell *target = getcell(screen, pos.row, pos.col);
 
   // TODO: bounds check
@@ -1019,6 +1179,8 @@ int vterm_screen_get_attrs_extent(const 
   if(extent->end_col < 0)
     extent->end_col = screen->cols;
 
+  int col;
+
   for(col = pos.col - 1; col >= extent->start_col; col--)
     if(attrs_differ(attrs, target, getcell(screen, pos.row, col)))
       break;
--- a/src/libvterm/src/state.c
+++ b/src/libvterm/src/state.c
@@ -387,9 +387,12 @@ static int on_text(const char bytes[], s
       }
 #endif
       if (i == glyph_starts || this_width > width)
-	width = this_width;
+	width = this_width;  // TODO: should be += ?
     }
 
+    while(i < npoints && vterm_unicode_is_combining(codepoints[i]))
+      i++;
+
     chars[glyph_ends - glyph_starts] = 0;
     i--;
 
@@ -1149,6 +1152,12 @@ static int on_csi(const char *leader, co
 	set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
       erase(state, rect, selective);
       break;
+
+    case 3:
+      if(state->callbacks && state->callbacks->sb_clear)
+        if((*state->callbacks->sb_clear)(state->cbdata))
+          return 1;
+      break;
     }
     break;
 
@@ -1391,6 +1400,29 @@ static int on_csi(const char *leader, co
     vterm_state_setpen(state, args, argcount);
     break;
 
+  case LEADER('?', 0x6d): // DECSGR
+    /* No actual DEC terminal recognised these, but some printers did. These
+     * are alternative ways to request subscript/superscript/off
+     */
+    for(int argi = 0; argi < argcount; argi++) {
+      long arg;
+      switch(arg = CSI_ARG(args[argi])) {
+        case 4: // Superscript on
+          arg = 73;
+          vterm_state_setpen(state, &arg, 1);
+          break;
+        case 5: // Subscript on
+          arg = 74;
+          vterm_state_setpen(state, &arg, 1);
+          break;
+        case 24: // Super+subscript off
+          arg = 75;
+          vterm_state_setpen(state, &arg, 1);
+          break;
+      }
+    }
+    break;
+
   case LEADER('>', 0x6d): // xterm resource modifyOtherKeys
     if (argcount == 2 && args[0] == 4)
       state->mode.modify_other_keys = args[1] == 2;
@@ -1857,11 +1889,11 @@ static void request_status_string(VTermS
   VTerm *vt = state->vt;
 
   char *tmp = state->tmp.decrqss;
-  size_t i = 0;
 
   if(frag.initial)
     tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0;
 
+  size_t i = 0;
   while(i < sizeof(state->tmp.decrqss)-1 && tmp[i])
     i++;
   while(i < sizeof(state->tmp.decrqss)-1 && frag.len--)
@@ -1877,14 +1909,13 @@ static void request_status_string(VTermS
       long args[20];
       int argc = vterm_state_getpen(state, args, sizeof(args)/sizeof(args[0]));
       size_t cur = 0;
-      int argi;
 
       cur += SNPRINTF(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
           vt->mode.ctrl8bit ? "\x90" "1$r" : ESC_S "P" "1$r"); // DCS 1$r ...
       if(cur >= vt->tmpbuffer_len)
         return;
 
-      for(argi = 0; argi < argc; argi++) {
+      for(int argi = 0; argi < argc; argi++) {
         cur += SNPRINTF(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
             argi == argc - 1             ? "%ld" :
             CSI_ARG_HAS_MORE(args[argi]) ? "%ld:" :
@@ -1996,15 +2027,14 @@ static int on_resize(int rows, int cols,
 {
   VTermState *state = user;
   VTermPos oldpos = state->pos;
-  VTermStateFields fields;
 
   if(cols != state->cols) {
-    int col;
     unsigned char *newtabstops = vterm_allocator_malloc(state->vt, (cols + 7) / 8);
     if (newtabstops == NULL)
       return 0;
 
     /* TODO: This can all be done much more efficiently bytewise */
+    int col;
     for(col = 0; col < state->cols && col < cols; col++) {
       unsigned char mask = 1 << (col & 7);
       if(state->tabstops[col >> 3] & mask)
@@ -2033,13 +2063,13 @@ static int on_resize(int rows, int cols,
   if(state->scrollregion_right > -1)
     UBOUND(state->scrollregion_right, state->cols);
 
+  VTermStateFields fields;
   fields.pos = state->pos;
   fields.lineinfos[0] = state->lineinfos[0];
   fields.lineinfos[1] = state->lineinfos[1];
 
   if(state->callbacks && state->callbacks->resize) {
     (*state->callbacks->resize)(rows, cols, &fields, state->cbdata);
-
     state->pos = fields.pos;
 
     state->lineinfos[0] = fields.lineinfos[0];
@@ -2111,11 +2141,10 @@ static const VTermParserCallbacks parser
  */
 VTermState *vterm_obtain_state(VTerm *vt)
 {
-  VTermState *state;
   if(vt->state)
     return vt->state;
 
-  state = vterm_state_new(vt);
+  VTermState *state = vterm_state_new(vt);
   if (state == NULL)
     return NULL;
   vt->state = state;
@@ -2127,8 +2156,6 @@ VTermState *vterm_obtain_state(VTerm *vt
 
 void vterm_state_reset(VTermState *state, int hard)
 {
-  VTermEncoding *default_enc;
-
   state->scrollregion_top = 0;
   state->scrollregion_bottom = -1;
   state->scrollregion_left = 0;
@@ -2149,37 +2176,28 @@ void vterm_state_reset(VTermState *state
 
   state->vt->mode.ctrl8bit   = 0;
 
-  {
-    int col;
-    for(col = 0; col < state->cols; col++)
-      if(col % 8 == 0)
-	set_col_tabstop(state, col);
-      else
-	clear_col_tabstop(state, col);
-  }
-
-  {
-    int row;
-    for(row = 0; row < state->rows; row++)
-      set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
-  }
+  for(int col = 0; col < state->cols; col++)
+    if(col % 8 == 0)
+      set_col_tabstop(state, col);
+    else
+      clear_col_tabstop(state, col);
+
+  for(int row = 0; row < state->rows; row++)
+    set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
 
   if(state->callbacks && state->callbacks->initpen)
     (*state->callbacks->initpen)(state->cbdata);
 
   vterm_state_resetpen(state);
 
-  default_enc = state->vt->mode.utf8 ?
+  VTermEncoding *default_enc = state->vt->mode.utf8 ?
       vterm_lookup_encoding(ENC_UTF8,      'u') :
       vterm_lookup_encoding(ENC_SINGLE_94, 'B');
 
-  {
-    int i;
-    for(i = 0; i < 4; i++) {
-      state->encoding[i].enc = default_enc;
-      if(default_enc->init)
-	(*default_enc->init)(default_enc, state->encoding[i].data);
-    }
+  for(int i = 0; i < 4; i++) {
+    state->encoding[i].enc = default_enc;
+    if(default_enc->init)
+      (*default_enc->init)(default_enc, state->encoding[i].data);
   }
 
   state->gl_set = 0;
@@ -2194,12 +2212,11 @@ void vterm_state_reset(VTermState *state
   settermprop_int (state, VTERM_PROP_CURSORSHAPE,   VTERM_PROP_CURSORSHAPE_BLOCK);
 
   if(hard) {
-    VTermRect rect = { 0, 0, 0, 0 };
-
     state->pos.row = 0;
     state->pos.col = 0;
     state->at_phantom = 0;
 
+    VTermRect rect = { 0, 0, 0, 0 };
     rect.end_row = state->rows;
     rect.end_col =  state->cols;
     erase(state, rect, 0);
--- a/src/libvterm/src/unicode.c
+++ b/src/libvterm/src/unicode.c
@@ -452,20 +452,64 @@ static int mk_wcwidth(uint32_t ucs)
 }
 #endif
 
-#if 0 // unused
-static int mk_wcswidth(const uint32_t *pwcs, size_t n)
-{
-  int w, width = 0;
+/* sorted list of non-overlapping intervals of East Asian Ambiguous
+* characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */
+static const struct interval ambiguous[] = {
+{ 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 },
+{ 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 },
+{ 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 },
+{ 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 },
+{ 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED },
+{ 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA },
+{ 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 },
+{ 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B },
+{ 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 },
+{ 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 },
+{ 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 },
+{ 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE },
+{ 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 },
+{ 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA },
+{ 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 },
+{ 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB },
+{ 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB },
+{ 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 },
+{ 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 },
+{ 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 },
+{ 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 },
+{ 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 },
+{ 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 },
+{ 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 },
+{ 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC },
+{ 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 },
+{ 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 },
+{ 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 },
+{ 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 },
+{ 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, { 0x21D2, 0x21D2 },
+{ 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, { 0x2200, 0x2200 },
+{ 0x2202, 0x2203 }, { 0x2207, 0x2208 }, { 0x220B, 0x220B },
+{ 0x220F, 0x220F }, { 0x2211, 0x2211 }, { 0x2215, 0x2215 },
+{ 0x221A, 0x221A }, { 0x221D, 0x2220 }, { 0x2223, 0x2223 },
+{ 0x2225, 0x2225 }, { 0x2227, 0x222C }, { 0x222E, 0x222E },
+{ 0x2234, 0x2237 }, { 0x223C, 0x223D }, { 0x2248, 0x2248 },
+{ 0x224C, 0x224C }, { 0x2252, 0x2252 }, { 0x2260, 0x2261 },
+{ 0x2264, 0x2267 }, { 0x226A, 0x226B }, { 0x226E, 0x226F },
+{ 0x2282, 0x2283 }, { 0x2286, 0x2287 }, { 0x2295, 0x2295 },
+{ 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, { 0x22BF, 0x22BF },
+{ 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, { 0x24EB, 0x254B },
+{ 0x2550, 0x2573 }, { 0x2580, 0x258F }, { 0x2592, 0x2595 },
+{ 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 },
+{ 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 },
+{ 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 },
+{ 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, { 0x2605, 0x2606 },
+{ 0x2609, 0x2609 }, { 0x260E, 0x260F }, { 0x2614, 0x2615 },
+{ 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 },
+{ 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 },
+{ 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F },
+{ 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF },
+{ 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD }
+};
 
-  for (;*pwcs && n-- > 0; pwcs++)
-    if ((w = mk_wcwidth(*pwcs)) < 0)
-      return -1;
-    else
-      width += w;
-
-  return width;
-}
-
+#ifdef USE_MK_WCWIDTH_CJK
 
 /*
  * The following functions are the same as mk_wcwidth() and
@@ -478,65 +522,6 @@ static int mk_wcswidth(const uint32_t *p
  */
 static int mk_wcwidth_cjk(uint32_t ucs)
 {
-#endif
-  /* sorted list of non-overlapping intervals of East Asian Ambiguous
-   * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */
-  static const struct interval ambiguous[] = {
-    { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 },
-    { 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 },
-    { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 },
-    { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 },
-    { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED },
-    { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA },
-    { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 },
-    { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B },
-    { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 },
-    { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 },
-    { 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 },
-    { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE },
-    { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 },
-    { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA },
-    { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 },
-    { 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB },
-    { 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB },
-    { 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 },
-    { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 },
-    { 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 },
-    { 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 },
-    { 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 },
-    { 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 },
-    { 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 },
-    { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC },
-    { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 },
-    { 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 },
-    { 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 },
-    { 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 },
-    { 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, { 0x21D2, 0x21D2 },
-    { 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, { 0x2200, 0x2200 },
-    { 0x2202, 0x2203 }, { 0x2207, 0x2208 }, { 0x220B, 0x220B },
-    { 0x220F, 0x220F }, { 0x2211, 0x2211 }, { 0x2215, 0x2215 },
-    { 0x221A, 0x221A }, { 0x221D, 0x2220 }, { 0x2223, 0x2223 },
-    { 0x2225, 0x2225 }, { 0x2227, 0x222C }, { 0x222E, 0x222E },
-    { 0x2234, 0x2237 }, { 0x223C, 0x223D }, { 0x2248, 0x2248 },
-    { 0x224C, 0x224C }, { 0x2252, 0x2252 }, { 0x2260, 0x2261 },
-    { 0x2264, 0x2267 }, { 0x226A, 0x226B }, { 0x226E, 0x226F },
-    { 0x2282, 0x2283 }, { 0x2286, 0x2287 }, { 0x2295, 0x2295 },
-    { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, { 0x22BF, 0x22BF },
-    { 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, { 0x24EB, 0x254B },
-    { 0x2550, 0x2573 }, { 0x2580, 0x258F }, { 0x2592, 0x2595 },
-    { 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 },
-    { 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 },
-    { 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 },
-    { 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, { 0x2605, 0x2606 },
-    { 0x2609, 0x2609 }, { 0x260E, 0x260F }, { 0x2614, 0x2615 },
-    { 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 },
-    { 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 },
-    { 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F },
-    { 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF },
-    { 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD }
-  };
-#if 0
-
   /* binary search in table of non-spacing characters */
   if (bisearch(ucs, ambiguous,
                sizeof(ambiguous) / sizeof(struct interval) - 1))
@@ -545,19 +530,6 @@ static int mk_wcwidth_cjk(uint32_t ucs)
   return mk_wcwidth(ucs);
 }
 
-
-static int mk_wcswidth_cjk(const uint32_t *pwcs, size_t n)
-{
-  int w, width = 0;
-
-  for (;*pwcs && n-- > 0; pwcs++)
-    if ((w = mk_wcwidth_cjk(*pwcs)) < 0)
-      return -1;
-    else
-      width += w;
-
-  return width;
-}
 #endif
 
 INTERNAL int vterm_unicode_is_ambiguous(uint32_t codepoint)
--- a/src/libvterm/src/vterm.c
+++ b/src/libvterm/src/vterm.c
@@ -134,6 +134,9 @@ void vterm_get_size(const VTerm *vt, int
 
 void vterm_set_size(VTerm *vt, int rows, int cols)
 {
+  if(rows < 1 || cols < 1)
+    return;
+
   vt->rows = rows;
   vt->cols = cols;
 
@@ -201,7 +204,6 @@ INTERNAL void vterm_push_output_sprintf(
 INTERNAL void vterm_push_output_sprintf_ctrl(VTerm *vt, unsigned char ctrl, const char *fmt, ...)
 {
   size_t cur;
-  va_list args;
 
   if(ctrl >= 0x80 && !vt->mode.ctrl8bit)
     cur = SNPRINTF(vt->tmpbuffer, vt->tmpbuffer_len,
@@ -213,6 +215,7 @@ INTERNAL void vterm_push_output_sprintf_
     return;
   vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
 
+  va_list args;
   va_start(args, fmt);
   vterm_push_output_vsprintf(vt, fmt, args);
   va_end(args);
@@ -221,7 +224,6 @@ INTERNAL void vterm_push_output_sprintf_
 INTERNAL void vterm_push_output_sprintf_str(VTerm *vt, unsigned char ctrl, int term, const char *fmt, ...)
 {
   size_t cur;
-  va_list args;
 
   if(ctrl) {
     if(ctrl >= 0x80 && !vt->mode.ctrl8bit)
@@ -236,6 +238,7 @@ INTERNAL void vterm_push_output_sprintf_
     vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
   }
 
+  va_list args;
   va_start(args, fmt);
   vterm_push_output_vsprintf(vt, fmt, args);
   va_end(args);
@@ -292,6 +295,8 @@ VTermValueType vterm_get_attr_type(VTerm
     case VTERM_ATTR_FONT:       return VTERM_VALUETYPE_INT;
     case VTERM_ATTR_FOREGROUND: return VTERM_VALUETYPE_COLOR;
     case VTERM_ATTR_BACKGROUND: return VTERM_VALUETYPE_COLOR;
+    case VTERM_ATTR_SMALL:      return VTERM_VALUETYPE_BOOL;
+    case VTERM_ATTR_BASELINE:   return VTERM_VALUETYPE_INT;
 
     case VTERM_N_ATTRS: return 0;
   }
@@ -396,8 +401,6 @@ void vterm_copy_cells(VTermRect dest,
   int init_row, test_row, init_col, test_col;
   int inc_row, inc_col;
 
-  VTermPos pos;
-
   if(downward < 0) {
     init_row = dest.end_row - 1;
     test_row = dest.start_row - 1;
@@ -420,6 +423,7 @@ void vterm_copy_cells(VTermRect dest,
     inc_col = +1;
   }
 
+  VTermPos pos;
   for(pos.row = init_row; pos.row != test_row; pos.row += inc_row)
     for(pos.col = init_col; pos.col != test_col; pos.col += inc_col) {
       VTermPos srcpos;
--- a/src/libvterm/src/vterm_internal.h
+++ b/src/libvterm/src/vterm_internal.h
@@ -60,6 +60,8 @@ struct VTermPen
   unsigned int conceal:1;
   unsigned int strike:1;
   unsigned int font:4; /* To store 0-9 */
+  unsigned int small:1;
+  unsigned int baseline:2;
 };
 
 struct VTermState
--- a/src/libvterm/t/10state_putglyph.test
+++ b/src/libvterm/t/10state_putglyph.test
@@ -52,6 +52,12 @@ PUSH "\xCC\x81Z"
   putglyph 0x65,0x301 1 0,0
   putglyph 0x5a 1 0,1
 
+!Spare combining chars get truncated
+RESET
+PUSH "e" . "\xCC\x81" x 10
+  putglyph 0x65,0x301,0x301,0x301,0x301,0x301 1 0,0
+  # and nothing more
+
 RESET
 PUSH "e"
   putglyph 0x65 1 0,0
--- a/src/libvterm/t/13state_edit.test
+++ b/src/libvterm/t/13state_edit.test
@@ -1,6 +1,6 @@
 INIT
 UTF8 1
-WANTSTATE se
+WANTSTATE seb
 
 !ICH
 RESET
@@ -242,6 +242,10 @@ PUSH "\e[2J"
   erase 0..25,0..80
   ?cursor = 1,1
 
+!ED 3
+PUSH "\e[3J"
+  sb_clear
+
 !SED
 RESET
   erase 0..25,0..80
--- a/src/libvterm/t/26state_query.test
+++ b/src/libvterm/t/26state_query.test
@@ -9,7 +9,7 @@ PUSH "\e[c"
 !XTVERSION
 RESET
 PUSH "\e[>q"
-  output "\eP>|libvterm(0.2)\e\\"
+  output "\eP>|libvterm(0.3)\e\\"
 
 !DSR
 RESET
--- a/src/libvterm/t/30state_pen.test
+++ b/src/libvterm/t/30state_pen.test
@@ -112,3 +112,14 @@ PUSH "\e[m\e[1;37m"
 PUSH "\e[m\e[37;1m"
   ?pen bold = on
   ?pen foreground = idx(15)
+
+!Super/Subscript
+PUSH "\e[73m"
+  ?pen small = on
+  ?pen baseline = raise
+PUSH "\e[74m"
+  ?pen small = on
+  ?pen baseline = lower
+PUSH "\e[75m"
+  ?pen small = off
+  ?pen baseline = normal
--- a/src/libvterm/t/64screen_pen.test
+++ b/src/libvterm/t/64screen_pen.test
@@ -35,6 +35,12 @@ PUSH "\e[31mG\e[m"
 PUSH "\e[42mH\e[m"
   ?screen_cell 0,7 = {0x48} width=1 attrs={} fg=rgb(240,240,240) bg=idx(2)
 
+!Super/subscript
+PUSH "x\e[74m0\e[73m2\e[m"
+  ?screen_cell 0,8  = {0x78} width=1 attrs={} fg=rgb(240,240,240) bg=rgb(0,0,0)
+  ?screen_cell 0,9  = {0x30} width=1 attrs={S_} fg=rgb(240,240,240) bg=rgb(0,0,0)
+  ?screen_cell 0,10 = {0x32} width=1 attrs={S^} fg=rgb(240,240,240) bg=rgb(0,0,0)
+
 !EL sets reverse and colours to end of line
 PUSH "\e[H\e[7;33;44m\e[K"
   ?screen_cell 0,0  = {} width=1 attrs={R} fg=idx(3) bg=idx(4)
new file mode 100644
--- /dev/null
+++ b/src/libvterm/t/69screen_reflow.test
@@ -0,0 +1,79 @@
+INIT
+# Run these tests on a much smaller default screen, so debug output is
+# nowhere near as noisy
+RESIZE 5,10
+WANTSTATE
+WANTSCREEN r
+RESET
+
+!Resize wider reflows wide lines
+RESET
+PUSH "A"x12
+  ?screen_row 0 = "AAAAAAAAAA"
+  ?screen_row 1 = "AA"
+  ?lineinfo 1 = cont
+  ?cursor = 1,2
+RESIZE 5,15
+  ?screen_row 0 = "AAAAAAAAAAAA"
+  ?screen_row 1 = 
+  ?lineinfo 1 =
+  ?cursor = 0,12
+RESIZE 5,20
+  ?screen_row 0 = "AAAAAAAAAAAA"
+  ?screen_row 1 = 
+  ?lineinfo 1 =
+  ?cursor = 0,12
+
+!Resize narrower can create continuation lines
+RESET
+RESIZE 5,10
+PUSH "ABCDEFGHI"
+  ?screen_row 0 = "ABCDEFGHI"
+  ?screen_row 1 = ""
+  ?lineinfo 1 =
+  ?cursor = 0,9
+RESIZE 5,8
+  ?screen_row 0 = "ABCDEFGH"
+  ?screen_row 1 = "I"
+  ?lineinfo 1 = cont
+  ?cursor = 1,1
+RESIZE 5,6
+  ?screen_row 0 = "ABCDEF"
+  ?screen_row 1 = "GHI"
+  ?lineinfo 1 = cont
+  ?cursor = 1,3
+
+!Shell wrapped prompt behaviour
+RESET
+RESIZE 5,10
+PUSH "PROMPT GOES HERE\r\n> \r\n\r\nPROMPT GOES HERE\r\n> "
+  ?screen_row 0 = "> "
+  ?screen_row 1 = ""
+  ?screen_row 2 = "PROMPT GOE"
+  ?screen_row 3 = "S HERE"
+  ?lineinfo 3 = cont
+  ?screen_row 4 = "> "
+  ?cursor = 4,2
+RESIZE 5,11
+  ?screen_row 0 = "> "
+  ?screen_row 1 = ""
+  ?screen_row 2 = "PROMPT GOES"
+  ?screen_row 3 = " HERE"
+  ?lineinfo 3 = cont
+  ?screen_row 4 = "> "
+  ?cursor = 4,2
+RESIZE 5,12
+  ?screen_row 0 = "> "
+  ?screen_row 1 = ""
+  ?screen_row 2 = "PROMPT GOES "
+  ?screen_row 3 = "HERE"
+  ?lineinfo 3 = cont
+  ?screen_row 4 = "> "
+  ?cursor = 4,2
+RESIZE 5,16
+  ?screen_row 0 = "> "
+  ?screen_row 1 = ""
+  ?screen_row 2 = "PROMPT GOES HERE"
+  ?lineinfo 3 =
+  ?screen_row 3 = "> "
+  ?cursor = 3,2
--- a/src/libvterm/t/harness.c
+++ b/src/libvterm/t/harness.c
@@ -1,6 +1,7 @@
 #include "vterm.h"
 #include "../src/vterm_internal.h" // We pull in some internal bits too
 
+#include <assert.h>
 #include <stdio.h>
 #include <string.h>
 
@@ -409,6 +410,8 @@ static struct {
   int conceal;
   int strike;
   int font;
+  int small;
+  int baseline;
   VTermColor foreground;
   VTermColor background;
 } state_pen;
@@ -439,6 +442,12 @@ static int state_setpenattr(VTermAttr at
   case VTERM_ATTR_FONT:
     state_pen.font = val->number;
     break;
+  case VTERM_ATTR_SMALL:
+    state_pen.small = val->boolean;
+    break;
+  case VTERM_ATTR_BASELINE:
+    state_pen.baseline = val->number;
+    break;
   case VTERM_ATTR_FOREGROUND:
     state_pen.foreground = val->color;
     break;
@@ -458,6 +467,15 @@ static int state_setlineinfo(int row UNU
   return 1;
 }
 
+static int want_state_scrollback = 0;
+static int state_sb_clear(void *user UNUSED) {
+  if(!want_state_scrollback)
+    return 1;
+
+  printf("sb_clear\n");
+  return 0;
+}
+
 VTermStateCallbacks state_cbs = {
   state_putglyph, // putglyph
   movecursor, // movecursor
@@ -470,6 +488,7 @@ VTermStateCallbacks state_cbs = {
   NULL, // bell
   NULL, // resize
   state_setlineinfo, // setlineinfo
+  state_sb_clear, // sb_clear
 };
 
 static int selection_set(VTermSelectionMask mask, VTermStringFragment frag, void *user UNUSED)
@@ -592,6 +611,15 @@ static int screen_sb_popline(int cols, V
   return 1;
 }
 
+static int screen_sb_clear(void *user UNUSED)
+{
+  if(!want_screen_scrollback)
+    return 1;
+
+  printf("sb_clear\n");
+  return 0;
+}
+
 VTermScreenCallbacks screen_cbs = {
   screen_damage, // damage
   moverect, // moverect
@@ -600,7 +628,8 @@ VTermScreenCallbacks screen_cbs = {
   NULL, // bell
   NULL, // resize
   screen_sb_pushline, // sb_pushline
-  screen_sb_popline // sb_popline
+  screen_sb_popline, // sb_popline
+  screen_sb_clear, // sb_clear
 };
 
 int main(int argc UNUSED, char **argv UNUSED)
@@ -629,12 +658,14 @@ int main(int argc UNUSED, char **argv UN
     }
 
     else if(streq(line, "WANTPARSER")) {
+      assert(vt);
       vterm_parser_set_callbacks(vt, &parser_cbs, NULL);
     }
 
     else if(strstartswith(line, "WANTSTATE") && (line[9] == '\0' || line[9] == ' ')) {
       int i = 9;
       int sense = 1;
+      assert(vt);
       if(!state) {
         state = vterm_obtain_state(vt);
         vterm_state_set_callbacks(state, &state_cbs, NULL);
@@ -671,6 +702,9 @@ int main(int argc UNUSED, char **argv UN
         case 'f':
           vterm_state_set_unrecognised_fallbacks(state, sense ? &fallbacks : NULL, NULL);
           break;
+        case 'b':
+          want_state_scrollback = sense;
+          break;
         default:
           fprintf(stderr, "Unrecognised WANTSTATE flag '%c'\n", line[i]);
         }
@@ -679,6 +713,7 @@ int main(int argc UNUSED, char **argv UN
     else if(strstartswith(line, "WANTSCREEN") && (line[10] == '\0' || line[10] == ' ')) {
       int i = 10;
       int sense = 1;
+      assert(vt);
       if(!screen)
         screen = vterm_obtain_screen(vt);
       vterm_screen_set_callbacks(screen, &screen_cbs, NULL);
@@ -712,6 +747,9 @@ int main(int argc UNUSED, char **argv UN
         case 'b':
           want_screen_scrollback = sense;
           break;
+        case 'r':
+          vterm_screen_enable_reflow(screen, sense);
+          break;
         default:
           fprintf(stderr, "Unrecognised WANTSCREEN flag '%c'\n", line[i]);
         }
@@ -743,6 +781,8 @@ int main(int argc UNUSED, char **argv UN
     else if(strstartswith(line, "PUSH ")) {
       char *bytes = line + 5;
       size_t len = inplace_hex2bytes(bytes);
+      assert(len);
+
       size_t written = vterm_input_write(vt, bytes, len);
       if(written < len)
         fprintf(stderr, "! short write\n");
@@ -759,6 +799,7 @@ int main(int argc UNUSED, char **argv UN
     else if(strstartswith(line, "ENCIN ")) {
       char *bytes = line + 6;
       size_t len = inplace_hex2bytes(bytes);
+      assert(len);
 
       uint32_t cp[1024];
       int cpi = 0;
@@ -814,6 +855,7 @@ int main(int argc UNUSED, char **argv UN
     }
 
     else if(strstartswith(line, "FOCUS ")) {
+      assert(state);
       char *linep = line + 6;
       if(streq(linep, "IN"))
         vterm_state_focus_in(state);
@@ -869,6 +911,8 @@ int main(int argc UNUSED, char **argv UN
       }
       frag.len = inplace_hex2bytes(linep);
       frag.str = linep;
+      assert(frag.len);
+
       linep += frag.len * 2;
       while(linep[0] == ' ')
         linep++;
@@ -879,6 +923,7 @@ int main(int argc UNUSED, char **argv UN
     }
 
     else if(strstartswith(line, "DAMAGEMERGE ")) {
+      assert(screen);
       char *linep = line + 12;
       while(linep[0] == ' ')
         linep++;
@@ -893,11 +938,13 @@ int main(int argc UNUSED, char **argv UN
     }
 
     else if(strstartswith(line, "DAMAGEFLUSH")) {
+      assert(screen);
       vterm_screen_flush_damage(screen);
     }
 
     else if(line[0] == '?') {
       if(streq(line, "?cursor")) {
+        assert(state);
         VTermPos pos;
         vterm_state_get_cursorpos(state, &pos);
         if(pos.row != state_pos.row)
@@ -910,6 +957,7 @@ int main(int argc UNUSED, char **argv UN
           printf("%d,%d\n", state_pos.row, state_pos.col);
       }
       else if(strstartswith(line, "?pen ")) {
+        assert(state);
         VTermValue val;
         char *linep = line + 5;
         while(linep[0] == ' ')
@@ -965,6 +1013,24 @@ int main(int argc UNUSED, char **argv UN
           else
             printf("%d\n", state_pen.font);
         }
+        else if(streq(linep, "small")) {
+          vterm_state_get_penattr(state, VTERM_ATTR_SMALL, &val);
+          if(val.boolean != state_pen.small)
+            printf("! pen small mismatch; state=%s, event=%s\n",
+                BOOLSTR(val.boolean), BOOLSTR(state_pen.small));
+          else
+            printf("%s\n", BOOLSTR(state_pen.small));
+        }
+        else if(streq(linep, "baseline")) {
+          vterm_state_get_penattr(state, VTERM_ATTR_BASELINE, &val);
+          if(val.number != state_pen.baseline)
+            printf("! pen baseline mismatch: state=%d, event=%d\n",
+                val.number, state_pen.baseline);
+          else
+            printf("%s\n", state_pen.baseline == VTERM_BASELINE_RAISE ? "raise"
+                         : state_pen.baseline == VTERM_BASELINE_LOWER ? "lower"
+                         : "normal");
+        }
         else if(streq(linep, "foreground")) {
           print_color(&state_pen.foreground);
           printf("\n");
@@ -977,6 +1043,7 @@ int main(int argc UNUSED, char **argv UN
           printf("?\n");
       }
       else if(strstartswith(line, "?lineinfo ")) {
+        assert(state);
         char *linep = line + 10;
         int row;
         const VTermLineInfo *info;
@@ -996,6 +1063,7 @@ int main(int argc UNUSED, char **argv UN
         printf("\n");
       }
       else if(strstartswith(line, "?screen_chars ")) {
+        assert(screen);
         char *linep = line + 13;
         VTermRect rect;
         size_t len;
@@ -1028,6 +1096,7 @@ int main(int argc UNUSED, char **argv UN
         }
       }
       else if(strstartswith(line, "?screen_text ")) {
+        assert(screen);
         char *linep = line + 12;
         VTermRect rect;
         size_t len;
@@ -1067,6 +1136,7 @@ int main(int argc UNUSED, char **argv UN
         }
       }
       else if(strstartswith(line, "?screen_cell ")) {
+        assert(screen);
         char *linep = line + 12;
 	int i;
         VTermPos pos;
@@ -1090,6 +1160,10 @@ int main(int argc UNUSED, char **argv UN
         if(cell.attrs.blink)     printf("K");
         if(cell.attrs.reverse)   printf("R");
         if(cell.attrs.font)      printf("F%d", cell.attrs.font);
+        if(cell.attrs.small)     printf("S");
+        if(cell.attrs.baseline)  printf(
+            cell.attrs.baseline == VTERM_BASELINE_RAISE ? "^" :
+                                                          "_");
         printf("} ");
         if(cell.attrs.dwl)       printf("dwl ");
         if(cell.attrs.dhl)       printf("dhl-%s ", cell.attrs.dhl == 2 ? "bottom" : "top");
@@ -1102,6 +1176,7 @@ int main(int argc UNUSED, char **argv UN
         printf("\n");
       }
       else if(strstartswith(line, "?screen_eol ")) {
+        assert(screen);
         VTermPos pos;
         char *linep = line + 12;
         while(linep[0] == ' ')
@@ -1113,6 +1188,7 @@ int main(int argc UNUSED, char **argv UN
         printf("%d\n", vterm_screen_is_eol(screen, pos));
       }
       else if(strstartswith(line, "?screen_attrs_extent ")) {
+        assert(screen);
         VTermPos pos;
         VTermRect rect;
         char *linep = line + 21;
--- a/src/libvterm/t/run-test.pl
+++ b/src/libvterm/t/run-test.pl
@@ -124,7 +124,7 @@ sub do_line
       elsif( $line =~ m/^putglyph (\S+) (.*)$/ ) {
          $line = "putglyph " . join( ",", map sprintf("%x", $_), eval($1) ) . " $2";
       }
-      elsif( $line =~ m/^(?:movecursor|scrollrect|moverect|erase|damage|sb_pushline|sb_popline|settermprop|setmousefunc|selection-query) / ) {
+      elsif( $line =~ m/^(?:movecursor|scrollrect|moverect|erase|damage|sb_pushline|sb_popline|sb_clear|settermprop|setmousefunc|selection-query) ?/ ) {
          # no conversion
       }
       elsif( $line =~ m/^(selection-set) (.*?) (\[?)(.*?)(\]?)$/ ) {
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -3447,7 +3447,8 @@ static VTermScreenCallbacks screen_callb
   handle_bell,		// bell
   handle_resize,	// resize
   handle_pushline,	// sb_pushline
-  NULL			// sb_popline
+  NULL,			// sb_popline
+  NULL			// sb_clear
 };
 
 /*
--- a/src/version.c
+++ b/src/version.c
@@ -696,6 +696,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    774,
+/**/
     773,
 /**/
     772,