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