Import libvterm r617.

This commit is contained in:
Adrien Destugues
2014-08-11 23:14:42 +02:00
parent b54c6f2e65
commit 1ec255178a
14 changed files with 1415 additions and 607 deletions
+144 -87
View File
@@ -44,37 +44,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; 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 { typedef struct {
uint8_t red, green, blue; uint8_t red, green, blue;
} VTermColor; } VTermColor;
@@ -121,33 +90,23 @@ typedef enum {
enum { enum {
VTERM_PROP_CURSORSHAPE_BLOCK = 1, VTERM_PROP_CURSORSHAPE_BLOCK = 1,
VTERM_PROP_CURSORSHAPE_UNDERLINE, VTERM_PROP_CURSORSHAPE_UNDERLINE,
VTERM_PROP_CURSORSHAPE_BAR_LEFT,
}; };
typedef void (*VTermMouseFunc)(int x, int y, int button, int pressed, int modifiers, void *data); typedef void (*VTermMouseFunc)(int x, int y, int button, int pressed, int modifiers, void *data);
typedef struct { typedef struct {
int (*putglyph)(const uint32_t chars[], int width, VTermPos pos, void *user); const uint32_t *chars;
int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user); int width;
int (*scrollrect)(VTermRect rect, int downward, int rightward, void *user); unsigned int protected_cell:1; /* DECSCA-protected against DECSEL/DECSED */
int (*moverect)(VTermRect dest, VTermRect src, void *user); unsigned int dwl:1; /* DECDWL or DECDHL double-width line */
int (*erase)(VTermRect rect, void *user); unsigned int dhl:2; /* DECDHL double-height line (1=top 2=bottom) */
int (*initpen)(void *user); } VTermGlyphInfo;
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 { typedef struct {
int (*damage)(VTermRect rect, void *user); unsigned int doublewidth:1; /* DECDWL or DECDHL line */
int (*moverect)(VTermRect dest, VTermRect src, void *user); unsigned int doubleheight:2; /* DECDHL line (1=top 2=bottom) */
int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user); } VTermLineInfo;
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 { typedef struct {
/* libvterm relies on this memory to be zeroed out before it is returned /* libvterm relies on this memory to be zeroed out before it is returned
@@ -160,22 +119,127 @@ VTerm *vterm_new(int rows, int cols);
VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *funcs, void *allocdata); VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *funcs, void *allocdata);
void vterm_free(VTerm* vt); void vterm_free(VTerm* vt);
void vterm_get_size(VTerm *vt, int *rowsp, int *colsp); void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp);
void vterm_set_size(VTerm *vt, int rows, int cols); 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_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);
int (*setlineinfo)(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user);
} VTermStateCallbacks;
VTermState *vterm_obtain_state(VTerm *vt); VTermState *vterm_obtain_state(VTerm *vt);
void vterm_state_reset(VTermState *state, int hard); void vterm_state_reset(VTermState *state, int hard);
void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user); void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user);
void vterm_state_get_cursorpos(VTermState *state, VTermPos *cursorpos); void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos);
void vterm_state_set_default_colors(VTermState *state, VTermColor *default_fg, VTermColor *default_bg); 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);
void vterm_state_set_default_colors(VTermState *state, const VTermColor *default_fg, const VTermColor *default_bg);
void vterm_state_set_palette_color(VTermState *state, int index, const VTermColor *col);
void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright); void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright);
int vterm_state_get_penattr(VTermState *state, VTermAttr attr, VTermValue *val); int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val);
int vterm_state_set_termprop(VTermState *state, VTermProp prop, VTermValue *val);
const VTermLineInfo *vterm_state_get_lineinfo(const VTermState *state, int row);
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 */
unsigned int dwl : 1; /* On a DECDWL or DECDHL line */
unsigned int dhl : 2; /* On a DECDHL line (1=top 2=bottom) */
} 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); VTermScreen *vterm_obtain_screen(VTerm *vt);
@@ -193,48 +257,41 @@ void vterm_screen_flush_damage(VTermScreen *screen);
void vterm_screen_set_damage_merge(VTermScreen *screen, VTermDamageSize size); void vterm_screen_set_damage_merge(VTermScreen *screen, VTermDamageSize size);
void vterm_screen_reset(VTermScreen *screen, int hard); void vterm_screen_reset(VTermScreen *screen, int hard);
size_t vterm_screen_get_chars(VTermScreen *screen, uint32_t *chars, size_t len, const VTermRect rect);
size_t vterm_screen_get_text(VTermScreen *screen, char *str, size_t len, const VTermRect rect);
typedef struct { /* Neither of these functions NUL-terminate the buffer */
#define VTERM_MAX_CHARS_PER_CELL 6 size_t vterm_screen_get_chars(const VTermScreen *screen, uint32_t *chars, size_t len, const VTermRect rect);
uint32_t chars[VTERM_MAX_CHARS_PER_CELL]; size_t vterm_screen_get_text(const VTermScreen *screen, char *str, size_t len, const VTermRect rect);
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;
void vterm_screen_get_cell(VTermScreen *screen, VTermPos pos, VTermScreenCell *cell); 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_is_eol(VTermScreen *screen, VTermPos pos); int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs);
void vterm_input_push_char(VTerm *vt, VTermModifier state, uint32_t c); int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCell *cell);
void vterm_input_push_key(VTerm *vt, VTermModifier state, VTermKey key);
void vterm_parser_set_utf8(VTerm *vt, int is_utf8); int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos);
void vterm_push_bytes(VTerm *vt, const char *bytes, size_t len);
size_t vterm_output_bufferlen(VTerm *vt); /* deprecated */ // ---------
// Utilities
// ---------
size_t vterm_output_get_buffer_size(VTerm *vt); VTermValueType vterm_get_attr_type(VTermAttr attr);
size_t vterm_output_get_buffer_current(VTerm *vt); VTermValueType vterm_get_prop_type(VTermProp prop);
size_t vterm_output_get_buffer_remaining(VTerm *vt);
size_t vterm_output_bufferread(VTerm *vt, char *buffer, size_t len);
void vterm_scroll_rect(VTermRect rect, void vterm_scroll_rect(VTermRect rect,
int downward, int downward,
int rightward, int rightward,
int (*moverect)(VTermRect src, VTermRect dest, void *user), int (*moverect)(VTermRect src, VTermRect dest, void *user),
int (*eraserect)(VTermRect rect, void *user), int (*eraserect)(VTermRect rect, int selective, void *user),
void *user); void *user);
void vterm_copy_cells(VTermRect dest, void vterm_copy_cells(VTermRect dest,
@@ -28,9 +28,28 @@ typedef enum {
VTERM_KEY_PAGEUP, VTERM_KEY_PAGEUP,
VTERM_KEY_PAGEDOWN, VTERM_KEY_PAGEDOWN,
VTERM_KEY_FUNCTION_0, VTERM_KEY_FUNCTION_0 = 256,,
VTERM_KEY_FUNCTION_MAX = VTERM_KEY_FUNCTION_0 + 255, VTERM_KEY_FUNCTION_MAX = VTERM_KEY_FUNCTION_0 + 255,
VTERM_KEY_KP_0,
VTERM_KEY_KP_1,
VTERM_KEY_KP_2,
VTERM_KEY_KP_3,
VTERM_KEY_KP_4,
VTERM_KEY_KP_5,
VTERM_KEY_KP_6,
VTERM_KEY_KP_7,
VTERM_KEY_KP_8,
VTERM_KEY_KP_9,
VTERM_KEY_KP_MULT,
VTERM_KEY_KP_PLUS,
VTERM_KEY_KP_COMMA,
VTERM_KEY_KP_MINUS,
VTERM_KEY_KP_PERIOD,
VTERM_KEY_KP_DIVIDE,
VTERM_KEY_KP_ENTER,
VTERM_KEY_KP_EQUAL,
VTERM_KEY_MAX, // Must be last VTERM_KEY_MAX, // Must be last
} VTermKey; } VTermKey;
+13 -12
View File
@@ -1,10 +1,8 @@
#include "vterm_internal.h" #include "vterm_internal.h"
#include <stdio.h>
#define UNICODE_INVALID 0xFFFD #define UNICODE_INVALID 0xFFFD
#ifdef DEBUG #if defined(DEBUG) && DEBUG > 1
# define DEBUG_PRINT_UTF8 # define DEBUG_PRINT_UTF8
#endif #endif
@@ -37,7 +35,7 @@ static void decode_utf8(VTermEncoding *enc, void *data_,
printf("BEGIN UTF-8\n"); printf("BEGIN UTF-8\n");
#endif #endif
for( ; *pos < bytelen; (*pos)++) { for(; *pos < bytelen && *cpi < cplen; (*pos)++) {
unsigned char c = bytes[*pos]; unsigned char c = bytes[*pos];
#ifdef DEBUG_PRINT_UTF8 #ifdef DEBUG_PRINT_UTF8
@@ -157,8 +155,10 @@ static void decode_usascii(VTermEncoding *enc, void *data,
uint32_t cp[], int *cpi, int cplen, uint32_t cp[], int *cpi, int cplen,
const char bytes[], size_t *pos, size_t bytelen) const char bytes[], size_t *pos, size_t bytelen)
{ {
for(; *pos < bytelen; (*pos)++) { int is_gr = bytes[*pos] & 0x80;
unsigned char c = bytes[*pos];
for(; *pos < bytelen && *cpi < cplen; (*pos)++) {
unsigned char c = bytes[*pos] ^ is_gr;
if(c < 0x20 || c >= 0x80) if(c < 0x20 || c >= 0x80)
return; return;
@@ -181,11 +181,12 @@ static void decode_table(VTermEncoding *enc, void *data,
const char bytes[], size_t *pos, size_t bytelen) const char bytes[], size_t *pos, size_t bytelen)
{ {
struct StaticTableEncoding *table = (struct StaticTableEncoding *)enc; struct StaticTableEncoding *table = (struct StaticTableEncoding *)enc;
int is_gr = bytes[*pos] & 0x80;
for(; *pos < bytelen; (*pos)++) { for(; *pos < bytelen && *cpi < cplen; (*pos)++) {
unsigned char c = (bytes[*pos]) & 0x7f; unsigned char c = bytes[*pos] ^ is_gr;
if(c < 0x20) if(c < 0x20 || c >= 0x80)
return; return;
if(table->chars[c]) if(table->chars[c])
@@ -208,13 +209,13 @@ encodings[] = {
{ ENC_SINGLE_94, '0', (VTermEncoding*)&encoding_DECdrawing }, { ENC_SINGLE_94, '0', (VTermEncoding*)&encoding_DECdrawing },
{ ENC_SINGLE_94, 'A', (VTermEncoding*)&encoding_uk }, { ENC_SINGLE_94, 'A', (VTermEncoding*)&encoding_uk },
{ ENC_SINGLE_94, 'B', &encoding_usascii }, { ENC_SINGLE_94, 'B', &encoding_usascii },
{ 0, 0 }, { 0 },
}; };
/* This ought to be INTERNAL but isn't because it's used by unit testing */
VTermEncoding *vterm_lookup_encoding(VTermEncodingType type, char designation) VTermEncoding *vterm_lookup_encoding(VTermEncodingType type, char designation)
{ {
int i; for(int i = 0; encodings[i].designation; i++)
for(i = 0; encodings[i].designation; i++)
if(encodings[i].type == type && encodings[i].designation == designation) if(encodings[i].type == type && encodings[i].designation == designation)
return encodings[i].enc; return encodings[i].enc;
return NULL; return NULL;
@@ -1,36 +0,0 @@
static const struct StaticTableEncoding encoding_DECdrawing = {
{ .decode = &decode_table },
{
[0x60] = 0x25C6,
[0x61] = 0x2592,
[0x62] = 0x2409,
[0x63] = 0x240C,
[0x64] = 0x240D,
[0x65] = 0x240A,
[0x66] = 0x00B0,
[0x67] = 0x00B1,
[0x68] = 0x2424,
[0x69] = 0x240B,
[0x6a] = 0x2518,
[0x6b] = 0x2510,
[0x6c] = 0x250C,
[0x6d] = 0x2514,
[0x6e] = 0x253C,
[0x6f] = 0x23BA,
[0x70] = 0x23BB,
[0x71] = 0x2500,
[0x72] = 0x23BC,
[0x73] = 0x23BD,
[0x74] = 0x251C,
[0x75] = 0x2524,
[0x76] = 0x2534,
[0x77] = 0x252C,
[0x78] = 0x2502,
[0x79] = 0x2A7D,
[0x7a] = 0x2A7E,
[0x7b] = 0x03C0,
[0x7c] = 0x2260,
[0x7d] = 0x00A3,
[0x7e] = 0x00B7,
}
};
@@ -1,6 +0,0 @@
static const struct StaticTableEncoding encoding_uk = {
{ .decode = &decode_table },
{
[0x23] = 0x00a3,
}
};
+78 -39
View File
@@ -6,17 +6,11 @@
void vterm_input_push_char(VTerm *vt, VTermModifier mod, uint32_t c) void vterm_input_push_char(VTerm *vt, VTermModifier mod, uint32_t c)
{ {
int needs_CSIu;
/* The shift modifier is never important for Unicode characters /* The shift modifier is never important for Unicode characters
* apart from Space * apart from Space
*/ */
if(c != ' ') if(c != ' ')
mod &= ~VTERM_MOD_SHIFT; mod &= ~VTERM_MOD_SHIFT;
/* However, since Shift-Space is too easy to mistype accidentally, remove
* shift if it's the only modifier
*/
else if(mod == VTERM_MOD_SHIFT)
mod = 0;
if(mod == 0) { if(mod == 0) {
// Normal text - ignore just shift // Normal text - ignore just shift
@@ -26,6 +20,7 @@ void vterm_input_push_char(VTerm *vt, VTermModifier mod, uint32_t c)
return; return;
} }
int needs_CSIu;
switch(c) { switch(c) {
/* Special Ctrl- letters that can't be represented elsewise */ /* Special Ctrl- letters that can't be represented elsewise */
case 'h': case 'i': case 'j': case 'm': case '[': case 'h': case 'i': case 'j': case 'm': case '[':
@@ -42,7 +37,7 @@ void vterm_input_push_char(VTerm *vt, VTermModifier mod, uint32_t c)
/* ALT we can just prefix with ESC; anything else requires CSI u */ /* ALT we can just prefix with ESC; anything else requires CSI u */
if(needs_CSIu && (mod & ~VTERM_MOD_ALT)) { if(needs_CSIu && (mod & ~VTERM_MOD_ALT)) {
vterm_push_output_sprintf(vt, "\e[%d;%du", c, mod+1); vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", c, mod+1);
return; return;
} }
@@ -58,15 +53,17 @@ typedef struct {
KEYCODE_LITERAL, KEYCODE_LITERAL,
KEYCODE_TAB, KEYCODE_TAB,
KEYCODE_ENTER, KEYCODE_ENTER,
KEYCODE_SS3,
KEYCODE_CSI, KEYCODE_CSI,
KEYCODE_CSI_CURSOR, KEYCODE_CSI_CURSOR,
KEYCODE_CSINUM, KEYCODE_CSINUM,
KEYCODE_KEYPAD,
} type; } type;
char literal; char literal;
int csinum; int csinum;
} keycodes_s; } keycodes_s;
keycodes_s keycodes[] = { static keycodes_s keycodes[] = {
{ KEYCODE_NONE }, // NONE { KEYCODE_NONE }, // NONE
{ KEYCODE_ENTER, '\r' }, // ENTER { KEYCODE_ENTER, '\r' }, // ENTER
@@ -85,7 +82,9 @@ keycodes_s keycodes[] = {
{ KEYCODE_CSI_CURSOR, 'F' }, // END { KEYCODE_CSI_CURSOR, 'F' }, // END
{ KEYCODE_CSINUM, '~', 5 }, // PAGEUP { KEYCODE_CSINUM, '~', 5 }, // PAGEUP
{ KEYCODE_CSINUM, '~', 6 }, // PAGEDOWN { KEYCODE_CSINUM, '~', 6 }, // PAGEDOWN
};
static keycodes_s keycodes_fn[] = {
{ KEYCODE_NONE }, // F0 - shouldn't happen { KEYCODE_NONE }, // F0 - shouldn't happen
{ KEYCODE_CSI_CURSOR, 'P' }, // F1 { KEYCODE_CSI_CURSOR, 'P' }, // F1
{ KEYCODE_CSI_CURSOR, 'Q' }, // F2 { KEYCODE_CSI_CURSOR, 'Q' }, // F2
@@ -101,22 +100,48 @@ keycodes_s keycodes[] = {
{ KEYCODE_CSINUM, '~', 24 }, // F12 { KEYCODE_CSINUM, '~', 24 }, // F12
}; };
static keycodes_s keycodes_kp[] = {
{ KEYCODE_KEYPAD, '0', 'p' }, // KP_0
{ KEYCODE_KEYPAD, '1', 'q' }, // KP_1
{ KEYCODE_KEYPAD, '2', 'r' }, // KP_2
{ KEYCODE_KEYPAD, '3', 's' }, // KP_3
{ KEYCODE_KEYPAD, '4', 't' }, // KP_4
{ KEYCODE_KEYPAD, '5', 'u' }, // KP_5
{ KEYCODE_KEYPAD, '6', 'v' }, // KP_6
{ KEYCODE_KEYPAD, '7', 'w' }, // KP_7
{ KEYCODE_KEYPAD, '8', 'x' }, // KP_8
{ KEYCODE_KEYPAD, '9', 'y' }, // KP_9
{ KEYCODE_KEYPAD, '*', 'j' }, // KP_MULT
{ KEYCODE_KEYPAD, '+', 'k' }, // KP_PLUS
{ KEYCODE_KEYPAD, ',', 'l' }, // KP_COMMA
{ KEYCODE_KEYPAD, '-', 'm' }, // KP_MINUS
{ KEYCODE_KEYPAD, '.', 'n' }, // KP_PERIOD
{ KEYCODE_KEYPAD, '/', 'o' }, // KP_DIVIDE
{ KEYCODE_KEYPAD, '\n', 'M' }, // KP_ENTER
{ KEYCODE_KEYPAD, '=', 'X' }, // KP_EQUAL
};
void vterm_input_push_key(VTerm *vt, VTermModifier mod, VTermKey key) void vterm_input_push_key(VTerm *vt, VTermModifier mod, VTermKey key)
{ {
if(key == VTERM_KEY_NONE)
return;
keycodes_s k; keycodes_s k;
/* Since Shift-Enter and Shift-Backspace are too easy to mistype if(key < VTERM_KEY_FUNCTION_0) {
* accidentally, remove shift if it's the only modifier if(key >= sizeof(keycodes)/sizeof(keycodes[0]))
*/ return;
if((key == VTERM_KEY_ENTER || key == VTERM_KEY_BACKSPACE) && mod == VTERM_MOD_SHIFT) k = keycodes[key];
mod = 0; }
else if(key >= VTERM_KEY_FUNCTION_0 && key <= VTERM_KEY_FUNCTION_MAX) {
if(key == VTERM_KEY_NONE || key >= VTERM_KEY_MAX) if((key - VTERM_KEY_FUNCTION_0) >= sizeof(keycodes_fn)/sizeof(keycodes_fn[0]))
return; return;
k = keycodes_fn[key - VTERM_KEY_FUNCTION_0];
if(key >= sizeof(keycodes)/sizeof(keycodes[0])) }
return; else if(key >= VTERM_KEY_KP_0) {
if((key - VTERM_KEY_KP_0) >= sizeof(keycodes_kp)/sizeof(keycodes_kp[0]))
k = keycodes[key]; return;
k = keycodes_kp[key - VTERM_KEY_KP_0];
}
switch(k.type) { switch(k.type) {
case KEYCODE_NONE: case KEYCODE_NONE:
@@ -125,11 +150,11 @@ void vterm_input_push_key(VTerm *vt, VTermModifier mod, VTermKey key)
case KEYCODE_TAB: case KEYCODE_TAB:
/* Shift-Tab is CSI Z but plain Tab is 0x09 */ /* Shift-Tab is CSI Z but plain Tab is 0x09 */
if(mod == VTERM_MOD_SHIFT) if(mod == VTERM_MOD_SHIFT)
vterm_push_output_sprintf(vt, "\e[Z"); vterm_push_output_sprintf_ctrl(vt, C1_CSI, "Z");
else if(mod & VTERM_MOD_SHIFT) else if(mod & VTERM_MOD_SHIFT)
vterm_push_output_sprintf(vt, "\e[1;%dZ", mod+1); vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%dZ", mod+1);
else else
goto literal; goto case_LITERAL;
break; break;
case KEYCODE_ENTER: case KEYCODE_ENTER:
@@ -137,35 +162,49 @@ void vterm_input_push_key(VTerm *vt, VTermModifier mod, VTermKey key)
if(vt->state->mode.newline) if(vt->state->mode.newline)
vterm_push_output_sprintf(vt, "\r\n"); vterm_push_output_sprintf(vt, "\r\n");
else else
goto literal; goto case_LITERAL;
break; break;
literal: case KEYCODE_LITERAL: case_LITERAL:
case KEYCODE_LITERAL:
if(mod & (VTERM_MOD_SHIFT|VTERM_MOD_CTRL)) if(mod & (VTERM_MOD_SHIFT|VTERM_MOD_CTRL))
vterm_push_output_sprintf(vt, "\e[%d;%du", k.literal, mod+1); vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", k.literal, mod+1);
else else
vterm_push_output_sprintf(vt, mod & VTERM_MOD_ALT ? "\e%c" : "%c", k.literal); vterm_push_output_sprintf(vt, mod & VTERM_MOD_ALT ? "\e%c" : "%c", k.literal);
break; break;
case KEYCODE_CSI_CURSOR: case KEYCODE_SS3: case_SS3:
if(vt->state->mode.cursor && mod == 0) {
vterm_push_output_sprintf(vt, "\eO%c", k.literal);
break;
}
/* else FALLTHROUGH */
case KEYCODE_CSI:
if(mod == 0) if(mod == 0)
vterm_push_output_sprintf(vt, "\e[%c", k.literal); vterm_push_output_sprintf_ctrl(vt, C1_SS3, "%c", k.literal);
else else
vterm_push_output_sprintf(vt, "\e[1;%d%c", mod + 1, k.literal); goto case_CSI;
break;
case KEYCODE_CSI: case_CSI:
if(mod == 0)
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%c", k.literal);
else
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%d%c", mod + 1, k.literal);
break; break;
case KEYCODE_CSINUM: case KEYCODE_CSINUM:
if(mod == 0) if(mod == 0)
vterm_push_output_sprintf(vt, "\e[%d%c", k.csinum, k.literal); vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d%c", k.csinum, k.literal);
else else
vterm_push_output_sprintf(vt, "\e[%d;%d%c", k.csinum, mod + 1, k.literal); vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%d%c", k.csinum, mod + 1, k.literal);
break; break;
case KEYCODE_CSI_CURSOR:
if(vt->state->mode.cursor)
goto case_SS3;
else
goto case_CSI;
case KEYCODE_KEYPAD:
if(vt->state->mode.keypad) {
k.literal = k.csinum;
goto case_SS3;
}
else
goto case_LITERAL;
} }
} }
+12 -12
View File
@@ -1,6 +1,5 @@
#include "vterm_internal.h" #include "vterm_internal.h"
#include <inttypes.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@@ -19,15 +18,10 @@ static void do_control(VTerm *vt, unsigned char control)
static void do_string_csi(VTerm *vt, const char *args, size_t arglen, char command) static void do_string_csi(VTerm *vt, const char *args, size_t arglen, char command)
{ {
size_t i = 0; int i = 0;
int leaderlen = 0; int leaderlen = 0;
char leader[CSI_LEADER_MAX]; char leader[CSI_LEADER_MAX];
int argcount = 1; // Always at least 1 arg
long csi_args[CSI_ARGS_MAX];
int argi;
int intermedlen = 0;
char intermed[CSI_INTERMED_MAX];
// Extract leader bytes 0x3c to 0x3f // Extract leader bytes 0x3c to 0x3f
for( ; i < arglen; i++) { for( ; i < arglen; i++) {
@@ -39,14 +33,18 @@ static void do_string_csi(VTerm *vt, const char *args, size_t arglen, char comma
leader[leaderlen] = 0; leader[leaderlen] = 0;
int argcount = 1; // Always at least 1 arg
for( ; i < arglen; i++) for( ; i < arglen; i++)
if(args[i] == 0x3b || args[i] == 0x3a) // ; or : if(args[i] == 0x3b || args[i] == 0x3a) // ; or :
argcount++; argcount++;
/* TODO: Consider if these buffers should live in the VTerm struct itself */ /* TODO: Consider if these buffers should live in the VTerm struct itself */
long csi_args[CSI_ARGS_MAX];
if(argcount > CSI_ARGS_MAX) if(argcount > CSI_ARGS_MAX)
argcount = CSI_ARGS_MAX; argcount = CSI_ARGS_MAX;
int argi;
for(argi = 0; argi < argcount; argi++) for(argi = 0; argi < argcount; argi++)
csi_args[argi] = CSI_ARG_MISSING; csi_args[argi] = CSI_ARG_MISSING;
@@ -72,6 +70,8 @@ static void do_string_csi(VTerm *vt, const char *args, size_t arglen, char comma
} }
done_leader: ; done_leader: ;
int intermedlen = 0;
char intermed[CSI_INTERMED_MAX];
for( ; i < arglen; i++) { for( ; i < arglen; i++) {
if((args[i] & 0xf0) != 0x20) if((args[i] & 0xf0) != 0x20)
@@ -118,8 +118,6 @@ static void append_strbuffer(VTerm *vt, const char *str, size_t len)
static size_t do_string(VTerm *vt, const char *str_frag, size_t len) static size_t do_string(VTerm *vt, const char *str_frag, size_t len)
{ {
size_t eaten;
if(vt->strbuffer_cur) { if(vt->strbuffer_cur) {
if(str_frag) if(str_frag)
append_strbuffer(vt, str_frag, len); append_strbuffer(vt, str_frag, len);
@@ -134,6 +132,8 @@ static size_t do_string(VTerm *vt, const char *str_frag, size_t len)
vt->strbuffer_cur = 0; vt->strbuffer_cur = 0;
size_t eaten;
switch(vt->parser_state) { switch(vt->parser_state) {
case NORMAL: case NORMAL:
if(vt->parser_callbacks && vt->parser_callbacks->text) if(vt->parser_callbacks && vt->parser_callbacks->text)
@@ -190,7 +190,7 @@ static size_t do_string(VTerm *vt, const char *str_frag, size_t len)
void vterm_push_bytes(VTerm *vt, const char *bytes, size_t len) void vterm_push_bytes(VTerm *vt, const char *bytes, size_t len)
{ {
size_t pos = 0; size_t pos = 0;
const char *string_start = NULL; const char *string_start;
switch(vt->parser_state) { switch(vt->parser_state) {
case NORMAL: case NORMAL:
@@ -299,14 +299,14 @@ void vterm_push_bytes(VTerm *vt, const char *bytes, size_t len)
case OSC: case OSC:
case DCS: case DCS:
if(c == 0x07 || (c == 0x9c && !vt->is_utf8)) { if(c == 0x07 || (c == 0x9c && !vt->mode.utf8)) {
do_string(vt, string_start, bytes + pos - string_start); do_string(vt, string_start, bytes + pos - string_start);
ENTER_NORMAL_STATE(); ENTER_NORMAL_STATE();
} }
break; break;
case NORMAL: case NORMAL:
if(c >= 0x80 && c < 0xa0 && !vt->is_utf8) { if(c >= 0x80 && c < 0xa0 && !vt->mode.utf8) {
switch(c) { switch(c) {
case 0x90: // DCS case 0x90: // DCS
ENTER_STRING_STATE(DCS); ENTER_STRING_STATE(DCS);
+133 -42
View File
@@ -33,17 +33,39 @@ static int ramp24[] = {
0x85, 0x90, 0x9B, 0xA6, 0xB1, 0xBC, 0xC7, 0xD2, 0xDD, 0xE8, 0xF3, 0xFF, 0x85, 0x90, 0x9B, 0xA6, 0xB1, 0xBC, 0xC7, 0xD2, 0xDD, 0xE8, 0xF3, 0xFF,
}; };
static void lookup_colour_ansi(long index, char is_bg, VTermColor *col) static void lookup_colour_ansi(const VTermState *state, long index, VTermColor *col)
{ {
if(index >= 0 && index < 16) { if(index >= 0 && index < 16) {
*col = ansi_colors[index]; *col = state->colors[index];
} }
} }
static int lookup_colour(int palette, const long args[], int argcount, char is_bg, VTermColor *col) static void lookup_colour_palette(const VTermState *state, long index, VTermColor *col)
{ {
long index; if(index >= 0 && index < 16) {
// Normal 8 colours or high intensity - parse as palette 0
lookup_colour_ansi(state, index, col);
}
else if(index >= 16 && index < 232) {
// 216-colour cube
index -= 16;
col->blue = ramp6[index % 6];
col->green = ramp6[index/6 % 6];
col->red = ramp6[index/6/6 % 6];
}
else if(index >= 232 && index < 256) {
// 24 greyscales
index -= 232;
col->red = ramp24[index];
col->green = ramp24[index];
col->blue = ramp24[index];
}
}
static int lookup_colour(const VTermState *state, int palette, const long args[], int argcount, VTermColor *col, int *index)
{
switch(palette) { switch(palette) {
case 2: // RGB mode - 3 args contain colour values directly case 2: // RGB mode - 3 args contain colour values directly
if(argcount < 3) if(argcount < 3)
@@ -56,28 +78,10 @@ static int lookup_colour(int palette, const long args[], int argcount, char is_b
return 3; return 3;
case 5: // XTerm 256-colour mode case 5: // XTerm 256-colour mode
index = argcount ? CSI_ARG_OR(args[0], -1) : -1; if(index)
*index = CSI_ARG_OR(args[0], -1);
if(index >= 0 && index < 16) { lookup_colour_palette(state, argcount ? CSI_ARG_OR(args[0], -1) : -1, col);
// Normal 8 colours or high intensity - parse as palette 0
lookup_colour_ansi(index, is_bg, col);
}
else if(index >= 16 && index < 232) {
// 216-colour cube
index -= 16;
col->blue = ramp6[index % 6];
col->green = ramp6[index/6 % 6];
col->red = ramp6[index/6/6 % 6];
}
else if(index >= 232 && index < 256) {
// 24 greyscales
index -= 232;
col->red = ramp24[index];
col->green = ramp24[index];
col->blue = ramp24[index];
}
return argcount ? 1 : 0; return argcount ? 1 : 0;
@@ -124,12 +128,22 @@ static void set_pen_col_ansi(VTermState *state, VTermAttr attr, long col)
{ {
VTermColor *colp = (attr == VTERM_ATTR_BACKGROUND) ? &state->pen.bg : &state->pen.fg; VTermColor *colp = (attr == VTERM_ATTR_BACKGROUND) ? &state->pen.bg : &state->pen.fg;
lookup_colour_ansi(col, attr == VTERM_ATTR_BACKGROUND, colp); lookup_colour_ansi(state, col, colp);
setpenattr_col(state, attr, *colp); setpenattr_col(state, attr, *colp);
} }
void vterm_state_resetpen(VTermState *state) INTERNAL void vterm_state_newpen(VTermState *state)
{
// 90% grey so that pure white is brighter
state->default_fg.red = state->default_fg.green = state->default_fg.blue = 240;
state->default_bg.red = state->default_bg.green = state->default_bg.blue = 0;
for(int col = 0; col < 16; col++)
state->colors[col] = ansi_colors[col];
}
INTERNAL void vterm_state_resetpen(VTermState *state)
{ {
state->pen.bold = 0; setpenattr_bool(state, VTERM_ATTR_BOLD, 0); 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);
@@ -139,12 +153,13 @@ void vterm_state_resetpen(VTermState *state)
state->pen.strike = 0; setpenattr_bool(state, VTERM_ATTR_STRIKE, 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->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.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); state->pen.bg = state->default_bg; setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->default_bg);
} }
void vterm_state_savepen(VTermState *state, int save) INTERNAL void vterm_state_savepen(VTermState *state, int save)
{ {
if(save) { if(save) {
state->saved.pen = state->pen; state->saved.pen = state->pen;
@@ -164,18 +179,35 @@ void vterm_state_savepen(VTermState *state, int save)
} }
} }
void vterm_state_set_default_colors(VTermState *state, VTermColor *default_fg, VTermColor *default_bg) void vterm_state_get_default_colors(const VTermState *state, VTermColor *default_fg, VTermColor *default_bg)
{
*default_fg = state->default_fg;
*default_bg = state->default_bg;
}
void vterm_state_get_palette_color(const VTermState *state, int index, VTermColor *col)
{
lookup_colour_palette(state, index, col);
}
void vterm_state_set_default_colors(VTermState *state, const VTermColor *default_fg, const VTermColor *default_bg)
{ {
state->default_fg = *default_fg; state->default_fg = *default_fg;
state->default_bg = *default_bg; state->default_bg = *default_bg;
} }
void vterm_state_set_palette_color(VTermState *state, int index, const VTermColor *col)
{
if(index >= 0 && index < 16)
state->colors[index] = *col;
}
void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright) void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright)
{ {
state->bold_is_highbright = bold_is_highbright; state->bold_is_highbright = bold_is_highbright;
} }
void vterm_state_setpen(VTermState *state, const long args[], int argcount) INTERNAL void vterm_state_setpen(VTermState *state, const long args[], int argcount)
{ {
// SGR - ECMA-48 8.3.117 // SGR - ECMA-48 8.3.117
@@ -197,8 +229,8 @@ void vterm_state_setpen(VTermState *state, const long args[], int argcount)
case 1: // Bold on case 1: // Bold on
state->pen.bold = 1; state->pen.bold = 1;
setpenattr_bool(state, VTERM_ATTR_BOLD, 1); setpenattr_bool(state, VTERM_ATTR_BOLD, 1);
if(state->fg_ansi > -1 && state->bold_is_highbright) if(state->fg_index > -1 && state->fg_index < 8 && state->bold_is_highbright)
set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, state->fg_ansi + (state->pen.bold ? 8 : 0)); set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, state->fg_index + (state->pen.bold ? 8 : 0));
break; break;
case 3: // Italic on case 3: // Italic on
@@ -270,51 +302,59 @@ void vterm_state_setpen(VTermState *state, const long args[], int argcount)
case 30: case 31: case 32: case 33: case 30: case 31: case 32: case 33:
case 34: case 35: case 36: case 37: // Foreground colour palette case 34: case 35: case 36: case 37: // Foreground colour palette
value = CSI_ARG(args[argi]) - 30; value = CSI_ARG(args[argi]) - 30;
state->fg_ansi = value; state->fg_index = value;
if(state->pen.bold && state->bold_is_highbright) if(state->pen.bold && state->bold_is_highbright)
value += 8; value += 8;
set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value); set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
break; break;
case 38: // Foreground colour alternative palette case 38: // Foreground colour alternative palette
state->fg_ansi = -1; state->fg_index = -1;
if(argcount - argi < 1) if(argcount - argi < 1)
return; return;
argi += 1 + lookup_colour(CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, 0, &state->pen.fg); argi += 1 + lookup_colour(state, 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); setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
break; break;
case 39: // Foreground colour default case 39: // Foreground colour default
state->fg_ansi = -1; state->fg_index = -1;
state->pen.fg = state->default_fg; state->pen.fg = state->default_fg;
setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg); setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
break; break;
case 40: case 41: case 42: case 43: case 40: case 41: case 42: case 43:
case 44: case 45: case 46: case 47: // Background colour palette 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; break;
case 48: // Background colour alternative palette case 48: // Background colour alternative palette
state->bg_index = -1;
if(argcount - argi < 1) if(argcount - argi < 1)
return; return;
argi += 1 + lookup_colour(CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, 1, &state->pen.bg); argi += 1 + lookup_colour(state, 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); setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
break; break;
case 49: // Default background case 49: // Default background
state->bg_index = -1;
state->pen.bg = state->default_bg; state->pen.bg = state->default_bg;
setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg); setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
break; break;
case 90: case 91: case 92: case 93: case 90: case 91: case 92: case 93:
case 94: case 95: case 96: case 97: // Foreground colour high-intensity palette case 94: case 95: case 96: case 97: // Foreground colour high-intensity palette
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; break;
case 100: case 101: case 102: case 103: case 100: case 101: case 102: case 103:
case 104: case 105: case 106: case 107: // Background colour high-intensity palette 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; break;
default: default:
@@ -329,7 +369,58 @@ void vterm_state_setpen(VTermState *state, const long args[], int argcount)
} }
} }
int vterm_state_get_penattr(VTermState *state, VTermAttr attr, VTermValue *val) INTERNAL 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) { switch(attr) {
case VTERM_ATTR_BOLD: case VTERM_ATTR_BOLD:
+347 -96
View File
@@ -1,6 +1,7 @@
#include "vterm_internal.h" #include "vterm_internal.h"
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include "rect.h" #include "rect.h"
#include "utf8.h" #include "utf8.h"
@@ -21,6 +22,11 @@ typedef struct
unsigned int reverse : 1; unsigned int reverse : 1;
unsigned int strike : 1; unsigned int strike : 1;
unsigned int font : 4; /* 0 to 9 */ unsigned int font : 4; /* 0 to 9 */
/* Extra state storage that isn't strictly pen-related */
unsigned int protected_cell : 1;
unsigned int dwl : 1; /* on a DECDWL or DECDHL line */
unsigned int dhl : 2; /* on a DECDHL line (1=top 2=bottom) */
} ScreenPen; } ScreenPen;
/* Internal representation of a screen cell */ /* Internal representation of a screen cell */
@@ -30,6 +36,8 @@ typedef struct
ScreenPen pen; ScreenPen pen;
} ScreenCell; } ScreenCell;
static int vterm_screen_set_cell(VTermScreen *screen, VTermPos pos, const VTermScreenCell *cell);
struct VTermScreen struct VTermScreen
{ {
VTerm *vt; VTerm *vt;
@@ -54,22 +62,27 @@ struct VTermScreen
/* buffer will == buffers[0] or buffers[1], depending on altscreen */ /* buffer will == buffers[0] or buffers[1], depending on altscreen */
ScreenCell *buffer; ScreenCell *buffer;
/* buffer for a single screen row used in scrollback storage callbacks */
VTermScreenCell *sb_buffer;
ScreenPen pen; ScreenPen pen;
}; };
static inline ScreenCell *getcell(VTermScreen *screen, int row, int col) static inline ScreenCell *getcell(const VTermScreen *screen, int row, int col)
{ {
/* TODO: Bounds checking */ if(row < 0 || row >= screen->rows)
return NULL;
if(col < 0 || col >= screen->cols)
return NULL;
return screen->buffer + (screen->cols * row) + col; return screen->buffer + (screen->cols * row) + col;
} }
static ScreenCell *realloc_buffer(VTermScreen *screen, ScreenCell *buffer, int new_rows, int new_cols) static ScreenCell *realloc_buffer(VTermScreen *screen, ScreenCell *buffer, int new_rows, int new_cols)
{ {
ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt, sizeof(ScreenCell) * new_rows * new_cols); ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt, sizeof(ScreenCell) * new_rows * new_cols);
int row, col;
for(row = 0; row < new_rows; row++) { for(int row = 0; row < new_rows; row++) {
for(col = 0; col < new_cols; col++) { for(int col = 0; col < new_cols; col++) {
ScreenCell *new_cell = new_buffer + row*new_cols + col; ScreenCell *new_cell = new_buffer + row*new_cols + col;
if(buffer && row < screen->rows && col < screen->cols) if(buffer && row < screen->rows && col < screen->cols)
@@ -156,49 +169,77 @@ static void damagescreen(VTermScreen *screen)
damagerect(screen, rect); damagerect(screen, rect);
} }
static int putglyph(const uint32_t chars[], int width, VTermPos pos, void *user) static int putglyph(VTermGlyphInfo *info, VTermPos pos, void *user)
{ {
VTermScreen *screen = user; VTermScreen *screen = user;
ScreenCell *cell = getcell(screen, pos.row, pos.col); ScreenCell *cell = getcell(screen, pos.row, pos.col);
if(!cell)
return 0;
int i; int i;
int col; for(i = 0; i < VTERM_MAX_CHARS_PER_CELL && info->chars[i]; i++) {
cell->chars[i] = info->chars[i];
VTermRect rect = {
.start_row = pos.row,
.end_row = pos.row+1,
.start_col = pos.col,
.end_col = pos.col+width,
};
for(i = 0; i < VTERM_MAX_CHARS_PER_CELL && chars[i]; i++) {
cell->chars[i] = chars[i];
cell->pen = screen->pen; cell->pen = screen->pen;
} }
if(i < VTERM_MAX_CHARS_PER_CELL) if(i < VTERM_MAX_CHARS_PER_CELL)
cell->chars[i] = 0; cell->chars[i] = 0;
for(col = 1; col < width; col++) for(int col = 1; col < info->width; col++)
getcell(screen, pos.row, pos.col + col)->chars[0] = (uint32_t)-1; getcell(screen, pos.row, pos.col + col)->chars[0] = (uint32_t)-1;
VTermRect rect = {
.start_row = pos.row,
.end_row = pos.row+1,
.start_col = pos.col,
.end_col = pos.col+info->width,
};
cell->pen.protected_cell = info->protected_cell;
cell->pen.dwl = info->dwl;
cell->pen.dhl = info->dhl;
damagerect(screen, rect); damagerect(screen, rect);
return 1; return 1;
} }
static void copycell(VTermPos dest, VTermPos src, void *user)
{
VTermScreen *screen = user;
ScreenCell *destcell = getcell(screen, dest.row, dest.col);
ScreenCell *srccell = getcell(screen, src.row, src.col);
*destcell = *srccell;
}
static int moverect_internal(VTermRect dest, VTermRect src, void *user) static int moverect_internal(VTermRect dest, VTermRect src, void *user)
{ {
VTermScreen *screen = user; VTermScreen *screen = user;
vterm_copy_cells(dest, src, &copycell, screen); 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);
(screen->callbacks->sb_pushline)(screen->cols, screen->sb_buffer, screen->cbdata);
}
}
int cols = src.end_col - src.start_col;
int downward = src.start_row - dest.start_row;
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; return 1;
} }
@@ -221,22 +262,30 @@ static int moverect_user(VTermRect dest, VTermRect src, void *user)
return 1; return 1;
} }
static int erase_internal(VTermRect rect, void *user) static int erase_internal(VTermRect rect, int selective, void *user)
{ {
VTermScreen *screen = user; VTermScreen *screen = user;
int row, col;
for(row = rect.start_row; row < rect.end_row; row++) for(int row = rect.start_row; row < rect.end_row; row++) {
for(col = rect.start_col; col < rect.end_col; col++) { const VTermLineInfo *info = vterm_state_get_lineinfo(screen->state, row);
for(int col = rect.start_col; col < rect.end_col; col++) {
ScreenCell *cell = getcell(screen, row, col); ScreenCell *cell = getcell(screen, row, col);
if(selective && cell->pen.protected_cell)
continue;
cell->chars[0] = 0; cell->chars[0] = 0;
cell->pen = screen->pen; cell->pen = screen->pen;
cell->pen.dwl = info->doublewidth;
cell->pen.dhl = info->doubleheight;
} }
}
return 1; return 1;
} }
static int erase_user(VTermRect rect, void *user) static int erase_user(VTermRect rect, int selective, void *user)
{ {
VTermScreen *screen = user; VTermScreen *screen = user;
@@ -245,10 +294,10 @@ static int erase_user(VTermRect rect, void *user)
return 1; return 1;
} }
static int erase(VTermRect rect, void *user) static int erase(VTermRect rect, int selective, void *user)
{ {
erase_internal(rect, user); erase_internal(rect, selective, user);
return erase_user(rect, user); return erase_user(rect, 0, user);
} }
static int scrollrect(VTermRect rect, int downward, int rightward, void *user) static int scrollrect(VTermRect rect, int downward, int rightward, void *user)
@@ -258,48 +307,75 @@ static int scrollrect(VTermRect rect, int downward, int rightward, void *user)
vterm_scroll_rect(rect, downward, rightward, vterm_scroll_rect(rect, downward, rightward,
moverect_internal, erase_internal, screen); moverect_internal, erase_internal, screen);
if(screen->damage_merge == VTERM_DAMAGE_SCROLL) { if(screen->damage_merge != VTERM_DAMAGE_SCROLL) {
if(screen->damaged.start_row != -1 && vterm_screen_flush_damage(screen);
!rect_intersects(&rect, &screen->damaged)) {
vterm_screen_flush_damage(screen);
}
if(screen->pending_scrollrect.start_row == -1) { vterm_scroll_rect(rect, downward, rightward,
screen->pending_scrollrect = rect; moverect_user, erase_user, screen);
screen->pending_scroll_downward = downward;
screen->pending_scroll_rightward = rightward;
}
else if(rect_equal(&screen->pending_scrollrect, &rect) &&
((screen->pending_scroll_downward == 0 && downward == 0) ||
(screen->pending_scroll_rightward == 0 && rightward == 0))) {
screen->pending_scroll_downward += downward;
screen->pending_scroll_rightward += rightward;
}
else {
vterm_screen_flush_damage(screen);
screen->pending_scrollrect = rect;
screen->pending_scroll_downward = downward;
screen->pending_scroll_rightward = rightward;
}
if(screen->damaged.start_row != -1) {
if(rect_contains(&rect, &screen->damaged)) {
vterm_rect_move(&screen->damaged, -downward, -rightward);
rect_clip(&screen->damaged, &rect);
}
else {
fprintf(stderr, "TODO: scrollrect split damage\n");
}
}
return 1; return 1;
} }
vterm_screen_flush_damage(screen); if(screen->damaged.start_row != -1 &&
!rect_intersects(&rect, &screen->damaged)) {
vterm_screen_flush_damage(screen);
}
vterm_scroll_rect(rect, downward, rightward, if(screen->pending_scrollrect.start_row == -1) {
moverect_user, erase_user, screen); screen->pending_scrollrect = rect;
screen->pending_scroll_downward = downward;
screen->pending_scroll_rightward = rightward;
}
else if(rect_equal(&screen->pending_scrollrect, &rect) &&
((screen->pending_scroll_downward == 0 && downward == 0) ||
(screen->pending_scroll_rightward == 0 && rightward == 0))) {
screen->pending_scroll_downward += downward;
screen->pending_scroll_rightward += rightward;
}
else {
vterm_screen_flush_damage(screen);
screen->pending_scrollrect = rect;
screen->pending_scroll_downward = downward;
screen->pending_scroll_rightward = rightward;
}
if(screen->damaged.start_row == -1)
return 1;
if(rect_contains(&rect, &screen->damaged)) {
vterm_rect_move(&screen->damaged, -downward, -rightward);
rect_clip(&screen->damaged, &rect);
}
/* There are a number of possible cases here, but lets restrict this to only
* the common case where we might actually gain some performance by
* optimising it. Namely, a vertical scroll that neatly cuts the damage
* region in half.
*/
else if(rect.start_col <= screen->damaged.start_col &&
rect.end_col >= screen->damaged.end_col &&
rightward == 0) {
if(screen->damaged.start_row >= rect.start_row &&
screen->damaged.start_row < rect.end_row) {
screen->damaged.start_row -= downward;
if(screen->damaged.start_row < rect.start_row)
screen->damaged.start_row = rect.start_row;
if(screen->damaged.start_row > rect.end_row)
screen->damaged.start_row = rect.end_row;
}
if(screen->damaged.end_row >= rect.start_row &&
screen->damaged.end_row < rect.end_row) {
screen->damaged.end_row -= downward;
if(screen->damaged.end_row < rect.start_row)
screen->damaged.end_row = rect.start_row;
if(screen->damaged.end_row > rect.end_row)
screen->damaged.end_row = rect.end_row;
}
}
else {
fprintf(stderr, "TODO: Just flush and redo damaged=" STRFrect " rect=" STRFrect "\n",
ARGSrect(screen->damaged), ARGSrect(rect));
}
return 1; return 1;
} }
@@ -401,25 +477,52 @@ static int bell(void *user)
return 0; 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; VTermScreen *screen = user;
int old_rows, old_cols;
int is_altscreen = (screen->buffers[1] && screen->buffer == screen->buffers[1]); 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); screen->buffers[0] = realloc_buffer(screen, screen->buffers[0], new_rows, new_cols);
if(screen->buffers[1]) if(screen->buffers[1])
screen->buffers[1] = realloc_buffer(screen, screen->buffers[1], new_rows, new_cols); screen->buffers[1] = realloc_buffer(screen, screen->buffers[1], new_rows, new_cols);
screen->buffer = is_altscreen ? screen->buffers[1] : screen->buffers[0]; screen->buffer = is_altscreen ? screen->buffers[1] : screen->buffers[0];
old_rows = screen->rows;
old_cols = screen->cols;
screen->rows = new_rows; screen->rows = new_rows;
screen->cols = new_cols; 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) { if(new_cols > old_cols) {
VTermRect rect = { VTermRect rect = {
.start_row = 0, .start_row = 0,
@@ -431,6 +534,34 @@ static int resize(int new_rows, int new_cols, void *user)
} }
if(new_rows > old_rows) { 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 = { VTermRect rect = {
.start_row = old_rows, .start_row = old_rows,
.end_row = new_rows, .end_row = new_rows,
@@ -446,6 +577,37 @@ static int resize(int new_rows, int new_cols, void *user)
return 1; return 1;
} }
static int setlineinfo(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user)
{
VTermScreen *screen = user;
if(newinfo->doublewidth != oldinfo->doublewidth ||
newinfo->doubleheight != oldinfo->doubleheight) {
for(int col = 0; col < screen->cols; col++) {
ScreenCell *cell = getcell(screen, row, col);
cell->pen.dwl = newinfo->doublewidth;
cell->pen.dhl = newinfo->doubleheight;
}
VTermRect rect = {
.start_row = row,
.end_row = row + 1,
.start_col = 0,
.end_col = newinfo->doublewidth ? screen->cols / 2 : screen->cols,
};
damagerect(screen, rect);
if(newinfo->doublewidth) {
rect.start_col = screen->cols / 2;
rect.end_col = screen->cols;
erase_internal(rect, 0, user);
}
}
return 1;
}
static VTermStateCallbacks state_cbs = { static VTermStateCallbacks state_cbs = {
.putglyph = &putglyph, .putglyph = &putglyph,
.movecursor = &movecursor, .movecursor = &movecursor,
@@ -456,18 +618,17 @@ static VTermStateCallbacks state_cbs = {
.setmousefunc = &setmousefunc, .setmousefunc = &setmousefunc,
.bell = &bell, .bell = &bell,
.resize = &resize, .resize = &resize,
.setlineinfo = &setlineinfo,
}; };
static VTermScreen *screen_new(VTerm *vt) static VTermScreen *screen_new(VTerm *vt)
{ {
VTermState *state = vterm_obtain_state(vt); VTermState *state = vterm_obtain_state(vt);
VTermScreen *screen;
int rows, cols;
if(!state) if(!state)
return NULL; return NULL;
screen = vterm_allocator_malloc(vt, sizeof(VTermScreen)); VTermScreen *screen = vterm_allocator_malloc(vt, sizeof(VTermScreen));
int rows, cols;
vterm_get_size(vt, &rows, &cols); vterm_get_size(vt, &rows, &cols);
@@ -485,17 +646,21 @@ static VTermScreen *screen_new(VTerm *vt)
screen->buffer = screen->buffers[0]; 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); vterm_state_set_callbacks(screen->state, &state_cbs, screen);
return screen; return screen;
} }
void vterm_screen_free(VTermScreen *screen) INTERNAL void vterm_screen_free(VTermScreen *screen)
{ {
vterm_allocator_free(screen->vt, screen->buffers[0]); vterm_allocator_free(screen->vt, screen->buffers[0]);
if(screen->buffers[1]) if(screen->buffers[1])
vterm_allocator_free(screen->vt, 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); vterm_allocator_free(screen->vt, screen);
} }
@@ -507,12 +672,10 @@ void vterm_screen_reset(VTermScreen *screen, int hard)
vterm_screen_flush_damage(screen); vterm_screen_flush_damage(screen);
} }
static size_t _get_chars(VTermScreen *screen, const int utf8, void *buffer, size_t len, const VTermRect rect) static size_t _get_chars(const VTermScreen *screen, const int utf8, void *buffer, size_t len, const VTermRect rect)
{ {
size_t outpos = 0; size_t outpos = 0;
int padding = 0; int padding = 0;
int row, col;
int i;
#define PUT(c) \ #define PUT(c) \
if(utf8) { \ if(utf8) { \
@@ -529,8 +692,8 @@ static size_t _get_chars(VTermScreen *screen, const int utf8, void *buffer, size
outpos++; \ outpos++; \
} }
for(row = rect.start_row; row < rect.end_row; row++) { for(int row = rect.start_row; row < rect.end_row; 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); ScreenCell *cell = getcell(screen, row, col);
if(cell->chars[0] == 0) if(cell->chars[0] == 0)
@@ -544,7 +707,7 @@ static size_t _get_chars(VTermScreen *screen, const int utf8, void *buffer, size
PUT(UNICODE_SPACE); PUT(UNICODE_SPACE);
padding--; 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]); PUT(cell->chars[i]);
} }
} }
@@ -559,23 +722,24 @@ static size_t _get_chars(VTermScreen *screen, const int utf8, void *buffer, size
return outpos; return outpos;
} }
size_t vterm_screen_get_chars(VTermScreen *screen, uint32_t *chars, size_t len, const VTermRect rect) size_t vterm_screen_get_chars(const VTermScreen *screen, uint32_t *chars, size_t len, const VTermRect rect)
{ {
return _get_chars(screen, 0, chars, len, rect); return _get_chars(screen, 0, chars, len, rect);
} }
size_t vterm_screen_get_text(VTermScreen *screen, char *str, size_t len, const VTermRect rect) size_t vterm_screen_get_text(const VTermScreen *screen, char *str, size_t len, const VTermRect rect)
{ {
return _get_chars(screen, 1, str, len, rect); return _get_chars(screen, 1, str, len, rect);
} }
/* Copy internal to external representation of a screen cell */ /* Copy internal to external representation of a screen cell */
void vterm_screen_get_cell(VTermScreen *screen, VTermPos pos, VTermScreenCell *cell) int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCell *cell)
{ {
ScreenCell *intcell = getcell(screen, pos.row, pos.col); ScreenCell *intcell = getcell(screen, pos.row, pos.col);
int i; if(!intcell)
return 0;
for(i = 0; ; i++) { for(int i = 0; ; i++) {
cell->chars[i] = intcell->chars[i]; cell->chars[i] = intcell->chars[i];
if(!intcell->chars[i]) if(!intcell->chars[i])
break; break;
@@ -589,6 +753,9 @@ void vterm_screen_get_cell(VTermScreen *screen, VTermPos pos, VTermScreenCell *c
cell->attrs.strike = intcell->pen.strike; cell->attrs.strike = intcell->pen.strike;
cell->attrs.font = intcell->pen.font; cell->attrs.font = intcell->pen.font;
cell->attrs.dwl = intcell->pen.dwl;
cell->attrs.dhl = intcell->pen.dhl;
cell->fg = intcell->pen.fg; cell->fg = intcell->pen.fg;
cell->bg = intcell->pen.bg; cell->bg = intcell->pen.bg;
@@ -597,9 +764,42 @@ void vterm_screen_get_cell(VTermScreen *screen, VTermPos pos, VTermScreenCell *c
cell->width = 2; cell->width = 2;
else else
cell->width = 1; cell->width = 1;
return 1;
} }
int vterm_screen_is_eol(VTermScreen *screen, VTermPos pos) /* 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 */ /* This cell is EOL if this and every cell to the right is black */
for(; pos.col < screen->cols; pos.col++) { for(; pos.col < screen->cols; pos.col++) {
@@ -613,11 +813,10 @@ int vterm_screen_is_eol(VTermScreen *screen, VTermPos pos)
VTermScreen *vterm_obtain_screen(VTerm *vt) VTermScreen *vterm_obtain_screen(VTerm *vt)
{ {
VTermScreen *screen;
if(vt->screen) if(vt->screen)
return vt->screen; return vt->screen;
screen = screen_new(vt); VTermScreen *screen = screen_new(vt);
vt->screen = screen; vt->screen = screen;
return screen; return screen;
@@ -662,3 +861,55 @@ void vterm_screen_set_damage_merge(VTermScreen *screen, VTermDamageSize size)
vterm_screen_flush_damage(screen); vterm_screen_flush_damage(screen);
screen->damage_merge = size; 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;
}
File diff suppressed because it is too large Load Diff
@@ -213,7 +213,7 @@ static int mk_wcwidth(wchar_t ucs)
(ucs >= 0x30000 && ucs <= 0x3fffd))); (ucs >= 0x30000 && ucs <= 0x3fffd)));
} }
#if 0
static int mk_wcswidth(const wchar_t *pwcs, size_t n) static int mk_wcswidth(const wchar_t *pwcs, size_t n)
{ {
int w, width = 0; int w, width = 0;
@@ -227,6 +227,7 @@ static int mk_wcswidth(const wchar_t *pwcs, size_t n)
return width; return width;
} }
/* /*
* The following functions are the same as mk_wcwidth() and * The following functions are the same as mk_wcwidth() and
* mk_wcswidth(), except that spacing characters in the East Asian * mk_wcswidth(), except that spacing characters in the East Asian
@@ -316,17 +317,16 @@ static int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n)
return width; return width;
} }
#endif
// ################################ // ################################
// ### The rest added by Paul Evans // ### The rest added by Paul Evans
int vterm_unicode_width(int codepoint) INTERNAL int vterm_unicode_width(int codepoint)
{ {
return mk_wcwidth(codepoint); return mk_wcwidth(codepoint);
} }
int vterm_unicode_is_combining(int codepoint) INTERNAL int vterm_unicode_is_combining(int codepoint)
{ {
return bisearch(codepoint, combining, sizeof(combining) / sizeof(struct interval) - 1); return bisearch(codepoint, combining, sizeof(combining) / sizeof(struct interval) - 1);
} }
+2 -4
View File
@@ -12,15 +12,13 @@ static inline unsigned int utf8_seqlen(long codepoint)
return 6; return 6;
} }
/* Does NOT NUL-terminate the buffer */
static int fill_utf8(long codepoint, char *str) static int fill_utf8(long codepoint, char *str)
{ {
int b;
int nbytes = utf8_seqlen(codepoint); int nbytes = utf8_seqlen(codepoint);
str[nbytes] = 0;
// This is easier done backwards // This is easier done backwards
b = nbytes; int b = nbytes;
while(b > 1) { while(b > 1) {
b--; b--;
str[b] = 0x80 | (codepoint & 0x3f); str[b] = 0x80 | (codepoint & 0x3f);
+44 -27
View File
@@ -70,17 +70,17 @@ void vterm_free(VTerm *vt)
vterm_allocator_free(vt, vt); vterm_allocator_free(vt, vt);
} }
void *vterm_allocator_malloc(VTerm *vt, size_t size) INTERNAL void *vterm_allocator_malloc(VTerm *vt, size_t size)
{ {
return (*vt->allocator->malloc)(size, vt->allocdata); return (*vt->allocator->malloc)(size, vt->allocdata);
} }
void vterm_allocator_free(VTerm *vt, void *ptr) INTERNAL void vterm_allocator_free(VTerm *vt, void *ptr)
{ {
(*vt->allocator->free)(ptr, vt->allocdata); (*vt->allocator->free)(ptr, vt->allocdata);
} }
void vterm_get_size(VTerm *vt, int *rowsp, int *colsp) void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp)
{ {
if(rowsp) if(rowsp)
*rowsp = vt->rows; *rowsp = vt->rows;
@@ -105,10 +105,10 @@ void vterm_set_parser_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks
void vterm_parser_set_utf8(VTerm *vt, int is_utf8) void vterm_parser_set_utf8(VTerm *vt, int is_utf8)
{ {
vt->is_utf8 = is_utf8; vt->mode.utf8 = is_utf8;
} }
void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len) INTERNAL void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len)
{ {
if(len > vt->outbuffer_len - vt->outbuffer_cur) { if(len > vt->outbuffer_len - vt->outbuffer_cur) {
fprintf(stderr, "vterm_push_output(): buffer overflow; truncating output\n"); fprintf(stderr, "vterm_push_output(): buffer overflow; truncating output\n");
@@ -119,7 +119,7 @@ void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len)
vt->outbuffer_cur += len; vt->outbuffer_cur += len;
} }
void vterm_push_output_vsprintf(VTerm *vt, const char *format, va_list args) INTERNAL void vterm_push_output_vsprintf(VTerm *vt, const char *format, va_list args)
{ {
int written = vsnprintf(vt->outbuffer + vt->outbuffer_cur, int written = vsnprintf(vt->outbuffer + vt->outbuffer_cur,
vt->outbuffer_len - vt->outbuffer_cur, vt->outbuffer_len - vt->outbuffer_cur,
@@ -127,7 +127,7 @@ void vterm_push_output_vsprintf(VTerm *vt, const char *format, va_list args)
vt->outbuffer_cur += written; vt->outbuffer_cur += written;
} }
void vterm_push_output_sprintf(VTerm *vt, const char *format, ...) INTERNAL void vterm_push_output_sprintf(VTerm *vt, const char *format, ...)
{ {
va_list args; va_list args;
va_start(args, format); va_start(args, format);
@@ -135,22 +135,50 @@ void vterm_push_output_sprintf(VTerm *vt, const char *format, ...)
va_end(args); va_end(args);
} }
INTERNAL void vterm_push_output_sprintf_ctrl(VTerm *vt, unsigned char ctrl, const char *fmt, ...)
{
if(ctrl >= 0x80 && !vt->mode.ctrl8bit)
vterm_push_output_sprintf(vt, "\e%c", ctrl - 0x40);
else
vterm_push_output_sprintf(vt, "%c", ctrl);
va_list args;
va_start(args, fmt);
vterm_push_output_vsprintf(vt, fmt, args);
va_end(args);
}
INTERNAL 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);
vterm_push_output_sprintf_ctrl(vt, C1_ST, "");
}
size_t vterm_output_bufferlen(VTerm *vt) size_t vterm_output_bufferlen(VTerm *vt)
{ {
return vterm_output_get_buffer_current(vt); return vterm_output_get_buffer_current(vt);
} }
size_t vterm_output_get_buffer_size(VTerm *vt) size_t vterm_output_get_buffer_size(const VTerm *vt)
{ {
return vt->outbuffer_len; return vt->outbuffer_len;
} }
size_t vterm_output_get_buffer_current(VTerm *vt) size_t vterm_output_get_buffer_current(const VTerm *vt)
{ {
return vt->outbuffer_cur; return vt->outbuffer_cur;
} }
size_t vterm_output_get_buffer_remaining(VTerm *vt) size_t vterm_output_get_buffer_remaining(const VTerm *vt)
{ {
return vt->outbuffer_len - vt->outbuffer_cur; return vt->outbuffer_len - vt->outbuffer_cur;
} }
@@ -204,7 +232,7 @@ void vterm_scroll_rect(VTermRect rect,
int downward, int downward,
int rightward, int rightward,
int (*moverect)(VTermRect src, VTermRect dest, void *user), int (*moverect)(VTermRect src, VTermRect dest, void *user),
int (*eraserect)(VTermRect rect, void *user), int (*eraserect)(VTermRect rect, int selective, void *user),
void *user) void *user)
{ {
VTermRect src; VTermRect src;
@@ -213,7 +241,7 @@ void vterm_scroll_rect(VTermRect rect,
if(abs(downward) >= rect.end_row - rect.start_row || if(abs(downward) >= rect.end_row - rect.start_row ||
abs(rightward) >= rect.end_col - rect.start_col) { abs(rightward) >= rect.end_col - rect.start_col) {
/* Scroll more than area; just erase the lot */ /* Scroll more than area; just erase the lot */
(*eraserect)(rect, user); (*eraserect)(rect, 0, user);
return; return;
} }
@@ -266,7 +294,7 @@ void vterm_scroll_rect(VTermRect rect,
else if(rightward < 0) else if(rightward < 0)
rect.end_col = rect.start_col - rightward; rect.end_col = rect.start_col - rightward;
(*eraserect)(rect, user); (*eraserect)(rect, 0, user);
} }
void vterm_copy_cells(VTermRect dest, void vterm_copy_cells(VTermRect dest,
@@ -280,19 +308,12 @@ void vterm_copy_cells(VTermRect dest,
int init_row, test_row, init_col, test_col; int init_row, test_row, init_col, test_col;
int inc_row, inc_col; int inc_row, inc_col;
VTermPos pos;
if(downward < 0) { if(downward < 0) {
init_row = dest.end_row - 1; init_row = dest.end_row - 1;
test_row = dest.start_row - 1; test_row = dest.start_row - 1;
inc_row = -1; inc_row = -1;
} }
else if(downward == 0) { else /* downward >= 0 */ {
init_row = dest.start_row;
test_row = dest.end_row;
inc_row = +1;
}
else /* downward > 0 */ {
init_row = dest.start_row; init_row = dest.start_row;
test_row = dest.end_row; test_row = dest.end_row;
inc_row = +1; inc_row = +1;
@@ -303,17 +324,13 @@ void vterm_copy_cells(VTermRect dest,
test_col = dest.start_col - 1; test_col = dest.start_col - 1;
inc_col = -1; inc_col = -1;
} }
else if(rightward == 0) { else /* rightward >= 0 */ {
init_col = dest.start_col;
test_col = dest.end_col;
inc_col = +1;
}
else /* rightward > 0 */ {
init_col = dest.start_col; init_col = dest.start_col;
test_col = dest.end_col; test_col = dest.end_col;
inc_col = +1; inc_col = +1;
} }
VTermPos pos;
for(pos.row = init_row; pos.row != test_row; pos.row += inc_row) 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) { for(pos.col = init_col; pos.col != test_col; pos.col += inc_col) {
VTermPos srcpos = { pos.row + downward, pos.col + rightward }; VTermPos srcpos = { pos.row + downward, pos.col + rightward };
@@ -5,6 +5,12 @@
#include <stdarg.h> #include <stdarg.h>
#if defined(__GNUC__)
# define INTERNAL __attribute__((visibility("internal")))
#else
# define INTERNAL
#endif
typedef struct VTermEncoding VTermEncoding; typedef struct VTermEncoding VTermEncoding;
typedef struct { typedef struct {
@@ -27,6 +33,11 @@ struct VTermPen
unsigned int font:4; /* To store 0-9 */ 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 struct VTermState
{ {
VTerm *vt; VTerm *vt;
@@ -42,13 +53,21 @@ struct VTermState
int at_phantom; /* True if we're on the "81st" phantom column to defer a wraparound */ int at_phantom; /* True if we're on the "81st" phantom column to defer a wraparound */
int scrollregion_start; int scrollregion_top;
int scrollregion_end; /* -1 means unbounded */ int scrollregion_bottom; /* -1 means unbounded */
#define SCROLLREGION_END(state) ((state)->scrollregion_end > -1 ? (state)->scrollregion_end : (state)->rows) #define SCROLLREGION_BOTTOM(state) ((state)->scrollregion_bottom > -1 ? (state)->scrollregion_bottom : (state)->rows)
int scrollregion_left;
#define SCROLLREGION_LEFT(state) ((state)->mode.leftrightmargin ? (state)->scrollregion_left : 0)
int scrollregion_right; /* -1 means unbounded */
#define SCROLLREGION_RIGHT(state) ((state)->mode.leftrightmargin && (state)->scrollregion_right > -1 ? (state)->scrollregion_right : (state)->cols)
/* Bitvector of tab stops */ /* Bitvector of tab stops */
unsigned char *tabstops; unsigned char *tabstops;
VTermLineInfo *lineinfo;
#define ROWWIDTH(state,row) ((state)->lineinfo[(row)].doublewidth ? ((state)->cols / 2) : (state)->cols)
#define THISROWWIDTH(state) ROWWIDTH(state, (state)->pos.row)
/* Mouse state */ /* Mouse state */
int mouse_col, mouse_row; int mouse_col, mouse_row;
int mouse_buttons; int mouse_buttons;
@@ -72,18 +91,25 @@ struct VTermState
unsigned int cursor_shape:2; unsigned int cursor_shape:2;
int alt_screen:1; int alt_screen:1;
int origin:1; int origin:1;
int screen:1;
int leftrightmargin:1;
} mode; } mode;
VTermEncodingInstance encoding[4], encoding_utf8; VTermEncodingInstance encoding[4], encoding_utf8;
int gl_set, gr_set; int gl_set, gr_set, gsingle_set;
struct VTermPen pen; struct VTermPen pen;
VTermColor default_fg; VTermColor default_fg;
VTermColor default_bg; VTermColor default_bg;
int fg_ansi; VTermColor colors[16]; // Store the 8 ANSI and the 8 ANSI high-brights only
int fg_index;
int bg_index;
int bold_is_highbright; int bold_is_highbright;
unsigned int protected_cell : 1;
/* Saved state under DEC mode 1048/1049 */ /* Saved state under DEC mode 1048/1049 */
struct { struct {
VTermPos pos; VTermPos pos;
@@ -105,7 +131,10 @@ struct VTerm
int rows; int rows;
int cols; int cols;
int is_utf8; struct {
int utf8:1;
int ctrl8bit:1;
} mode;
enum VTermParserState { enum VTermParserState {
NORMAL, NORMAL,
@@ -150,13 +179,26 @@ void vterm_allocator_free(VTerm *vt, void *ptr);
void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len); 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_vsprintf(VTerm *vt, const char *format, va_list args);
void vterm_push_output_sprintf(VTerm *vt, const char *format, ...); 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_free(VTermState *state);
void vterm_state_newpen(VTermState *state);
void vterm_state_resetpen(VTermState *state); void vterm_state_resetpen(VTermState *state);
void vterm_state_setpen(VTermState *state, const long args[], int argcount); 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); void vterm_state_savepen(VTermState *state, int save);
enum {
C1_SS3 = 0x8f,
C1_DCS = 0x90,
C1_CSI = 0x9b,
C1_ST = 0x9c,
};
void vterm_state_push_output_sprintf_CSI(VTermState *vts, const char *format, ...);
void vterm_screen_free(VTermScreen *screen); void vterm_screen_free(VTermScreen *screen);
VTermEncoding *vterm_lookup_encoding(VTermEncodingType type, char designation); VTermEncoding *vterm_lookup_encoding(VTermEncodingType type, char designation);