summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2013-04-23 10:34:06 -0700
committerJeff Sharkey <jsharkey@android.com>2013-04-23 17:02:30 -0700
commit73fbfc38792bd96137d5b6ae3016dfc4d9805d46 (patch)
tree4cbe1d51a179c7e6ff76a6e09ee1ceabb5f8810a
parent5b78a3aa7741c3f44b676ccffa765cecee1cbd4c (diff)
downloadlibvterm-73fbfc38792bd96137d5b6ae3016dfc4d9805d46.tar.gz
Has updated scrollback API and bugfixes. Change-Id: I92c097393d57f3ae04dfddb745c618c145e8ff1a
-rw-r--r--include/vterm.h204
-rw-r--r--src/encoding.c2
-rw-r--r--src/pen.c88
-rw-r--r--src/screen.c203
-rw-r--r--src/state.c45
-rw-r--r--src/vterm.c16
-rw-r--r--src/vterm_internal.h12
7 files changed, 412 insertions, 158 deletions
diff --git a/include/vterm.h b/include/vterm.h
index 68e4feb..2e2b32b 100644
--- a/include/vterm.h
+++ b/include/vterm.h
@@ -48,37 +48,6 @@ static inline void vterm_rect_move(VTermRect *rect, int row_delta, int col_delta
rect->start_col += col_delta; rect->end_col += col_delta;
}
-/* Flag to indicate non-final subparameters in a single CSI parameter.
- * Consider
- * CSI 1;2:3:4;5a
- * 1 4 and 5 are final.
- * 2 and 3 are non-final and will have this bit set
- *
- * Don't confuse this with the final byte of the CSI escape; 'a' in this case.
- */
-#define CSI_ARG_FLAG_MORE (1<<31)
-#define CSI_ARG_MASK (~(1<<31))
-
-#define CSI_ARG_HAS_MORE(a) ((a) & CSI_ARG_FLAG_MORE)
-#define CSI_ARG(a) ((a) & CSI_ARG_MASK)
-
-/* Can't use -1 to indicate a missing argument; use this instead */
-#define CSI_ARG_MISSING ((1UL<<31)-1)
-
-#define CSI_ARG_IS_MISSING(a) (CSI_ARG(a) == CSI_ARG_MISSING)
-#define CSI_ARG_OR(a,def) (CSI_ARG(a) == CSI_ARG_MISSING ? (def) : CSI_ARG(a))
-#define CSI_ARG_COUNT(a) (CSI_ARG(a) == CSI_ARG_MISSING || CSI_ARG(a) == 0 ? 1 : CSI_ARG(a))
-
-typedef struct {
- int (*text)(const char *bytes, size_t len, void *user);
- int (*control)(unsigned char control, void *user);
- int (*escape)(const char *bytes, size_t len, void *user);
- int (*csi)(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user);
- int (*osc)(const char *command, size_t cmdlen, void *user);
- int (*dcs)(const char *command, size_t cmdlen, void *user);
- int (*resize)(int rows, int cols, void *user);
-} VTermParserCallbacks;
-
typedef struct {
uint8_t red, green, blue;
} VTermColor;
@@ -133,35 +102,10 @@ typedef void (*VTermMouseFunc)(int x, int y, int button, int pressed, int modifi
typedef struct {
const uint32_t *chars;
int width;
- int protected_cell; /* DECSCA-protected against DECSEL/DECSED */
+ unsigned int protected_cell:1; /* DECSCA-protected against DECSEL/DECSED */
} VTermGlyphInfo;
typedef struct {
- int (*putglyph)(VTermGlyphInfo *info, VTermPos pos, void *user);
- int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
- int (*scrollrect)(VTermRect rect, int downward, int rightward, void *user);
- int (*moverect)(VTermRect dest, VTermRect src, void *user);
- int (*erase)(VTermRect rect, int selective, void *user);
- int (*initpen)(void *user);
- int (*setpenattr)(VTermAttr attr, VTermValue *val, void *user);
- int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
- int (*setmousefunc)(VTermMouseFunc func, void *data, void *user);
- int (*bell)(void *user);
- int (*resize)(int rows, int cols, void *user);
-} VTermStateCallbacks;
-
-typedef struct {
- int (*damage)(VTermRect rect, void *user);
- int (*prescroll)(VTermRect rect, void *user);
- int (*moverect)(VTermRect dest, VTermRect src, void *user);
- int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
- int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
- int (*setmousefunc)(VTermMouseFunc func, void *data, void *user);
- int (*bell)(void *user);
- int (*resize)(int rows, int cols, void *user);
-} VTermScreenCallbacks;
-
-typedef struct {
/* libvterm relies on this memory to be zeroed out before it is returned
* by the allocator. */
void *(*malloc)(size_t size, void *allocdata);
@@ -175,8 +119,76 @@ void vterm_free(VTerm* vt);
void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp);
void vterm_set_size(VTerm *vt, int rows, int cols);
+void vterm_push_bytes(VTerm *vt, const char *bytes, size_t len);
+
+void vterm_input_push_char(VTerm *vt, VTermModifier state, uint32_t c);
+void vterm_input_push_key(VTerm *vt, VTermModifier state, VTermKey key);
+
+size_t vterm_output_bufferlen(VTerm *vt); /* deprecated */
+
+size_t vterm_output_get_buffer_size(const VTerm *vt);
+size_t vterm_output_get_buffer_current(const VTerm *vt);
+size_t vterm_output_get_buffer_remaining(const VTerm *vt);
+
+size_t vterm_output_bufferread(VTerm *vt, char *buffer, size_t len);
+
+// ------------
+// Parser layer
+// ------------
+
+/* Flag to indicate non-final subparameters in a single CSI parameter.
+ * Consider
+ * CSI 1;2:3:4;5a
+ * 1 4 and 5 are final.
+ * 2 and 3 are non-final and will have this bit set
+ *
+ * Don't confuse this with the final byte of the CSI escape; 'a' in this case.
+ */
+#define CSI_ARG_FLAG_MORE (1<<31)
+#define CSI_ARG_MASK (~(1<<31))
+
+#define CSI_ARG_HAS_MORE(a) ((a) & CSI_ARG_FLAG_MORE)
+#define CSI_ARG(a) ((a) & CSI_ARG_MASK)
+
+/* Can't use -1 to indicate a missing argument; use this instead */
+#define CSI_ARG_MISSING ((1UL<<31)-1)
+
+#define CSI_ARG_IS_MISSING(a) (CSI_ARG(a) == CSI_ARG_MISSING)
+#define CSI_ARG_OR(a,def) (CSI_ARG(a) == CSI_ARG_MISSING ? (def) : CSI_ARG(a))
+#define CSI_ARG_COUNT(a) (CSI_ARG(a) == CSI_ARG_MISSING || CSI_ARG(a) == 0 ? 1 : CSI_ARG(a))
+
+typedef struct {
+ int (*text)(const char *bytes, size_t len, void *user);
+ int (*control)(unsigned char control, void *user);
+ int (*escape)(const char *bytes, size_t len, void *user);
+ int (*csi)(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user);
+ int (*osc)(const char *command, size_t cmdlen, void *user);
+ int (*dcs)(const char *command, size_t cmdlen, void *user);
+ int (*resize)(int rows, int cols, void *user);
+} VTermParserCallbacks;
+
void vterm_set_parser_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user);
+void vterm_parser_set_utf8(VTerm *vt, int is_utf8);
+
+// -----------
+// State layer
+// -----------
+
+typedef struct {
+ int (*putglyph)(VTermGlyphInfo *info, VTermPos pos, void *user);
+ int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
+ int (*scrollrect)(VTermRect rect, int downward, int rightward, void *user);
+ int (*moverect)(VTermRect dest, VTermRect src, void *user);
+ int (*erase)(VTermRect rect, int selective, void *user);
+ int (*initpen)(void *user);
+ int (*setpenattr)(VTermAttr attr, VTermValue *val, void *user);
+ int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
+ int (*setmousefunc)(VTermMouseFunc func, void *data, void *user);
+ int (*bell)(void *user);
+ int (*resize)(int rows, int cols, VTermPos *delta, void *user);
+} VTermStateCallbacks;
+
VTermState *vterm_obtain_state(VTerm *vt);
void vterm_state_reset(VTermState *state, int hard);
@@ -189,8 +201,37 @@ void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright);
int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val);
int vterm_state_set_termprop(VTermState *state, VTermProp prop, VTermValue *val);
-VTermValueType vterm_get_attr_type(VTermAttr attr);
-VTermValueType vterm_get_prop_type(VTermProp prop);
+// ------------
+// Screen layer
+// ------------
+
+typedef struct {
+#define VTERM_MAX_CHARS_PER_CELL 6
+ uint32_t chars[VTERM_MAX_CHARS_PER_CELL];
+ char width;
+ struct {
+ unsigned int bold : 1;
+ unsigned int underline : 2;
+ unsigned int italic : 1;
+ unsigned int blink : 1;
+ unsigned int reverse : 1;
+ unsigned int strike : 1;
+ unsigned int font : 4; /* 0 to 9 */
+ } attrs;
+ VTermColor fg, bg;
+} VTermScreenCell;
+
+typedef struct {
+ int (*damage)(VTermRect rect, void *user);
+ int (*moverect)(VTermRect dest, VTermRect src, void *user);
+ int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
+ int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
+ int (*setmousefunc)(VTermMouseFunc func, void *data, void *user);
+ int (*bell)(void *user);
+ int (*resize)(int rows, int cols, void *user);
+ int (*sb_pushline)(int cols, const VTermScreenCell *cells, void *user);
+ int (*sb_popline)(int cols, VTermScreenCell *cells, void *user);
+} VTermScreenCallbacks;
VTermScreen *vterm_obtain_screen(VTerm *vt);
@@ -211,39 +252,30 @@ void vterm_screen_reset(VTermScreen *screen, int hard);
size_t vterm_screen_get_chars(const VTermScreen *screen, uint32_t *chars, size_t len, const VTermRect rect);
size_t vterm_screen_get_text(const VTermScreen *screen, char *str, size_t len, const VTermRect rect);
-typedef struct {
-#define VTERM_MAX_CHARS_PER_CELL 6
- uint32_t chars[VTERM_MAX_CHARS_PER_CELL];
- char width;
- struct {
- unsigned int bold : 1;
- unsigned int underline : 2;
- unsigned int italic : 1;
- unsigned int blink : 1;
- unsigned int reverse : 1;
- unsigned int strike : 1;
- unsigned int font : 4; /* 0 to 9 */
- } attrs;
- VTermColor fg, bg;
-} VTermScreenCell;
+typedef enum {
+ VTERM_ATTR_BOLD_MASK = 1 << 0,
+ VTERM_ATTR_UNDERLINE_MASK = 1 << 1,
+ VTERM_ATTR_ITALIC_MASK = 1 << 2,
+ VTERM_ATTR_BLINK_MASK = 1 << 3,
+ VTERM_ATTR_REVERSE_MASK = 1 << 4,
+ VTERM_ATTR_STRIKE_MASK = 1 << 5,
+ VTERM_ATTR_FONT_MASK = 1 << 6,
+ VTERM_ATTR_FOREGROUND_MASK = 1 << 7,
+ VTERM_ATTR_BACKGROUND_MASK = 1 << 8,
+} VTermAttrMask;
+
+int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs);
int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCell *cell);
int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos);
-void vterm_input_push_char(VTerm *vt, VTermModifier state, uint32_t c);
-void vterm_input_push_key(VTerm *vt, VTermModifier state, VTermKey key);
+// ---------
+// Utilities
+// ---------
-void vterm_parser_set_utf8(VTerm *vt, int is_utf8);
-void vterm_push_bytes(VTerm *vt, const char *bytes, size_t len);
-
-size_t vterm_output_bufferlen(VTerm *vt); /* deprecated */
-
-size_t vterm_output_get_buffer_size(const VTerm *vt);
-size_t vterm_output_get_buffer_current(const VTerm *vt);
-size_t vterm_output_get_buffer_remaining(const VTerm *vt);
-
-size_t vterm_output_bufferread(VTerm *vt, char *buffer, size_t len);
+VTermValueType vterm_get_attr_type(VTermAttr attr);
+VTermValueType vterm_get_prop_type(VTermProp prop);
void vterm_scroll_rect(VTermRect rect,
int downward,
diff --git a/src/encoding.c b/src/encoding.c
index 130cfff..a7629f9 100644
--- a/src/encoding.c
+++ b/src/encoding.c
@@ -209,7 +209,7 @@ encodings[] = {
{ ENC_SINGLE_94, '0', (VTermEncoding*)&encoding_DECdrawing },
{ ENC_SINGLE_94, 'A', (VTermEncoding*)&encoding_uk },
{ ENC_SINGLE_94, 'B', &encoding_usascii },
- { 0, 0 },
+ { 0 },
};
VTermEncoding *vterm_lookup_encoding(VTermEncodingType type, char designation)
diff --git a/src/pen.c b/src/pen.c
index eed63c0..044e9aa 100644
--- a/src/pen.c
+++ b/src/pen.c
@@ -64,7 +64,7 @@ static void lookup_colour_palette(long index, VTermColor *col)
}
}
-static int lookup_colour(int palette, const long args[], int argcount, VTermColor *col)
+static int lookup_colour(int palette, const long args[], int argcount, VTermColor *col, int *index)
{
switch(palette) {
case 2: // RGB mode - 3 args contain colour values directly
@@ -78,6 +78,9 @@ static int lookup_colour(int palette, const long args[], int argcount, VTermColo
return 3;
case 5: // XTerm 256-colour mode
+ if(index)
+ *index = CSI_ARG_OR(args[0], -1);
+
lookup_colour_palette(argcount ? CSI_ARG_OR(args[0], -1) : -1, col);
return argcount ? 1 : 0;
@@ -140,7 +143,8 @@ void vterm_state_resetpen(VTermState *state)
state->pen.strike = 0; setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
state->pen.font = 0; setpenattr_int( state, VTERM_ATTR_FONT, 0);
- state->fg_ansi = -1;
+ state->fg_index = -1;
+ state->bg_index = -1;
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);
}
@@ -209,8 +213,8 @@ void vterm_state_setpen(VTermState *state, const long args[], int argcount)
case 1: // Bold on
state->pen.bold = 1;
setpenattr_bool(state, VTERM_ATTR_BOLD, 1);
- if(state->fg_ansi > -1 && state->bold_is_highbright)
- set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, state->fg_ansi + (state->pen.bold ? 8 : 0));
+ if(state->fg_index > -1 && state->fg_index < 8 && state->bold_is_highbright)
+ set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, state->fg_index + (state->pen.bold ? 8 : 0));
break;
case 3: // Italic on
@@ -282,52 +286,59 @@ void vterm_state_setpen(VTermState *state, const long args[], int argcount)
case 30: case 31: case 32: case 33:
case 34: case 35: case 36: case 37: // Foreground colour palette
value = CSI_ARG(args[argi]) - 30;
- state->fg_ansi = value;
+ state->fg_index = value;
if(state->pen.bold && state->bold_is_highbright)
value += 8;
set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
break;
case 38: // Foreground colour alternative palette
- state->fg_ansi = -1;
+ state->fg_index = -1;
if(argcount - argi < 1)
return;
- argi += 1 + lookup_colour(CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.fg);
+ argi += 1 + lookup_colour(CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.fg, &state->fg_index);
setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
break;
case 39: // Foreground colour default
- state->fg_ansi = -1;
+ state->fg_index = -1;
state->pen.fg = state->default_fg;
setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
break;
case 40: case 41: case 42: case 43:
case 44: case 45: case 46: case 47: // Background colour palette
- set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, CSI_ARG(args[argi]) - 40);
+ value = CSI_ARG(args[argi]) - 40;
+ state->bg_index = value;
+ set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
break;
case 48: // Background colour alternative palette
+ state->bg_index = -1;
if(argcount - argi < 1)
return;
- argi += 1 + lookup_colour(CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.bg);
+ argi += 1 + lookup_colour(CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.bg, &state->bg_index);
setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
break;
case 49: // Default background
+ state->bg_index = -1;
state->pen.bg = state->default_bg;
setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
break;
case 90: case 91: case 92: case 93:
case 94: case 95: case 96: case 97: // Foreground colour high-intensity palette
- state->fg_ansi = -1;
- set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, CSI_ARG(args[argi]) - 90 + 8);
+ value = CSI_ARG(args[argi]) - 90 + 8;
+ state->fg_index = value;
+ set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
break;
case 100: case 101: case 102: case 103:
case 104: case 105: case 106: case 107: // Background colour high-intensity palette
- set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, CSI_ARG(args[argi]) - 100 + 8);
+ value = CSI_ARG(args[argi]) - 100 + 8;
+ state->bg_index = value;
+ set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
break;
default:
@@ -342,6 +353,57 @@ void vterm_state_setpen(VTermState *state, const long args[], int argcount)
}
}
+int vterm_state_getpen(VTermState *state, long args[], int argcount)
+{
+ int argi = 0;
+
+ if(state->pen.bold)
+ args[argi++] = 1;
+
+ if(state->pen.italic)
+ args[argi++] = 3;
+
+ if(state->pen.underline == 1)
+ args[argi++] = 4;
+
+ if(state->pen.blink)
+ args[argi++] = 5;
+
+ if(state->pen.reverse)
+ args[argi++] = 7;
+
+ if(state->pen.strike)
+ args[argi++] = 9;
+
+ if(state->pen.font)
+ args[argi++] = 10 + state->pen.font;
+
+ if(state->pen.underline == 2)
+ args[argi++] = 21;
+
+ if(state->fg_index >= 0 && state->fg_index < 8)
+ args[argi++] = 30 + state->fg_index;
+ else if(state->fg_index >= 8 && state->fg_index < 16)
+ args[argi++] = 90 + state->fg_index - 8;
+ else if(state->fg_index >= 16 && state->fg_index < 256) {
+ args[argi++] = CSI_ARG_FLAG_MORE|38;
+ args[argi++] = CSI_ARG_FLAG_MORE|5;
+ args[argi++] = state->fg_index;
+ }
+
+ if(state->bg_index >= 0 && state->bg_index < 8)
+ args[argi++] = 40 + state->bg_index;
+ else if(state->bg_index >= 8 && state->bg_index < 16)
+ args[argi++] = 100 + state->bg_index - 8;
+ else if(state->bg_index >= 16 && state->bg_index < 256) {
+ args[argi++] = CSI_ARG_FLAG_MORE|48;
+ args[argi++] = CSI_ARG_FLAG_MORE|5;
+ args[argi++] = state->bg_index;
+ }
+
+ return argi;
+}
+
int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val)
{
switch(attr) {
diff --git a/src/screen.c b/src/screen.c
index 64bedf2..db11c6d 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -34,6 +34,8 @@ typedef struct
ScreenPen pen;
} ScreenCell;
+static int vterm_screen_set_cell(VTermScreen *screen, VTermPos pos, const VTermScreenCell *cell);
+
struct VTermScreen
{
VTerm *vt;
@@ -58,6 +60,9 @@ struct VTermScreen
/* buffer will == buffers[0] or buffers[1], depending on altscreen */
ScreenCell *buffer;
+ /* buffer for a single screen row used in scrollback storage callbacks */
+ VTermScreenCell *sb_buffer;
+
ScreenPen pen;
};
@@ -199,45 +204,16 @@ static int moverect_internal(VTermRect dest, VTermRect src, void *user)
{
VTermScreen *screen = user;
- if(screen->callbacks && screen->callbacks->prescroll) {
- // TODO: These calculations don't properly take account of combined
- // horizontal and vertical movements
- if(dest.start_row < src.start_row) {
- VTermRect rect = {
- .start_row = dest.start_row,
- .end_row = src.start_row,
- .start_col = dest.start_col,
- .end_col = dest.end_col,
- };
- (*screen->callbacks->prescroll)(rect, screen->cbdata);
- }
- else if(dest.start_row > src.start_row) {
- VTermRect rect = {
- .start_row = src.end_row,
- .end_row = dest.end_row,
- .start_col = dest.start_col,
- .end_col = dest.end_col,
- };
- (*screen->callbacks->prescroll)(rect, screen->cbdata);
- }
+ if(screen->callbacks && screen->callbacks->sb_pushline &&
+ dest.start_row == 0 && dest.start_col == 0 && // starts top-left corner
+ dest.end_col == screen->cols && // full width
+ screen->buffer == screen->buffers[0]) { // not altscreen
+ VTermPos pos;
+ for(pos.row = 0; pos.row < src.start_row; pos.row++) {
+ for(pos.col = 0; pos.col < screen->cols; pos.col++)
+ vterm_screen_get_cell(screen, pos, screen->sb_buffer + pos.col);
- if(dest.start_col < src.start_col) {
- VTermRect rect = {
- .start_row = dest.start_row,
- .end_row = dest.end_row,
- .start_col = dest.start_col,
- .end_col = src.start_col,
- };
- (*screen->callbacks->prescroll)(rect, screen->cbdata);
- }
- else if(dest.start_col > src.start_col) {
- VTermRect rect = {
- .start_row = dest.start_row,
- .end_row = dest.end_row,
- .start_col = src.end_col,
- .end_col = dest.end_col,
- };
- (*screen->callbacks->prescroll)(rect, screen->cbdata);
+ (screen->callbacks->sb_pushline)(screen->cols, screen->sb_buffer, screen->cbdata);
}
}
@@ -492,24 +468,52 @@ static int bell(void *user)
return 0;
}
-static int resize(int new_rows, int new_cols, void *user)
+static int resize(int new_rows, int new_cols, VTermPos *delta, void *user)
{
VTermScreen *screen = user;
int is_altscreen = (screen->buffers[1] && screen->buffer == screen->buffers[1]);
+ int old_rows = screen->rows;
+ int old_cols = screen->cols;
+
+ if(!is_altscreen && new_rows < old_rows) {
+ // Fewer rows - determine if we're going to scroll at all, and if so, push
+ // those lines to scrollback
+ VTermPos pos = { 0, 0 };
+ for(pos.row = old_rows - 1; pos.row >= new_rows; pos.row--)
+ if(!vterm_screen_is_eol(screen, pos))
+ break;
+
+ int first_blank_row = pos.row + 1;
+ if(first_blank_row > new_rows) {
+ VTermRect rect = {
+ .start_row = 0,
+ .end_row = old_rows,
+ .start_col = 0,
+ .end_col = old_cols,
+ };
+ scrollrect(rect, first_blank_row - new_rows, 0, user);
+ vterm_screen_flush_damage(screen);
+
+ delta->row -= first_blank_row - new_rows;
+ }
+ }
+
screen->buffers[0] = realloc_buffer(screen, screen->buffers[0], new_rows, new_cols);
if(screen->buffers[1])
screen->buffers[1] = realloc_buffer(screen, screen->buffers[1], new_rows, new_cols);
screen->buffer = is_altscreen ? screen->buffers[1] : screen->buffers[0];
- int old_rows = screen->rows;
- int old_cols = screen->cols;
-
screen->rows = new_rows;
screen->cols = new_cols;
+ if(screen->sb_buffer)
+ vterm_allocator_free(screen->vt, screen->sb_buffer);
+
+ screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * new_cols);
+
if(new_cols > old_cols) {
VTermRect rect = {
.start_row = 0,
@@ -521,6 +525,34 @@ static int resize(int new_rows, int new_cols, void *user)
}
if(new_rows > old_rows) {
+ if(!is_altscreen && screen->callbacks && screen->callbacks->sb_popline) {
+ int rows = new_rows - old_rows;
+ while(rows) {
+ if(!(screen->callbacks->sb_popline(screen->cols, screen->sb_buffer, screen->cbdata)))
+ break;
+
+ VTermRect rect = {
+ .start_row = 0,
+ .end_row = screen->rows,
+ .start_col = 0,
+ .end_col = screen->cols,
+ };
+ scrollrect(rect, -1, 0, user);
+
+ VTermPos pos = { 0, 0 };
+ for(pos.col = 0; pos.col < screen->cols; pos.col += screen->sb_buffer[pos.col].width)
+ vterm_screen_set_cell(screen, pos, screen->sb_buffer + pos.col);
+
+ rect.end_row = 1;
+ damagerect(screen, rect);
+
+ vterm_screen_flush_damage(screen);
+
+ rows--;
+ delta->row++;
+ }
+ }
+
VTermRect rect = {
.start_row = old_rows,
.end_row = new_rows,
@@ -573,6 +605,8 @@ static VTermScreen *screen_new(VTerm *vt)
screen->buffer = screen->buffers[0];
+ screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * cols);
+
vterm_state_set_callbacks(screen->state, &state_cbs, screen);
return screen;
@@ -584,6 +618,8 @@ void vterm_screen_free(VTermScreen *screen)
if(screen->buffers[1])
vterm_allocator_free(screen->vt, screen->buffers[1]);
+ vterm_allocator_free(screen->vt, screen->sb_buffer);
+
vterm_allocator_free(screen->vt, screen);
}
@@ -688,6 +724,37 @@ int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCe
return 1;
}
+/* Copy external to internal representation of a screen cell */
+/* static because it's only used internally for sb_popline during resize */
+static int vterm_screen_set_cell(VTermScreen *screen, VTermPos pos, const VTermScreenCell *cell)
+{
+ ScreenCell *intcell = getcell(screen, pos.row, pos.col);
+ if(!intcell)
+ return 0;
+
+ for(int i = 0; ; i++) {
+ intcell->chars[i] = cell->chars[i];
+ if(!cell->chars[i])
+ break;
+ }
+
+ intcell->pen.bold = cell->attrs.bold;
+ intcell->pen.underline = cell->attrs.underline;
+ intcell->pen.italic = cell->attrs.italic;
+ intcell->pen.blink = cell->attrs.blink;
+ intcell->pen.reverse = cell->attrs.reverse ^ screen->global_reverse;
+ intcell->pen.strike = cell->attrs.strike;
+ intcell->pen.font = cell->attrs.font;
+
+ intcell->pen.fg = cell->fg;
+ intcell->pen.bg = cell->bg;
+
+ if(cell->width == 2)
+ getcell(screen, pos.row, pos.col + 1)->chars[0] = (uint32_t)-1;
+
+ return 1;
+}
+
int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos)
{
/* This cell is EOL if this and every cell to the right is black */
@@ -750,3 +817,55 @@ void vterm_screen_set_damage_merge(VTermScreen *screen, VTermDamageSize size)
vterm_screen_flush_damage(screen);
screen->damage_merge = size;
}
+
+static int attrs_differ(VTermAttrMask attrs, ScreenCell *a, ScreenCell *b)
+{
+ if((attrs & VTERM_ATTR_BOLD_MASK) && (a->pen.bold != b->pen.bold))
+ return 1;
+ if((attrs & VTERM_ATTR_UNDERLINE_MASK) && (a->pen.underline != b->pen.underline))
+ return 1;
+ if((attrs & VTERM_ATTR_ITALIC_MASK) && (a->pen.italic != b->pen.italic))
+ return 1;
+ if((attrs & VTERM_ATTR_BLINK_MASK) && (a->pen.blink != b->pen.blink))
+ return 1;
+ if((attrs & VTERM_ATTR_REVERSE_MASK) && (a->pen.reverse != b->pen.reverse))
+ return 1;
+ if((attrs & VTERM_ATTR_STRIKE_MASK) && (a->pen.strike != b->pen.strike))
+ return 1;
+ if((attrs & VTERM_ATTR_FONT_MASK) && (a->pen.font != b->pen.font))
+ return 1;
+ if((attrs & VTERM_ATTR_FOREGROUND_MASK) && !vterm_color_equal(a->pen.fg, b->pen.fg))
+ return 1;
+ if((attrs & VTERM_ATTR_BACKGROUND_MASK) && !vterm_color_equal(a->pen.bg, b->pen.bg))
+ return 1;
+
+ return 0;
+}
+
+int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs)
+{
+ ScreenCell *target = getcell(screen, pos.row, pos.col);
+
+ // TODO: bounds check
+ extent->start_row = pos.row;
+ extent->end_row = pos.row + 1;
+
+ if(extent->start_col < 0)
+ extent->start_col = 0;
+ 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;
+ extent->start_col = col + 1;
+
+ for(col = pos.col + 1; col < extent->end_col; col++)
+ if(attrs_differ(attrs, target, getcell(screen, pos.row, col)))
+ break;
+ extent->end_col = col - 1;
+
+ return 1;
+}
diff --git a/src/state.c b/src/state.c
index 26d5262..f5348c9 100644
--- a/src/state.c
+++ b/src/state.c
@@ -72,6 +72,7 @@ static VTermState *vterm_state_new(VTerm *vt)
void vterm_state_free(VTermState *state)
{
+ vterm_allocator_free(state->vt, state->tabstops);
vterm_allocator_free(state->vt, state->combine_chars);
vterm_allocator_free(state->vt, state);
}
@@ -242,7 +243,7 @@ static int on_text(const char bytes[], size_t len, void *user)
printf("}, onscreen width %d\n", width);
#endif
- if(state->at_phantom) {
+ if(state->at_phantom || state->pos.col + width > state->cols) {
linefeed(state);
state->pos.col = 0;
state->at_phantom = 0;
@@ -1346,11 +1347,26 @@ static void request_status_string(VTermState *state, const char *command, size_t
{
if(cmdlen == 1)
switch(command[0]) {
+ case 'm': // Query SGR
+ {
+ long args[20];
+ int argc = vterm_state_getpen(state, args, sizeof(args)/sizeof(args[0]));
+ vterm_push_output_sprintf_ctrl(state->vt, C1_DCS, "1$r");
+ for(int argi = 0; argi < argc; argi++)
+ vterm_push_output_sprintf(state->vt,
+ argi == argc - 1 ? "%d" :
+ CSI_ARG_HAS_MORE(args[argi]) ? "%d:" :
+ "%d;",
+ CSI_ARG(args[argi]));
+ vterm_push_output_sprintf(state->vt, "m");
+ vterm_push_output_sprintf_ctrl(state->vt, C1_ST, "");
+ }
+ return;
case 'r': // Query DECSTBM
- vterm_push_output_sprintf_ctrl(state->vt, C1_DCS, "1$r%d;%dr", state->scrollregion_top+1, SCROLLREGION_BOTTOM(state));
+ vterm_push_output_sprintf_dcs(state->vt, "1$r%d;%dr", state->scrollregion_top+1, SCROLLREGION_BOTTOM(state));
return;
case 's': // Query DECSLRM
- vterm_push_output_sprintf_ctrl(state->vt, C1_DCS, "1$r%d;%ds", SCROLLREGION_LEFT(state)+1, SCROLLREGION_RIGHT(state));
+ vterm_push_output_sprintf_dcs(state->vt, "1$r%d;%ds", SCROLLREGION_LEFT(state)+1, SCROLLREGION_RIGHT(state));
return;
}
@@ -1364,16 +1380,16 @@ static void request_status_string(VTermState *state, const char *command, size_t
}
if(state->mode.cursor_blink)
reply--;
- vterm_push_output_sprintf_ctrl(state->vt, C1_DCS, "1$r%d q", reply);
+ vterm_push_output_sprintf_dcs(state->vt, "1$r%d q", reply);
return;
}
else if(strneq(command, "\"q", 2)) {
- vterm_push_output_sprintf_ctrl(state->vt, C1_DCS, "1$r%d\"q", state->protected_cell ? 1 : 2);
+ vterm_push_output_sprintf_dcs(state->vt, "1$r%d\"q", state->protected_cell ? 1 : 2);
return;
}
}
- vterm_push_output_sprintf_ctrl(state->vt, C1_DCS, "0$r%.s", (int)cmdlen, command);
+ vterm_push_output_sprintf_dcs(state->vt, "0$r%.s", (int)cmdlen, command);
}
static int on_dcs(const char *command, size_t cmdlen, void *user)
@@ -1421,18 +1437,23 @@ static int on_resize(int rows, int cols, void *user)
state->rows = rows;
state->cols = cols;
- if(state->pos.row >= rows)
- state->pos.row = rows - 1;
- if(state->pos.col >= cols)
- state->pos.col = cols - 1;
+ VTermPos delta = { 0, 0 };
+
+ if(state->callbacks && state->callbacks->resize)
+ (*state->callbacks->resize)(rows, cols, &delta, state->cbdata);
if(state->at_phantom && state->pos.col < cols-1) {
state->at_phantom = 0;
state->pos.col++;
}
- if(state->callbacks && state->callbacks->resize)
- (*state->callbacks->resize)(rows, cols, state->cbdata);
+ state->pos.row += delta.row;
+ state->pos.col += delta.col;
+
+ if(state->pos.row >= rows)
+ state->pos.row = rows - 1;
+ if(state->pos.col >= cols)
+ state->pos.col = cols - 1;
updatecursor(state, &oldpos, 1);
diff --git a/src/vterm.c b/src/vterm.c
index b8d7bd1..940d1fb 100644
--- a/src/vterm.c
+++ b/src/vterm.c
@@ -146,9 +146,21 @@ void vterm_push_output_sprintf_ctrl(VTerm *vt, unsigned char ctrl, const char *f
va_start(args, fmt);
vterm_push_output_vsprintf(vt, fmt, args);
va_end(args);
+}
+
+void vterm_push_output_sprintf_dcs(VTerm *vt, const char *fmt, ...)
+{
+ if(!vt->mode.ctrl8bit)
+ vterm_push_output_sprintf(vt, "\e%c", C1_DCS - 0x40);
+ else
+ vterm_push_output_sprintf(vt, "%c", C1_DCS);
+
+ va_list args;
+ va_start(args, fmt);
+ vterm_push_output_vsprintf(vt, fmt, args);
+ va_end(args);
- if(ctrl == C1_DCS)
- vterm_push_output_sprintf_ctrl(vt, C1_ST, "");
+ vterm_push_output_sprintf_ctrl(vt, C1_ST, "");
}
size_t vterm_output_bufferlen(VTerm *vt)
diff --git a/src/vterm_internal.h b/src/vterm_internal.h
index 2139d0a..9a0183e 100644
--- a/src/vterm_internal.h
+++ b/src/vterm_internal.h
@@ -27,6 +27,11 @@ struct VTermPen
unsigned int font:4; /* To store 0-9 */
};
+static inline int vterm_color_equal(VTermColor a, VTermColor b)
+{
+ return a.red == b.red && a.green == b.green && a.blue == b.blue;
+}
+
struct VTermState
{
VTerm *vt;
@@ -87,10 +92,11 @@ struct VTermState
VTermColor default_fg;
VTermColor default_bg;
- int fg_ansi;
+ int fg_index;
+ int bg_index;
int bold_is_highbright;
- int protected_cell;
+ unsigned int protected_cell : 1;
/* Saved state under DEC mode 1048/1049 */
struct {
@@ -162,11 +168,13 @@ void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len);
void vterm_push_output_vsprintf(VTerm *vt, const char *format, va_list args);
void vterm_push_output_sprintf(VTerm *vt, const char *format, ...);
void vterm_push_output_sprintf_ctrl(VTerm *vt, unsigned char ctrl, const char *fmt, ...);
+void vterm_push_output_sprintf_dcs(VTerm *vt, const char *fmt, ...);
void vterm_state_free(VTermState *state);
void vterm_state_resetpen(VTermState *state);
void vterm_state_setpen(VTermState *state, const long args[], int argcount);
+int vterm_state_getpen(VTermState *state, long args[], int argcount);
void vterm_state_savepen(VTermState *state, int save);
enum {