diff options
author | Jeff Sharkey <jsharkey@android.com> | 2013-04-23 10:34:06 -0700 |
---|---|---|
committer | Jeff Sharkey <jsharkey@android.com> | 2013-04-23 17:02:30 -0700 |
commit | 73fbfc38792bd96137d5b6ae3016dfc4d9805d46 (patch) | |
tree | 4cbe1d51a179c7e6ff76a6e09ee1ceabb5f8810a | |
parent | 5b78a3aa7741c3f44b676ccffa765cecee1cbd4c (diff) | |
download | libvterm-73fbfc38792bd96137d5b6ae3016dfc4d9805d46.tar.gz |
Snap to current upstream libvterm.android-cts-5.1_r9android-cts-5.1_r8android-cts-5.1_r7android-cts-5.1_r6android-cts-5.1_r5android-cts-5.1_r4android-cts-5.1_r3android-cts-5.1_r28android-cts-5.1_r27android-cts-5.1_r26android-cts-5.1_r25android-cts-5.1_r24android-cts-5.1_r23android-cts-5.1_r22android-cts-5.1_r21android-cts-5.1_r20android-cts-5.1_r2android-cts-5.1_r19android-cts-5.1_r18android-cts-5.1_r17android-cts-5.1_r16android-cts-5.1_r15android-cts-5.1_r14android-cts-5.1_r13android-cts-5.1_r10android-cts-5.1_r1android-5.1.1_r9android-5.1.1_r8android-5.1.1_r7android-5.1.1_r6android-5.1.1_r5android-5.1.1_r4android-5.1.1_r38android-5.1.1_r37android-5.1.1_r36android-5.1.1_r35android-5.1.1_r34android-5.1.1_r33android-5.1.1_r30android-5.1.1_r3android-5.1.1_r29android-5.1.1_r28android-5.1.1_r26android-5.1.1_r25android-5.1.1_r24android-5.1.1_r23android-5.1.1_r22android-5.1.1_r20android-5.1.1_r2android-5.1.1_r19android-5.1.1_r18android-5.1.1_r17android-5.1.1_r16android-5.1.1_r15android-5.1.1_r14android-5.1.1_r13android-5.1.1_r12android-5.1.1_r10android-5.1.1_r1android-5.1.0_r5android-5.1.0_r4android-5.1.0_r3android-5.1.0_r1lollipop-mr1-wfc-releaselollipop-mr1-releaselollipop-mr1-fi-releaselollipop-mr1-devlollipop-mr1-cts-release
Has updated scrollback API and bugfixes.
Change-Id: I92c097393d57f3ae04dfddb745c618c145e8ff1a
-rw-r--r-- | include/vterm.h | 204 | ||||
-rw-r--r-- | src/encoding.c | 2 | ||||
-rw-r--r-- | src/pen.c | 88 | ||||
-rw-r--r-- | src/screen.c | 203 | ||||
-rw-r--r-- | src/state.c | 45 | ||||
-rw-r--r-- | src/vterm.c | 16 | ||||
-rw-r--r-- | src/vterm_internal.h | 12 |
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) @@ -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 { |