summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2022-10-16 19:26:52 +0100
committerBram Moolenaar <Bram@vim.org>2022-10-16 19:26:52 +0100
commit6a12d26f3404e45ce25cf9152857e355b28f392a (patch)
tree7866a4154ea9964e8026ac282d9407e20c4df861
parentd094e580b0873d67c2d30f60c9fd43c9a0044149 (diff)
patch 9.0.0774: the libvterm code is outdatedv9.0.0774
Problem: The libvterm code is outdated. Solution: Include libvterm changes from revision 802 to 817. Revert some changes made for C89.
-rw-r--r--src/libvterm/CONTRIBUTING4
-rw-r--r--src/libvterm/Makefile2
-rw-r--r--src/libvterm/README7
-rw-r--r--src/libvterm/doc/seqs.txt4
-rw-r--r--src/libvterm/include/vterm.h36
-rw-r--r--src/libvterm/src/encoding.c4
-rw-r--r--src/libvterm/src/keyboard.c32
-rw-r--r--src/libvterm/src/parser.c5
-rw-r--r--src/libvterm/src/pen.c57
-rw-r--r--src/libvterm/src/screen.c322
-rw-r--r--src/libvterm/src/state.c85
-rw-r--r--src/libvterm/src/unicode.c142
-rw-r--r--src/libvterm/src/vterm.c12
-rw-r--r--src/libvterm/src/vterm_internal.h2
-rw-r--r--src/libvterm/t/10state_putglyph.test6
-rw-r--r--src/libvterm/t/13state_edit.test6
-rw-r--r--src/libvterm/t/26state_query.test2
-rw-r--r--src/libvterm/t/30state_pen.test11
-rw-r--r--src/libvterm/t/64screen_pen.test6
-rw-r--r--src/libvterm/t/69screen_reflow.test79
-rw-r--r--src/libvterm/t/harness.c78
-rw-r--r--src/libvterm/t/run-test.pl2
-rw-r--r--src/terminal.c3
-rw-r--r--src/version.c2
24 files changed, 655 insertions, 254 deletions
diff --git a/src/libvterm/CONTRIBUTING b/src/libvterm/CONTRIBUTING
index 2100d1e510..e9a8f0c331 100644
--- 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>
diff --git a/src/libvterm/Makefile b/src/libvterm/Makefile
index 2f83e0d68c..ba4a3157ed 100644
--- 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
diff --git a/src/libvterm/README b/src/libvterm/README
index 8e9341d070..264128445d 100644
--- 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:
diff --git a/src/libvterm/doc/seqs.txt b/src/libvterm/doc/seqs.txt
index 02a9fe51f6..27f28b91b6 100644
--- a/src/libvterm/doc/seqs.txt
+++ b/src/libvterm/doc/seqs.txt
@@ -122,6 +122,7 @@ x = xterm
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 @@ x = xterm
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
diff --git a/src/libvterm/include/vterm.h b/src/libvterm/include/vterm.h
index 21295a62a9..e5887c8714 100644
--- 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 VTermBuilder *builder);
/* 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 button, int pressed, VTermModifier mod);
#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_fbdata(VTermState *state);
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(VTermScreen *screen);
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);
diff --git a/src/libvterm/src/encoding.c b/src/libvterm/src/encoding.c
index 694ed6a0ca..817344f333 100644
--- a/src/libvterm/src/encoding.c
+++ b/src/libvterm/src/encoding.c
@@ -49,6 +49,7 @@ static void decode_utf8(VTermEncoding *enc UNUSED, void *data_,
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;
diff --git a/src/libvterm/src/keyboard.c b/src/libvterm/src/keyboard.c
index 87288fe970..c04984abab 100644
--- 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, uint32_t c, VTermModifier mod)
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;
diff --git a/src/libvterm/src/parser.c b/src/libvterm/src/parser.c
index 0d4422cf49..d4cd0dc477 100644
--- a/src/libvterm/src/parser.c
+++ b/src/libvterm/src/parser.c
@@ -142,7 +142,6 @@ size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len)
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, const char *bytes, size_t len)
}
// 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, const char *bytes, size_t len)
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)
diff --git a/src/libvterm/src/pen.c b/src/libvterm/src/pen.c
index c5a0b71c5d..1c6cd4e488 100644
--- 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 *state, VTermAttr attr, long col)
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(VTermState *state, int save)
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(VTermState *state, const long args[], int argco
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(VTermState *state, long args[], int argcount UNU
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 VTermState *state, VTermAttr attr, VTermValue
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;
}
diff --git a/src/libvterm/src/screen.c b/src/libvterm/src/screen.c
index 25ddb13991..069306ab95 100644
--- 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 VTermScreen *screen, int row, int col)
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 *screen)
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, VTermPos pos, void *user)
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, VTermPos pos, void *user)
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,34 +227,30 @@ static int moverect_internal(VTermRect dest, VTermRect src, void *user)
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;
-
- 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 cols = src.end_col - src.start_col;
+ int downward = src.start_row - dest.start_row;
- 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));
+ 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(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, VTermRect src, void *user)
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, VTermValue *val, void *user)
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 *screen, int bufidx, int new_rows, int new
// Find the final row of old buffer content
int old_row = old_rows - 1;
int new_row = new_rows - 1;
- int col;
- 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]);
+ VTermPos old_cursor = statefields->pos;
+ VTermPos new_cursor = { -1, -1 };
- new_lineinfo[new_row] = old_lineinfo[old_row];
+#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
- old_row--;
- new_row--;
+ /* 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;
+
+ 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;
+
+ 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_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));
+ if(new_cursor.row >= 0)
+ new_cursor.row += downwards;
- 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;
+ }
+
+ 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_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 *screen, int bufidx, int new_rows, int new
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];