Integrate KodoTerm for SSH terminal sessions

This commit is contained in:
Keith Smith
2026-03-01 10:36:06 -07:00
parent c3369b8e48
commit 776ddc1a53
503 changed files with 22870 additions and 95 deletions

645
third_party/libvterm/include/vterm.h vendored Normal file
View File

@@ -0,0 +1,645 @@
#ifndef __VTERM_H__
#define __VTERM_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include "vterm_keycodes.h"
#define VTERM_VERSION_MAJOR 0
#define VTERM_VERSION_MINOR 3
#define VTERM_VERSION_PATCH 3
#define VTERM_CHECK_VERSION \
vterm_check_version(VTERM_VERSION_MAJOR, VTERM_VERSION_MINOR)
/* Any cell can contain at most one basic printing character and 5 combining
* characters. This number could be changed but will be ABI-incompatible if
* you do */
#define VTERM_MAX_CHARS_PER_CELL 6
typedef struct VTerm VTerm;
typedef struct VTermState VTermState;
typedef struct VTermScreen VTermScreen;
typedef struct {
int row;
int col;
} VTermPos;
/* some small utility functions; we can just keep these static here */
/* order points by on-screen flow order */
static inline int vterm_pos_cmp(VTermPos a, VTermPos b)
{
return (a.row == b.row) ? a.col - b.col : a.row - b.row;
}
typedef struct {
int start_row;
int end_row;
int start_col;
int end_col;
} VTermRect;
/* true if the rect contains the point */
static inline int vterm_rect_contains(VTermRect r, VTermPos p)
{
return p.row >= r.start_row && p.row < r.end_row &&
p.col >= r.start_col && p.col < r.end_col;
}
/* move a rect */
static inline void vterm_rect_move(VTermRect *rect, int row_delta, int col_delta)
{
rect->start_row += row_delta; rect->end_row += row_delta;
rect->start_col += col_delta; rect->end_col += col_delta;
}
/**
* Bit-field describing the content of the tagged union `VTermColor`.
*/
typedef enum {
/**
* If the lower bit of `type` is not set, the colour is 24-bit RGB.
*/
VTERM_COLOR_RGB = 0x00,
/**
* The colour is an index into a palette of 256 colours.
*/
VTERM_COLOR_INDEXED = 0x01,
/**
* Mask that can be used to extract the RGB/Indexed bit.
*/
VTERM_COLOR_TYPE_MASK = 0x01,
/**
* If set, indicates that this colour should be the default foreground
* color, i.e. there was no SGR request for another colour. When
* rendering this colour it is possible to ignore "idx" and just use a
* colour that is not in the palette.
*/
VTERM_COLOR_DEFAULT_FG = 0x02,
/**
* If set, indicates that this colour should be the default background
* color, i.e. there was no SGR request for another colour. A common
* option when rendering this colour is to not render a background at
* all, for example by rendering the window transparently at this spot.
*/
VTERM_COLOR_DEFAULT_BG = 0x04,
/**
* Mask that can be used to extract the default foreground/background bit.
*/
VTERM_COLOR_DEFAULT_MASK = 0x06
} VTermColorType;
/**
* Returns true if the VTERM_COLOR_RGB `type` flag is set, indicating that the
* given VTermColor instance is an indexed colour.
*/
#define VTERM_COLOR_IS_INDEXED(col) \
(((col)->type & VTERM_COLOR_TYPE_MASK) == VTERM_COLOR_INDEXED)
/**
* Returns true if the VTERM_COLOR_INDEXED `type` flag is set, indicating that
* the given VTermColor instance is an rgb colour.
*/
#define VTERM_COLOR_IS_RGB(col) \
(((col)->type & VTERM_COLOR_TYPE_MASK) == VTERM_COLOR_RGB)
/**
* Returns true if the VTERM_COLOR_DEFAULT_FG `type` flag is set, indicating
* that the given VTermColor instance corresponds to the default foreground
* color.
*/
#define VTERM_COLOR_IS_DEFAULT_FG(col) \
(!!((col)->type & VTERM_COLOR_DEFAULT_FG))
/**
* Returns true if the VTERM_COLOR_DEFAULT_BG `type` flag is set, indicating
* that the given VTermColor instance corresponds to the default background
* color.
*/
#define VTERM_COLOR_IS_DEFAULT_BG(col) \
(!!((col)->type & VTERM_COLOR_DEFAULT_BG))
/**
* Tagged union storing either an RGB color or an index into a colour palette.
* In order to convert indexed colours to RGB, you may use the
* vterm_state_convert_color_to_rgb() or vterm_screen_convert_color_to_rgb()
* functions which lookup the RGB colour from the palette maintained by a
* VTermState or VTermScreen instance.
*/
typedef union {
/**
* Tag indicating which union member is actually valid. This variable
* coincides with the `type` member of the `rgb` and the `indexed` struct
* in memory. Please use the `VTERM_COLOR_IS_*` test macros to check whether
* a particular type flag is set.
*/
uint8_t type;
/**
* Valid if `VTERM_COLOR_IS_RGB(type)` is true. Holds the RGB colour values.
*/
struct {
/**
* Same as the top-level `type` member stored in VTermColor.
*/
uint8_t type;
/**
* The actual 8-bit red, green, blue colour values.
*/
uint8_t red, green, blue;
} rgb;
/**
* If `VTERM_COLOR_IS_INDEXED(type)` is true, this member holds the index into
* the colour palette.
*/
struct {
/**
* Same as the top-level `type` member stored in VTermColor.
*/
uint8_t type;
/**
* Index into the colour map.
*/
uint8_t idx;
} indexed;
} VTermColor;
/**
* Constructs a new VTermColor instance representing the given RGB values.
*/
static inline void vterm_color_rgb(VTermColor *col, uint8_t red, uint8_t green,
uint8_t blue)
{
col->type = VTERM_COLOR_RGB;
col->rgb.red = red;
col->rgb.green = green;
col->rgb.blue = blue;
}
/**
* Construct a new VTermColor instance representing an indexed color with the
* given index.
*/
static inline void vterm_color_indexed(VTermColor *col, uint8_t idx)
{
col->type = VTERM_COLOR_INDEXED;
col->indexed.idx = idx;
}
/**
* Compares two colours. Returns true if the colors are equal, false otherwise.
*/
int vterm_color_is_equal(const VTermColor *a, const VTermColor *b);
typedef enum {
/* VTERM_VALUETYPE_NONE = 0 */
VTERM_VALUETYPE_BOOL = 1,
VTERM_VALUETYPE_INT,
VTERM_VALUETYPE_STRING,
VTERM_VALUETYPE_COLOR,
VTERM_N_VALUETYPES
} VTermValueType;
typedef struct {
const char *str;
size_t len : 30;
bool initial : 1;
bool final : 1;
} VTermStringFragment;
typedef union {
int boolean;
int number;
VTermStringFragment string;
VTermColor color;
} VTermValue;
typedef enum {
/* VTERM_ATTR_NONE = 0 */
VTERM_ATTR_BOLD = 1, // bool: 1, 22
VTERM_ATTR_UNDERLINE, // number: 4, 21, 24
VTERM_ATTR_ITALIC, // bool: 3, 23
VTERM_ATTR_BLINK, // bool: 5, 25
VTERM_ATTR_REVERSE, // bool: 7, 27
VTERM_ATTR_CONCEAL, // bool: 8, 28
VTERM_ATTR_STRIKE, // bool: 9, 29
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;
typedef enum {
/* VTERM_PROP_NONE = 0 */
VTERM_PROP_CURSORVISIBLE = 1, // bool
VTERM_PROP_CURSORBLINK, // bool
VTERM_PROP_ALTSCREEN, // bool
VTERM_PROP_TITLE, // string
VTERM_PROP_ICONNAME, // string
VTERM_PROP_REVERSE, // bool
VTERM_PROP_CURSORSHAPE, // number
VTERM_PROP_MOUSE, // number
VTERM_PROP_FOCUSREPORT, // bool
VTERM_N_PROPS
} VTermProp;
enum {
VTERM_PROP_CURSORSHAPE_BLOCK = 1,
VTERM_PROP_CURSORSHAPE_UNDERLINE,
VTERM_PROP_CURSORSHAPE_BAR_LEFT,
VTERM_N_PROP_CURSORSHAPES
};
enum {
VTERM_PROP_MOUSE_NONE = 0,
VTERM_PROP_MOUSE_CLICK,
VTERM_PROP_MOUSE_DRAG,
VTERM_PROP_MOUSE_MOVE,
VTERM_N_PROP_MOUSES
};
typedef enum {
VTERM_SELECTION_CLIPBOARD = (1<<0),
VTERM_SELECTION_PRIMARY = (1<<1),
VTERM_SELECTION_SECONDARY = (1<<2),
VTERM_SELECTION_SELECT = (1<<3),
VTERM_SELECTION_CUT0 = (1<<4), /* also CUT1 .. CUT7 by bitshifting */
} VTermSelectionMask;
typedef struct {
const uint32_t *chars;
int width;
unsigned int protected_cell:1; /* DECSCA-protected against DECSEL/DECSED */
unsigned int dwl:1; /* DECDWL or DECDHL double-width line */
unsigned int dhl:2; /* DECDHL double-height line (1=top 2=bottom) */
} VTermGlyphInfo;
typedef struct {
unsigned int doublewidth:1; /* DECDWL or DECDHL line */
unsigned int doubleheight:2; /* DECDHL line (1=top 2=bottom) */
unsigned int continuation:1; /* Line is a flow continuation of the previous */
} VTermLineInfo;
/* Copies of VTermState fields that the 'resize' callback might have reason to
* edit. 'resize' callback gets total control of these fields and may
* free-and-reallocate them if required. They will be copied back from the
* struct after the callback has returned.
*/
typedef struct {
VTermPos pos; /* current cursor position */
VTermLineInfo *lineinfos[2]; /* [1] may be NULL */
} VTermStateFields;
typedef struct {
/* libvterm relies on this memory to be zeroed out before it is returned
* by the allocator. */
void *(*malloc)(size_t size, void *allocdata);
void (*free)(void *ptr, void *allocdata);
} VTermAllocatorFunctions;
void vterm_check_version(int major, int minor);
struct VTermBuilder {
int ver; /* currently unused but reserved for some sort of ABI version flag */
int rows, cols;
const VTermAllocatorFunctions *allocator;
void *allocdata;
/* Override default sizes for various structures */
size_t outbuffer_len; /* default: 4096 */
size_t tmpbuffer_len; /* default: 4096 */
};
VTerm *vterm_build(const struct VTermBuilder *builder);
/* A convenient shortcut for default cases */
VTerm *vterm_new(int rows, int cols);
/* This shortcuts are generally discouraged in favour of just using vterm_build() */
VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *funcs, void *allocdata);
void vterm_free(VTerm* vt);
void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp);
void vterm_set_size(VTerm *vt, int rows, int cols);
int vterm_get_utf8(const VTerm *vt);
void vterm_set_utf8(VTerm *vt, int is_utf8);
size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len);
/* Setting output callback will override the buffer logic */
typedef void VTermOutputCallback(const char *s, size_t len, void *user);
void vterm_output_set_callback(VTerm *vt, VTermOutputCallback *func, void *user);
/* These buffer functions only work if output callback is NOT set
* These are deprecated and will be removed in a later version */
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);
/* This too */
size_t vterm_output_read(VTerm *vt, char *buffer, size_t len);
void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod);
void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod);
void vterm_keyboard_start_paste(VTerm *vt);
void vterm_keyboard_end_paste(VTerm *vt);
void vterm_mouse_move(VTerm *vt, int row, int col, VTermModifier mod);
void vterm_mouse_button(VTerm *vt, int button, bool pressed, VTermModifier mod);
// ------------
// 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 (1U<<31)
#define CSI_ARG_MASK (~(1U<<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)(int command, VTermStringFragment frag, void *user);
int (*dcs)(const char *command, size_t commandlen, VTermStringFragment frag, void *user);
int (*apc)(VTermStringFragment frag, void *user);
int (*pm)(VTermStringFragment frag, void *user);
int (*sos)(VTermStringFragment frag, void *user);
int (*resize)(int rows, int cols, void *user);
} VTermParserCallbacks;
void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user);
void *vterm_parser_get_cbdata(VTerm *vt);
/* Normally NUL, CAN, SUB and DEL are ignored. Setting this true causes them
* to be emitted by the 'control' callback
*/
void vterm_parser_set_emit_nul(VTerm *vt, bool emit);
// -----------
// 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 (*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);
// ABI-compat only enabled if vterm_state_callbacks_has_premove() is invoked
int (*premove)(VTermRect dest, void *user);
} VTermStateCallbacks;
typedef struct {
int (*control)(unsigned char control, void *user);
int (*csi)(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user);
int (*osc)(int command, VTermStringFragment frag, void *user);
int (*dcs)(const char *command, size_t commandlen, VTermStringFragment frag, void *user);
int (*apc)(VTermStringFragment frag, void *user);
int (*pm)(VTermStringFragment frag, void *user);
int (*sos)(VTermStringFragment frag, void *user);
} VTermStateFallbacks;
typedef struct {
int (*set)(VTermSelectionMask mask, VTermStringFragment frag, void *user);
int (*query)(VTermSelectionMask mask, void *user);
} VTermSelectionCallbacks;
VTermState *vterm_obtain_state(VTerm *vt);
void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user);
void *vterm_state_get_cbdata(VTermState *state);
void vterm_state_callbacks_has_premove(VTermState *state);
void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermStateFallbacks *fallbacks, void *user);
void *vterm_state_get_unrecognised_fbdata(VTermState *state);
void vterm_state_reset(VTermState *state, int hard);
void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos);
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);
int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val);
int vterm_state_set_termprop(VTermState *state, VTermProp prop, VTermValue *val);
void vterm_state_focus_in(VTermState *state);
void vterm_state_focus_out(VTermState *state);
const VTermLineInfo *vterm_state_get_lineinfo(const VTermState *state, int row);
/**
* Makes sure that the given color `col` is indeed an RGB colour. After this
* function returns, VTERM_COLOR_IS_RGB(col) will return true, while all other
* flags stored in `col->type` will have been reset.
*
* @param state is the VTermState instance from which the colour palette should
* be extracted.
* @param col is a pointer at the VTermColor instance that should be converted
* to an RGB colour.
*/
void vterm_state_convert_color_to_rgb(const VTermState *state, VTermColor *col);
void vterm_state_set_selection_callbacks(VTermState *state, const VTermSelectionCallbacks *callbacks, void *user,
char *buffer, size_t buflen);
void vterm_state_send_selection(VTermState *state, VTermSelectionMask mask, VTermStringFragment frag);
// ------------
// Screen layer
// ------------
typedef struct {
unsigned int bold : 1;
unsigned int underline : 2;
unsigned int italic : 1;
unsigned int blink : 1;
unsigned int reverse : 1;
unsigned int conceal : 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) */
unsigned int small : 1;
unsigned int baseline : 2;
} VTermScreenCellAttrs;
enum {
VTERM_UNDERLINE_OFF,
VTERM_UNDERLINE_SINGLE,
VTERM_UNDERLINE_DOUBLE,
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;
VTermScreenCellAttrs 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 (*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);
int (*sb_clear)(void* user);
/* ABI-compat this is only used if vterm_screen_callbacks_has_pushline4() is called */
int (*sb_pushline4)(int cols, const VTermScreenCell *cells, bool continuation, void *user);
} VTermScreenCallbacks;
VTermScreen *vterm_obtain_screen(VTerm *vt);
void vterm_screen_set_callbacks(VTermScreen *screen, const VTermScreenCallbacks *callbacks, void *user);
void *vterm_screen_get_cbdata(VTermScreen *screen);
void vterm_screen_callbacks_has_pushline4(VTermScreen *screen);
void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermStateFallbacks *fallbacks, void *user);
void *vterm_screen_get_unrecognised_fbdata(VTermScreen *screen);
void vterm_screen_enable_reflow(VTermScreen *screen, bool reflow);
// Back-compat alias for the brief time it was in 0.3-RC1
#define vterm_screen_set_reflow vterm_screen_enable_reflow
void vterm_screen_enable_altscreen(VTermScreen *screen, int altscreen);
typedef enum {
VTERM_DAMAGE_CELL, /* every cell */
VTERM_DAMAGE_ROW, /* entire rows */
VTERM_DAMAGE_SCREEN, /* entire screen */
VTERM_DAMAGE_SCROLL, /* entire screen + scrollrect */
VTERM_N_DAMAGES
} VTermDamageSize;
void vterm_screen_flush_damage(VTermScreen *screen);
void vterm_screen_set_damage_merge(VTermScreen *screen, VTermDamageSize size);
void vterm_screen_reset(VTermScreen *screen, int hard);
/* Neither of these functions NUL-terminate the buffer */
size_t vterm_screen_get_chars(const VTermScreen *screen, uint32_t *chars, size_t len, const VTermRect rect);
size_t vterm_screen_get_text(const VTermScreen *screen, char *str, size_t len, const VTermRect rect);
typedef 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,
VTERM_ATTR_CONCEAL_MASK = 1 << 9,
VTERM_ATTR_SMALL_MASK = 1 << 10,
VTERM_ATTR_BASELINE_MASK = 1 << 11,
VTERM_ALL_ATTRS_MASK = (1 << 12) - 1
} VTermAttrMask;
int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs);
int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCell *cell);
int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos);
/**
* Same as vterm_state_convert_color_to_rgb(), but takes a `screen` instead of a `state`
* instance.
*/
void vterm_screen_convert_color_to_rgb(const VTermScreen *screen, VTermColor *col);
/**
* Similar to vterm_state_set_default_colors(), but also resets colours in the
* screen buffer(s)
*/
void vterm_screen_set_default_colors(VTermScreen *screen, const VTermColor *default_fg, const VTermColor *default_bg);
// ---------
// Utilities
// ---------
VTermValueType vterm_get_attr_type(VTermAttr attr);
VTermValueType vterm_get_prop_type(VTermProp prop);
void vterm_scroll_rect(VTermRect rect,
int downward,
int rightward,
int (*moverect)(VTermRect src, VTermRect dest, void *user),
int (*eraserect)(VTermRect rect, int selective, void *user),
void *user);
void vterm_copy_cells(VTermRect dest,
VTermRect src,
void (*copycell)(VTermPos dest, VTermPos src, void *user),
void *user);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,61 @@
#ifndef __VTERM_INPUT_H__
#define __VTERM_INPUT_H__
typedef enum {
VTERM_MOD_NONE = 0x00,
VTERM_MOD_SHIFT = 0x01,
VTERM_MOD_ALT = 0x02,
VTERM_MOD_CTRL = 0x04,
VTERM_ALL_MODS_MASK = 0x07
} VTermModifier;
typedef enum {
VTERM_KEY_NONE,
VTERM_KEY_ENTER,
VTERM_KEY_TAB,
VTERM_KEY_BACKSPACE,
VTERM_KEY_ESCAPE,
VTERM_KEY_UP,
VTERM_KEY_DOWN,
VTERM_KEY_LEFT,
VTERM_KEY_RIGHT,
VTERM_KEY_INS,
VTERM_KEY_DEL,
VTERM_KEY_HOME,
VTERM_KEY_END,
VTERM_KEY_PAGEUP,
VTERM_KEY_PAGEDOWN,
VTERM_KEY_FUNCTION_0 = 256,
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_N_KEYS = VTERM_KEY_MAX
} VTermKey;
#define VTERM_KEY_FUNCTION(n) (VTERM_KEY_FUNCTION_0+(n))
#endif

230
third_party/libvterm/src/encoding.c vendored Normal file
View File

@@ -0,0 +1,230 @@
#include "vterm_internal.h"
#define UNICODE_INVALID 0xFFFD
#if defined(DEBUG) && DEBUG > 1
# define DEBUG_PRINT_UTF8
#endif
struct UTF8DecoderData {
// number of bytes remaining in this codepoint
int bytes_remaining;
// number of bytes total in this codepoint once it's finished
// (for detecting overlongs)
int bytes_total;
int this_cp;
};
static void init_utf8(VTermEncoding *enc, void *data_)
{
struct UTF8DecoderData *data = data_;
data->bytes_remaining = 0;
data->bytes_total = 0;
}
static void decode_utf8(VTermEncoding *enc, void *data_,
uint32_t cp[], int *cpi, int cplen,
const char bytes[], size_t *pos, size_t bytelen)
{
struct UTF8DecoderData *data = data_;
#ifdef DEBUG_PRINT_UTF8
printf("BEGIN UTF-8\n");
#endif
for(; *pos < bytelen && *cpi < cplen; (*pos)++) {
unsigned char c = bytes[*pos];
#ifdef DEBUG_PRINT_UTF8
printf(" pos=%zd c=%02x rem=%d\n", *pos, c, data->bytes_remaining);
#endif
if(c < 0x20) // C0
return;
else if(c >= 0x20 && c < 0x7f) {
if(data->bytes_remaining)
cp[(*cpi)++] = UNICODE_INVALID;
cp[(*cpi)++] = c;
#ifdef DEBUG_PRINT_UTF8
printf(" UTF-8 char: U+%04x\n", c);
#endif
data->bytes_remaining = 0;
}
else if(c == 0x7f) // DEL
return;
else if(c >= 0x80 && c < 0xc0) {
if(!data->bytes_remaining) {
cp[(*cpi)++] = UNICODE_INVALID;
continue;
}
data->this_cp <<= 6;
data->this_cp |= c & 0x3f;
data->bytes_remaining--;
if(!data->bytes_remaining) {
#ifdef DEBUG_PRINT_UTF8
printf(" UTF-8 raw char U+%04x bytelen=%d ", data->this_cp, data->bytes_total);
#endif
// Check for overlong sequences
switch(data->bytes_total) {
case 2:
if(data->this_cp < 0x0080) data->this_cp = UNICODE_INVALID;
break;
case 3:
if(data->this_cp < 0x0800) data->this_cp = UNICODE_INVALID;
break;
case 4:
if(data->this_cp < 0x10000) data->this_cp = UNICODE_INVALID;
break;
case 5:
if(data->this_cp < 0x200000) data->this_cp = UNICODE_INVALID;
break;
case 6:
if(data->this_cp < 0x4000000) data->this_cp = UNICODE_INVALID;
break;
}
// Now look for plain invalid ones
if((data->this_cp >= 0xD800 && data->this_cp <= 0xDFFF) ||
data->this_cp == 0xFFFE ||
data->this_cp == 0xFFFF)
data->this_cp = UNICODE_INVALID;
#ifdef DEBUG_PRINT_UTF8
printf(" char: U+%04x\n", data->this_cp);
#endif
cp[(*cpi)++] = data->this_cp;
}
}
else if(c >= 0xc0 && c < 0xe0) {
if(data->bytes_remaining)
cp[(*cpi)++] = UNICODE_INVALID;
data->this_cp = c & 0x1f;
data->bytes_total = 2;
data->bytes_remaining = 1;
}
else if(c >= 0xe0 && c < 0xf0) {
if(data->bytes_remaining)
cp[(*cpi)++] = UNICODE_INVALID;
data->this_cp = c & 0x0f;
data->bytes_total = 3;
data->bytes_remaining = 2;
}
else if(c >= 0xf0 && c < 0xf8) {
if(data->bytes_remaining)
cp[(*cpi)++] = UNICODE_INVALID;
data->this_cp = c & 0x07;
data->bytes_total = 4;
data->bytes_remaining = 3;
}
else if(c >= 0xf8 && c < 0xfc) {
if(data->bytes_remaining)
cp[(*cpi)++] = UNICODE_INVALID;
data->this_cp = c & 0x03;
data->bytes_total = 5;
data->bytes_remaining = 4;
}
else if(c >= 0xfc && c < 0xfe) {
if(data->bytes_remaining)
cp[(*cpi)++] = UNICODE_INVALID;
data->this_cp = c & 0x01;
data->bytes_total = 6;
data->bytes_remaining = 5;
}
else {
cp[(*cpi)++] = UNICODE_INVALID;
}
}
}
static VTermEncoding encoding_utf8 = {
.init = &init_utf8,
.decode = &decode_utf8,
};
static void decode_usascii(VTermEncoding *enc, void *data,
uint32_t cp[], int *cpi, int cplen,
const char bytes[], size_t *pos, size_t bytelen)
{
int is_gr = bytes[*pos] & 0x80;
for(; *pos < bytelen && *cpi < cplen; (*pos)++) {
unsigned char c = bytes[*pos] ^ is_gr;
if(c < 0x20 || c == 0x7f || c >= 0x80)
return;
cp[(*cpi)++] = c;
}
}
static VTermEncoding encoding_usascii = {
.decode = &decode_usascii,
};
struct StaticTableEncoding {
const VTermEncoding enc;
const uint32_t chars[128];
};
static void decode_table(VTermEncoding *enc, void *data,
uint32_t cp[], int *cpi, int cplen,
const char bytes[], size_t *pos, size_t bytelen)
{
struct StaticTableEncoding *table = (struct StaticTableEncoding *)enc;
int is_gr = bytes[*pos] & 0x80;
for(; *pos < bytelen && *cpi < cplen; (*pos)++) {
unsigned char c = bytes[*pos] ^ is_gr;
if(c < 0x20 || c == 0x7f || c >= 0x80)
return;
if(table->chars[c])
cp[(*cpi)++] = table->chars[c];
else
cp[(*cpi)++] = c;
}
}
#include "encoding/DECdrawing.inc"
#include "encoding/uk.inc"
static struct {
VTermEncodingType type;
char designation;
VTermEncoding *enc;
}
encodings[] = {
{ ENC_UTF8, 'u', &encoding_utf8 },
{ ENC_SINGLE_94, '0', (VTermEncoding*)&encoding_DECdrawing },
{ ENC_SINGLE_94, 'A', (VTermEncoding*)&encoding_uk },
{ ENC_SINGLE_94, 'B', &encoding_usascii },
{ 0 },
};
/* This ought to be INTERNAL but isn't because it's used by unit testing */
VTermEncoding *vterm_lookup_encoding(VTermEncodingType type, char designation)
{
for(int i = 0; encodings[i].designation; i++)
if(encodings[i].type == type && encodings[i].designation == designation)
return encodings[i].enc;
return NULL;
}

View File

@@ -0,0 +1,36 @@
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,
}
};

View File

@@ -0,0 +1,31 @@
6/0 = U+25C6 # BLACK DIAMOND
6/1 = U+2592 # MEDIUM SHADE (checkerboard)
6/2 = U+2409 # SYMBOL FOR HORIZONTAL TAB
6/3 = U+240C # SYMBOL FOR FORM FEED
6/4 = U+240D # SYMBOL FOR CARRIAGE RETURN
6/5 = U+240A # SYMBOL FOR LINE FEED
6/6 = U+00B0 # DEGREE SIGN
6/7 = U+00B1 # PLUS-MINUS SIGN (plus or minus)
6/8 = U+2424 # SYMBOL FOR NEW LINE
6/9 = U+240B # SYMBOL FOR VERTICAL TAB
6/10 = U+2518 # BOX DRAWINGS LIGHT UP AND LEFT (bottom-right corner)
6/11 = U+2510 # BOX DRAWINGS LIGHT DOWN AND LEFT (top-right corner)
6/12 = U+250C # BOX DRAWINGS LIGHT DOWN AND RIGHT (top-left corner)
6/13 = U+2514 # BOX DRAWINGS LIGHT UP AND RIGHT (bottom-left corner)
6/14 = U+253C # BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL (crossing lines)
6/15 = U+23BA # HORIZONTAL SCAN LINE-1
7/0 = U+23BB # HORIZONTAL SCAN LINE-3
7/1 = U+2500 # BOX DRAWINGS LIGHT HORIZONTAL
7/2 = U+23BC # HORIZONTAL SCAN LINE-7
7/3 = U+23BD # HORIZONTAL SCAN LINE-9
7/4 = U+251C # BOX DRAWINGS LIGHT VERTICAL AND RIGHT
7/5 = U+2524 # BOX DRAWINGS LIGHT VERTICAL AND LEFT
7/6 = U+2534 # BOX DRAWINGS LIGHT UP AND HORIZONTAL
7/7 = U+252C # BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
7/8 = U+2502 # BOX DRAWINGS LIGHT VERTICAL
7/9 = U+2A7D # LESS-THAN OR SLANTED EQUAL-TO
7/10 = U+2A7E # GREATER-THAN OR SLANTED EQUAL-TO
7/11 = U+03C0 # GREEK SMALL LETTER PI
7/12 = U+2260 # NOT EQUAL TO
7/13 = U+00A3 # POUND SIGN
7/14 = U+00B7 # MIDDLE DOT

View File

@@ -0,0 +1,6 @@
static const struct StaticTableEncoding encoding_uk = {
{ .decode = &decode_table },
{
[0x23] = 0x00a3,
}
};

View File

@@ -0,0 +1 @@
2/3 = "£"

111
third_party/libvterm/src/fullwidth.inc vendored Normal file
View File

@@ -0,0 +1,111 @@
{ 0x1100, 0x115f },
{ 0x231a, 0x231b },
{ 0x2329, 0x232a },
{ 0x23e9, 0x23ec },
{ 0x23f0, 0x23f0 },
{ 0x23f3, 0x23f3 },
{ 0x25fd, 0x25fe },
{ 0x2614, 0x2615 },
{ 0x2648, 0x2653 },
{ 0x267f, 0x267f },
{ 0x2693, 0x2693 },
{ 0x26a1, 0x26a1 },
{ 0x26aa, 0x26ab },
{ 0x26bd, 0x26be },
{ 0x26c4, 0x26c5 },
{ 0x26ce, 0x26ce },
{ 0x26d4, 0x26d4 },
{ 0x26ea, 0x26ea },
{ 0x26f2, 0x26f3 },
{ 0x26f5, 0x26f5 },
{ 0x26fa, 0x26fa },
{ 0x26fd, 0x26fd },
{ 0x2705, 0x2705 },
{ 0x270a, 0x270b },
{ 0x2728, 0x2728 },
{ 0x274c, 0x274c },
{ 0x274e, 0x274e },
{ 0x2753, 0x2755 },
{ 0x2757, 0x2757 },
{ 0x2795, 0x2797 },
{ 0x27b0, 0x27b0 },
{ 0x27bf, 0x27bf },
{ 0x2b1b, 0x2b1c },
{ 0x2b50, 0x2b50 },
{ 0x2b55, 0x2b55 },
{ 0x2e80, 0x2e99 },
{ 0x2e9b, 0x2ef3 },
{ 0x2f00, 0x2fd5 },
{ 0x2ff0, 0x2ffb },
{ 0x3000, 0x303e },
{ 0x3041, 0x3096 },
{ 0x3099, 0x30ff },
{ 0x3105, 0x312f },
{ 0x3131, 0x318e },
{ 0x3190, 0x31ba },
{ 0x31c0, 0x31e3 },
{ 0x31f0, 0x321e },
{ 0x3220, 0x3247 },
{ 0x3250, 0x4dbf },
{ 0x4e00, 0xa48c },
{ 0xa490, 0xa4c6 },
{ 0xa960, 0xa97c },
{ 0xac00, 0xd7a3 },
{ 0xf900, 0xfaff },
{ 0xfe10, 0xfe19 },
{ 0xfe30, 0xfe52 },
{ 0xfe54, 0xfe66 },
{ 0xfe68, 0xfe6b },
{ 0xff01, 0xff60 },
{ 0xffe0, 0xffe6 },
{ 0x16fe0, 0x16fe3 },
{ 0x17000, 0x187f7 },
{ 0x18800, 0x18af2 },
{ 0x1b000, 0x1b11e },
{ 0x1b150, 0x1b152 },
{ 0x1b164, 0x1b167 },
{ 0x1b170, 0x1b2fb },
{ 0x1f004, 0x1f004 },
{ 0x1f0cf, 0x1f0cf },
{ 0x1f18e, 0x1f18e },
{ 0x1f191, 0x1f19a },
{ 0x1f200, 0x1f202 },
{ 0x1f210, 0x1f23b },
{ 0x1f240, 0x1f248 },
{ 0x1f250, 0x1f251 },
{ 0x1f260, 0x1f265 },
{ 0x1f300, 0x1f320 },
{ 0x1f32d, 0x1f335 },
{ 0x1f337, 0x1f37c },
{ 0x1f37e, 0x1f393 },
{ 0x1f3a0, 0x1f3ca },
{ 0x1f3cf, 0x1f3d3 },
{ 0x1f3e0, 0x1f3f0 },
{ 0x1f3f4, 0x1f3f4 },
{ 0x1f3f8, 0x1f43e },
{ 0x1f440, 0x1f440 },
{ 0x1f442, 0x1f4fc },
{ 0x1f4ff, 0x1f53d },
{ 0x1f54b, 0x1f54e },
{ 0x1f550, 0x1f567 },
{ 0x1f57a, 0x1f57a },
{ 0x1f595, 0x1f596 },
{ 0x1f5a4, 0x1f5a4 },
{ 0x1f5fb, 0x1f64f },
{ 0x1f680, 0x1f6c5 },
{ 0x1f6cc, 0x1f6cc },
{ 0x1f6d0, 0x1f6d2 },
{ 0x1f6d5, 0x1f6d5 },
{ 0x1f6eb, 0x1f6ec },
{ 0x1f6f4, 0x1f6fa },
{ 0x1f7e0, 0x1f7eb },
{ 0x1f90d, 0x1f971 },
{ 0x1f973, 0x1f976 },
{ 0x1f97a, 0x1f9a2 },
{ 0x1f9a5, 0x1f9aa },
{ 0x1f9ae, 0x1f9ca },
{ 0x1f9cd, 0x1f9ff },
{ 0x1fa70, 0x1fa73 },
{ 0x1fa78, 0x1fa7a },
{ 0x1fa80, 0x1fa82 },
{ 0x1fa90, 0x1fa95 },

226
third_party/libvterm/src/keyboard.c vendored Normal file
View File

@@ -0,0 +1,226 @@
#include "vterm_internal.h"
#include <stdio.h>
#include "utf8.h"
void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod)
{
/* The shift modifier is never important for Unicode characters
* apart from Space
*/
if(c != ' ')
mod &= ~VTERM_MOD_SHIFT;
if(mod == 0) {
// Normal text - ignore just shift
char str[6];
int seqlen = fill_utf8(c, str);
vterm_push_output_bytes(vt, str, seqlen);
return;
}
int needs_CSIu;
switch(c) {
/* Special Ctrl- letters that can't be represented elsewise */
case 'i': case 'j': case 'm': case '[':
needs_CSIu = 1;
break;
/* Ctrl-\ ] ^ _ don't need CSUu */
case '\\': case ']': case '^': case '_':
needs_CSIu = 0;
break;
/* Shift-space needs CSIu */
case ' ':
needs_CSIu = !!(mod & VTERM_MOD_SHIFT);
break;
/* All other characters needs CSIu except for letters a-z */
default:
needs_CSIu = (c < 'a' || c > 'z');
}
/* ALT we can just prefix with ESC; anything else requires CSI u */
if(needs_CSIu && (mod & ~VTERM_MOD_ALT)) {
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", c, mod+1);
return;
}
if(mod & VTERM_MOD_CTRL)
c &= 0x1f;
vterm_push_output_sprintf(vt, "%s%c", mod & VTERM_MOD_ALT ? ESC_S : "", c);
}
typedef struct {
enum {
KEYCODE_NONE,
KEYCODE_LITERAL,
KEYCODE_TAB,
KEYCODE_ENTER,
KEYCODE_SS3,
KEYCODE_CSI,
KEYCODE_CSI_CURSOR,
KEYCODE_CSINUM,
KEYCODE_KEYPAD,
} type;
char literal;
int csinum;
} keycodes_s;
static keycodes_s keycodes[] = {
{ KEYCODE_NONE }, // NONE
{ KEYCODE_ENTER, '\r' }, // ENTER
{ KEYCODE_TAB, '\t' }, // TAB
{ KEYCODE_LITERAL, '\x7f' }, // BACKSPACE == ASCII DEL
{ KEYCODE_LITERAL, '\x1b' }, // ESCAPE
{ KEYCODE_CSI_CURSOR, 'A' }, // UP
{ KEYCODE_CSI_CURSOR, 'B' }, // DOWN
{ KEYCODE_CSI_CURSOR, 'D' }, // LEFT
{ KEYCODE_CSI_CURSOR, 'C' }, // RIGHT
{ KEYCODE_CSINUM, '~', 2 }, // INS
{ KEYCODE_CSINUM, '~', 3 }, // DEL
{ KEYCODE_CSI_CURSOR, 'H' }, // HOME
{ KEYCODE_CSI_CURSOR, 'F' }, // END
{ KEYCODE_CSINUM, '~', 5 }, // PAGEUP
{ KEYCODE_CSINUM, '~', 6 }, // PAGEDOWN
};
static keycodes_s keycodes_fn[] = {
{ KEYCODE_NONE }, // F0 - shouldn't happen
{ KEYCODE_SS3, 'P' }, // F1
{ KEYCODE_SS3, 'Q' }, // F2
{ KEYCODE_SS3, 'R' }, // F3
{ KEYCODE_SS3, 'S' }, // 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
};
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_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod)
{
if(key == VTERM_KEY_NONE)
return;
keycodes_s k;
if(key < VTERM_KEY_FUNCTION_0) {
if(key >= sizeof(keycodes)/sizeof(keycodes[0]))
return;
k = keycodes[key];
}
else if(key >= VTERM_KEY_FUNCTION_0 && key <= VTERM_KEY_FUNCTION_MAX) {
if((key - VTERM_KEY_FUNCTION_0) >= sizeof(keycodes_fn)/sizeof(keycodes_fn[0]))
return;
k = keycodes_fn[key - VTERM_KEY_FUNCTION_0];
}
else if(key >= VTERM_KEY_KP_0) {
if((key - VTERM_KEY_KP_0) >= sizeof(keycodes_kp)/sizeof(keycodes_kp[0]))
return;
k = keycodes_kp[key - VTERM_KEY_KP_0];
}
switch(k.type) {
case KEYCODE_NONE:
break;
case KEYCODE_TAB:
/* Shift-Tab is CSI Z but plain Tab is 0x09 */
if(mod == VTERM_MOD_SHIFT)
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "Z");
else if(mod & VTERM_MOD_SHIFT)
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%dZ", mod+1);
else
goto case_LITERAL;
break;
case KEYCODE_ENTER:
/* Enter is CRLF in newline mode, but just LF in linefeed */
if(vt->state->mode.newline)
vterm_push_output_sprintf(vt, "\r\n");
else
goto case_LITERAL;
break;
case KEYCODE_LITERAL: case_LITERAL:
if(mod & (VTERM_MOD_SHIFT|VTERM_MOD_CTRL))
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", k.literal, mod+1);
else
vterm_push_output_sprintf(vt, mod & VTERM_MOD_ALT ? ESC_S "%c" : "%c", k.literal);
break;
case KEYCODE_SS3: case_SS3:
if(mod == 0)
vterm_push_output_sprintf_ctrl(vt, C1_SS3, "%c", k.literal);
else
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;
case KEYCODE_CSINUM:
if(mod == 0)
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d%c", k.csinum, k.literal);
else
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%d%c", k.csinum, mod + 1, k.literal);
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;
}
}
void vterm_keyboard_start_paste(VTerm *vt)
{
if(vt->state->mode.bracketpaste)
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "200~");
}
void vterm_keyboard_end_paste(VTerm *vt)
{
if(vt->state->mode.bracketpaste)
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "201~");
}

99
third_party/libvterm/src/mouse.c vendored Normal file
View File

@@ -0,0 +1,99 @@
#include "vterm_internal.h"
#include "utf8.h"
static void output_mouse(VTermState *state, int code, int pressed, int modifiers, int col, int row)
{
modifiers <<= 2;
switch(state->mouse_protocol) {
case MOUSE_X10:
if(col + 0x21 > 0xff)
col = 0xff - 0x21;
if(row + 0x21 > 0xff)
row = 0xff - 0x21;
if(!pressed)
code = 3;
vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "M%c%c%c",
(code | modifiers) + 0x20, col + 0x21, row + 0x21);
break;
case MOUSE_UTF8:
{
char utf8[18]; size_t len = 0;
if(!pressed)
code = 3;
len += fill_utf8((code | modifiers) + 0x20, utf8 + len);
len += fill_utf8(col + 0x21, utf8 + len);
len += fill_utf8(row + 0x21, utf8 + len);
utf8[len] = 0;
vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "M%s", utf8);
}
break;
case MOUSE_SGR:
vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "<%d;%d;%d%c",
code | modifiers, col + 1, row + 1, pressed ? 'M' : 'm');
break;
case MOUSE_RXVT:
if(!pressed)
code = 3;
vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%d;%d;%dM",
code | modifiers, col + 1, row + 1);
break;
}
}
void vterm_mouse_move(VTerm *vt, int row, int col, VTermModifier mod)
{
VTermState *state = vt->state;
if(col == state->mouse_col && row == state->mouse_row)
return;
state->mouse_col = col;
state->mouse_row = row;
if((state->mouse_flags & MOUSE_WANT_DRAG && state->mouse_buttons) ||
(state->mouse_flags & MOUSE_WANT_MOVE)) {
int button = state->mouse_buttons & 0x01 ? 1 :
state->mouse_buttons & 0x02 ? 2 :
state->mouse_buttons & 0x04 ? 3 : 4;
output_mouse(state, button-1 + 0x20, 1, mod, col, row);
}
}
void vterm_mouse_button(VTerm *vt, int button, bool pressed, VTermModifier mod)
{
VTermState *state = vt->state;
int old_buttons = state->mouse_buttons;
if(button > 0 && button <= 3) {
if(pressed)
state->mouse_buttons |= (1 << (button-1));
else
state->mouse_buttons &= ~(1 << (button-1));
}
/* Most of the time we don't get button releases from 4/5 */
if(state->mouse_buttons == old_buttons && button < 4)
return;
if(!state->mouse_flags)
return;
if(button < 4) {
output_mouse(state, button-1, pressed, mod, state->mouse_col, state->mouse_row);
}
else if(button < 8) {
output_mouse(state, button-4 + 0x40, pressed, mod, state->mouse_col, state->mouse_row);
}
}

402
third_party/libvterm/src/parser.c vendored Normal file
View File

@@ -0,0 +1,402 @@
#include "vterm_internal.h"
#include <stdio.h>
#include <string.h>
#undef DEBUG_PARSER
static bool is_intermed(unsigned char c)
{
return c >= 0x20 && c <= 0x2f;
}
static void do_control(VTerm *vt, unsigned char control)
{
if(vt->parser.callbacks && vt->parser.callbacks->control)
if((*vt->parser.callbacks->control)(control, vt->parser.cbdata))
return;
DEBUG_LOG("libvterm: Unhandled control 0x%02x\n", control);
}
static void do_csi(VTerm *vt, char command)
{
#ifdef DEBUG_PARSER
printf("Parsed CSI args as:\n", arglen, args);
printf(" leader: %s\n", vt->parser.v.csi.leader);
for(int argi = 0; argi < vt->parser.v.csi.argi; argi++) {
printf(" %lu", CSI_ARG(vt->parser.v.csi.args[argi]));
if(!CSI_ARG_HAS_MORE(vt->parser.v.csi.args[argi]))
printf("\n");
printf(" intermed: %s\n", vt->parser.intermed);
}
#endif
if(vt->parser.callbacks && vt->parser.callbacks->csi)
if((*vt->parser.callbacks->csi)(
vt->parser.v.csi.leaderlen ? vt->parser.v.csi.leader : NULL,
vt->parser.v.csi.args,
vt->parser.v.csi.argi,
vt->parser.intermedlen ? vt->parser.intermed : NULL,
command,
vt->parser.cbdata))
return;
DEBUG_LOG("libvterm: Unhandled CSI %c\n", command);
}
static void do_escape(VTerm *vt, char command)
{
char seq[INTERMED_MAX+1];
size_t len = vt->parser.intermedlen;
strncpy(seq, vt->parser.intermed, len);
seq[len++] = command;
seq[len] = 0;
if(vt->parser.callbacks && vt->parser.callbacks->escape)
if((*vt->parser.callbacks->escape)(seq, len, vt->parser.cbdata))
return;
DEBUG_LOG("libvterm: Unhandled escape ESC 0x%02x\n", command);
}
static void string_fragment(VTerm *vt, const char *str, size_t len, bool final)
{
VTermStringFragment frag = {
.str = str,
.len = len,
.initial = vt->parser.string_initial,
.final = final,
};
switch(vt->parser.state) {
case OSC:
if(vt->parser.callbacks && vt->parser.callbacks->osc)
(*vt->parser.callbacks->osc)(vt->parser.v.osc.command, frag, vt->parser.cbdata);
break;
case DCS:
if(vt->parser.callbacks && vt->parser.callbacks->dcs)
(*vt->parser.callbacks->dcs)(vt->parser.v.dcs.command, vt->parser.v.dcs.commandlen, frag, vt->parser.cbdata);
break;
case APC:
if(vt->parser.callbacks && vt->parser.callbacks->apc)
(*vt->parser.callbacks->apc)(frag, vt->parser.cbdata);
break;
case PM:
if(vt->parser.callbacks && vt->parser.callbacks->pm)
(*vt->parser.callbacks->pm)(frag, vt->parser.cbdata);
break;
case SOS:
if(vt->parser.callbacks && vt->parser.callbacks->sos)
(*vt->parser.callbacks->sos)(frag, vt->parser.cbdata);
break;
case NORMAL:
case CSI_LEADER:
case CSI_ARGS:
case CSI_INTERMED:
case OSC_COMMAND:
case DCS_COMMAND:
break;
}
vt->parser.string_initial = false;
}
size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len)
{
size_t pos = 0;
const char *string_start;
switch(vt->parser.state) {
case NORMAL:
case CSI_LEADER:
case CSI_ARGS:
case CSI_INTERMED:
case OSC_COMMAND:
case DCS_COMMAND:
string_start = NULL;
break;
case OSC:
case DCS:
case APC:
case PM:
case SOS:
string_start = bytes;
break;
}
#define ENTER_STATE(st) do { vt->parser.state = st; string_start = NULL; } while(0)
#define ENTER_NORMAL_STATE() ENTER_STATE(NORMAL)
#define IS_STRING_STATE() (vt->parser.state >= OSC_COMMAND)
for( ; pos < len; pos++) {
unsigned char c = bytes[pos];
bool c1_allowed = !vt->mode.utf8;
if(c == 0x00 || c == 0x7f) { // NUL, DEL
if(IS_STRING_STATE()) {
string_fragment(vt, string_start, bytes + pos - string_start, false);
string_start = bytes + pos + 1;
}
if(vt->parser.emit_nul)
do_control(vt, c);
continue;
}
if(c == 0x18 || c == 0x1a) { // CAN, SUB
vt->parser.in_esc = false;
ENTER_NORMAL_STATE();
if(vt->parser.emit_nul)
do_control(vt, c);
continue;
}
else if(c == 0x1b) { // ESC
vt->parser.intermedlen = 0;
if(!IS_STRING_STATE())
vt->parser.state = NORMAL;
vt->parser.in_esc = true;
continue;
}
else if(c == 0x07 && // BEL, can stand for ST in OSC or DCS state
IS_STRING_STATE()) {
// fallthrough
}
else if(c < 0x20) { // other C0
if(vt->parser.state == SOS)
continue; // All other C0s permitted in SOS
if(IS_STRING_STATE())
string_fragment(vt, string_start, bytes + pos - string_start, false);
do_control(vt, c);
if(IS_STRING_STATE())
string_start = bytes + pos + 1;
continue;
}
// else fallthrough
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
// Always accept ESC \ == ST even in string mode
if(!vt->parser.intermedlen &&
c >= 0x40 && c < 0x60 &&
((!IS_STRING_STATE() || c == 0x5c))) {
c += 0x40;
c1_allowed = true;
if(string_len)
string_len -= 1;
vt->parser.in_esc = false;
}
else {
string_start = NULL;
vt->parser.state = NORMAL;
}
}
switch(vt->parser.state) {
case CSI_LEADER:
/* Extract leader bytes 0x3c to 0x3f */
if(c >= 0x3c && c <= 0x3f) {
if(vt->parser.v.csi.leaderlen < CSI_LEADER_MAX-1)
vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen++] = c;
break;
}
/* else fallthrough */
vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen] = 0;
vt->parser.v.csi.argi = 0;
vt->parser.v.csi.args[0] = CSI_ARG_MISSING;
vt->parser.state = CSI_ARGS;
/* fallthrough */
case CSI_ARGS:
/* Numerical value of argument */
if(c >= '0' && c <= '9') {
if(vt->parser.v.csi.args[vt->parser.v.csi.argi] == CSI_ARG_MISSING)
vt->parser.v.csi.args[vt->parser.v.csi.argi] = 0;
vt->parser.v.csi.args[vt->parser.v.csi.argi] *= 10;
vt->parser.v.csi.args[vt->parser.v.csi.argi] += c - '0';
break;
}
if(c == ':') {
vt->parser.v.csi.args[vt->parser.v.csi.argi] |= CSI_ARG_FLAG_MORE;
c = ';';
}
if(c == ';') {
vt->parser.v.csi.argi++;
vt->parser.v.csi.args[vt->parser.v.csi.argi] = CSI_ARG_MISSING;
break;
}
/* else fallthrough */
vt->parser.v.csi.argi++;
vt->parser.intermedlen = 0;
vt->parser.state = CSI_INTERMED;
case CSI_INTERMED:
if(is_intermed(c)) {
if(vt->parser.intermedlen < INTERMED_MAX-1)
vt->parser.intermed[vt->parser.intermedlen++] = c;
break;
}
else if(c == 0x1b) {
/* ESC in CSI cancels */
}
else if(c >= 0x40 && c <= 0x7e) {
vt->parser.intermed[vt->parser.intermedlen] = 0;
do_csi(vt, c);
}
/* else was invalid CSI */
ENTER_NORMAL_STATE();
break;
case OSC_COMMAND:
/* Numerical value of command */
if(c >= '0' && c <= '9') {
if(vt->parser.v.osc.command == -1)
vt->parser.v.osc.command = 0;
else
vt->parser.v.osc.command *= 10;
vt->parser.v.osc.command += c - '0';
break;
}
if(c == ';') {
vt->parser.state = OSC;
string_start = bytes + pos + 1;
break;
}
/* else fallthrough */
string_start = bytes + pos;
string_len = 0;
vt->parser.state = OSC;
goto string_state;
case DCS_COMMAND:
if(vt->parser.v.dcs.commandlen < CSI_LEADER_MAX)
vt->parser.v.dcs.command[vt->parser.v.dcs.commandlen++] = c;
if(c >= 0x40 && c<= 0x7e) {
string_start = bytes + pos + 1;
vt->parser.state = DCS;
}
break;
string_state:
case OSC:
case DCS:
case APC:
case PM:
case SOS:
if(c == 0x07 || (c1_allowed && c == 0x9c)) {
string_fragment(vt, string_start, string_len, true);
ENTER_NORMAL_STATE();
}
break;
case NORMAL:
if(vt->parser.in_esc) {
if(is_intermed(c)) {
if(vt->parser.intermedlen < INTERMED_MAX-1)
vt->parser.intermed[vt->parser.intermedlen++] = c;
}
else if(c >= 0x30 && c < 0x7f) {
do_escape(vt, c);
vt->parser.in_esc = 0;
ENTER_NORMAL_STATE();
}
else {
DEBUG_LOG("TODO: Unhandled byte %02x in Escape\n", c);
}
break;
}
if(c1_allowed && c >= 0x80 && c < 0xa0) {
switch(c) {
case 0x90: // DCS
vt->parser.string_initial = true;
vt->parser.v.dcs.commandlen = 0;
ENTER_STATE(DCS_COMMAND);
break;
case 0x98: // SOS
vt->parser.string_initial = true;
ENTER_STATE(SOS);
string_start = bytes + pos + 1;
string_len = 0;
break;
case 0x9b: // CSI
vt->parser.v.csi.leaderlen = 0;
ENTER_STATE(CSI_LEADER);
break;
case 0x9d: // OSC
vt->parser.v.osc.command = -1;
vt->parser.string_initial = true;
string_start = bytes + pos + 1;
ENTER_STATE(OSC_COMMAND);
break;
case 0x9e: // PM
vt->parser.string_initial = true;
ENTER_STATE(PM);
string_start = bytes + pos + 1;
string_len = 0;
break;
case 0x9f: // APC
vt->parser.string_initial = true;
ENTER_STATE(APC);
string_start = bytes + pos + 1;
string_len = 0;
break;
default:
do_control(vt, c);
break;
}
}
else {
size_t eaten = 0;
if(vt->parser.callbacks && vt->parser.callbacks->text)
eaten = (*vt->parser.callbacks->text)(bytes + pos, len - pos, vt->parser.cbdata);
if(!eaten) {
DEBUG_LOG("libvterm: Text callback did not consume any input\n");
/* force it to make progress */
eaten = 1;
}
pos += (eaten - 1); // we'll ++ it again in a moment
}
break;
}
}
if(string_start) {
size_t string_len = bytes + pos - string_start;
if(vt->parser.in_esc)
string_len -= 1;
string_fragment(vt, string_start, string_len, false);
}
return len;
}
void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user)
{
vt->parser.callbacks = callbacks;
vt->parser.cbdata = user;
}
void *vterm_parser_get_cbdata(VTerm *vt)
{
return vt->parser.cbdata;
}
void vterm_parser_set_emit_nul(VTerm *vt, bool emit)
{
vt->parser.emit_nul = emit;
}

607
third_party/libvterm/src/pen.c vendored Normal file
View File

@@ -0,0 +1,607 @@
#include "vterm_internal.h"
#include <stdio.h>
/**
* Structure used to store RGB triples without the additional metadata stored in
* VTermColor.
*/
typedef struct {
uint8_t red, green, blue;
} VTermRGB;
static const VTermRGB ansi_colors[] = {
/* R G B */
{ 0, 0, 0 }, // black
{ 224, 0, 0 }, // red
{ 0, 224, 0 }, // green
{ 224, 224, 0 }, // yellow
{ 0, 0, 224 }, // blue
{ 224, 0, 224 }, // magenta
{ 0, 224, 224 }, // cyan
{ 224, 224, 224 }, // white == light grey
// high intensity
{ 128, 128, 128 }, // black
{ 255, 64, 64 }, // red
{ 64, 255, 64 }, // green
{ 255, 255, 64 }, // yellow
{ 64, 64, 255 }, // blue
{ 255, 64, 255 }, // magenta
{ 64, 255, 255 }, // cyan
{ 255, 255, 255 }, // white for real
};
static int ramp6[] = {
0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF,
};
static int ramp24[] = {
0x00, 0x0B, 0x16, 0x21, 0x2C, 0x37, 0x42, 0x4D, 0x58, 0x63, 0x6E, 0x79,
0x85, 0x90, 0x9B, 0xA6, 0xB1, 0xBC, 0xC7, 0xD2, 0xDD, 0xE8, 0xF3, 0xFF,
};
static void lookup_default_colour_ansi(long idx, VTermColor *col)
{
if (idx >= 0 && idx < 16) {
vterm_color_rgb(
col,
ansi_colors[idx].red, ansi_colors[idx].green, ansi_colors[idx].blue);
}
}
static bool lookup_colour_ansi(const VTermState *state, long index, VTermColor *col)
{
if(index >= 0 && index < 16) {
*col = state->colors[index];
return true;
}
return false;
}
static bool lookup_colour_palette(const VTermState *state, long index, VTermColor *col)
{
if(index >= 0 && index < 16) {
// Normal 8 colours or high intensity - parse as palette 0
return lookup_colour_ansi(state, index, col);
}
else if(index >= 16 && index < 232) {
// 216-colour cube
index -= 16;
vterm_color_rgb(col, ramp6[index/6/6 % 6],
ramp6[index/6 % 6],
ramp6[index % 6]);
return true;
}
else if(index >= 232 && index < 256) {
// 24 greyscales
index -= 232;
vterm_color_rgb(col, ramp24[index], ramp24[index], ramp24[index]);
return true;
}
return false;
}
static int lookup_colour(const VTermState *state, int palette, const long args[], int argcount, VTermColor *col)
{
switch(palette) {
case 2: // RGB mode - 3 args contain colour values directly
if(argcount < 3)
return argcount;
vterm_color_rgb(col, CSI_ARG(args[0]), CSI_ARG(args[1]), CSI_ARG(args[2]));
return 3;
case 5: // XTerm 256-colour mode
if (!argcount || CSI_ARG_IS_MISSING(args[0])) {
return argcount ? 1 : 0;
}
vterm_color_indexed(col, args[0]);
return argcount ? 1 : 0;
default:
DEBUG_LOG("Unrecognised colour palette %d\n", palette);
return 0;
}
}
// Some conveniences
static void setpenattr(VTermState *state, VTermAttr attr, VTermValueType type, VTermValue *val)
{
#ifdef DEBUG
if(type != vterm_get_attr_type(attr)) {
DEBUG_LOG("Cannot set attr %d as it has type %d, not type %d\n",
attr, vterm_get_attr_type(attr), type);
return;
}
#endif
if(state->callbacks && state->callbacks->setpenattr)
(*state->callbacks->setpenattr)(attr, val, state->cbdata);
}
static void setpenattr_bool(VTermState *state, VTermAttr attr, int boolean)
{
VTermValue val = { .boolean = boolean };
setpenattr(state, attr, VTERM_VALUETYPE_BOOL, &val);
}
static void setpenattr_int(VTermState *state, VTermAttr attr, int number)
{
VTermValue val = { .number = number };
setpenattr(state, attr, VTERM_VALUETYPE_INT, &val);
}
static void setpenattr_col(VTermState *state, VTermAttr attr, VTermColor color)
{
VTermValue val = { .color = color };
setpenattr(state, attr, VTERM_VALUETYPE_COLOR, &val);
}
static void set_pen_col_ansi(VTermState *state, VTermAttr attr, long col)
{
VTermColor *colp = (attr == VTERM_ATTR_BACKGROUND) ? &state->pen.bg : &state->pen.fg;
vterm_color_indexed(colp, col);
setpenattr_col(state, attr, *colp);
}
INTERNAL void vterm_state_newpen(VTermState *state)
{
// 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(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.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.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);
}
INTERNAL void vterm_state_savepen(VTermState *state, int save)
{
if(save) {
state->saved.pen = state->pen;
}
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_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);
}
}
int vterm_color_is_equal(const VTermColor *a, const VTermColor *b)
{
/* First make sure that the two colours are of the same type (RGB/Indexed) */
if (a->type != b->type) {
return false;
}
/* Depending on the type inspect the corresponding members */
if (VTERM_COLOR_IS_INDEXED(a)) {
return a->indexed.idx == b->indexed.idx;
}
else if (VTERM_COLOR_IS_RGB(a)) {
return (a->rgb.red == b->rgb.red)
&& (a->rgb.green == b->rgb.green)
&& (a->rgb.blue == b->rgb.blue);
}
return 0;
}
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)
{
if(default_fg) {
state->default_fg = *default_fg;
state->default_fg.type = (state->default_fg.type & ~VTERM_COLOR_DEFAULT_MASK)
| VTERM_COLOR_DEFAULT_FG;
}
if(default_bg) {
state->default_bg = *default_bg;
state->default_bg.type = (state->default_bg.type & ~VTERM_COLOR_DEFAULT_MASK)
| VTERM_COLOR_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_convert_color_to_rgb(const VTermState *state, VTermColor *col)
{
if (VTERM_COLOR_IS_INDEXED(col)) { /* Convert indexed colors to RGB */
lookup_colour_palette(state, col->indexed.idx, col);
}
col->type &= VTERM_COLOR_TYPE_MASK; /* Reset any metadata but the type */
}
void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright)
{
state->bold_is_highbright = bold_is_highbright;
}
INTERNAL void vterm_state_setpen(VTermState *state, const long args[], int argcount)
{
// SGR - ECMA-48 8.3.117
int argi = 0;
int value;
while(argi < argcount) {
// This logic is easier to do 'done' backwards; set it true, and make it
// false again in the 'default' case
int done = 1;
long arg;
switch(arg = CSI_ARG(args[argi])) {
case CSI_ARG_MISSING:
case 0: // Reset
vterm_state_resetpen(state);
break;
case 1: { // Bold on
const VTermColor *fg = &state->pen.fg;
state->pen.bold = 1;
setpenattr_bool(state, VTERM_ATTR_BOLD, 1);
if(!VTERM_COLOR_IS_DEFAULT_FG(fg) && VTERM_COLOR_IS_INDEXED(fg) && fg->indexed.idx < 8 && state->bold_is_highbright)
set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, fg->indexed.idx + (state->pen.bold ? 8 : 0));
break;
}
case 3: // Italic on
state->pen.italic = 1;
setpenattr_bool(state, VTERM_ATTR_ITALIC, 1);
break;
case 4: // Underline
state->pen.underline = VTERM_UNDERLINE_SINGLE;
if(CSI_ARG_HAS_MORE(args[argi])) {
argi++;
switch(CSI_ARG(args[argi])) {
case 0:
state->pen.underline = 0;
break;
case 1:
state->pen.underline = VTERM_UNDERLINE_SINGLE;
break;
case 2:
state->pen.underline = VTERM_UNDERLINE_DOUBLE;
break;
case 3:
state->pen.underline = VTERM_UNDERLINE_CURLY;
break;
}
}
setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
break;
case 5: // Blink
state->pen.blink = 1;
setpenattr_bool(state, VTERM_ATTR_BLINK, 1);
break;
case 7: // Reverse on
state->pen.reverse = 1;
setpenattr_bool(state, VTERM_ATTR_REVERSE, 1);
break;
case 8: // Conceal on
state->pen.conceal = 1;
setpenattr_bool(state, VTERM_ATTR_CONCEAL, 1);
break;
case 9: // Strikethrough on
state->pen.strike = 1;
setpenattr_bool(state, VTERM_ATTR_STRIKE, 1);
break;
case 10: case 11: case 12: case 13: case 14:
case 15: case 16: case 17: case 18: case 19: // Select font
state->pen.font = CSI_ARG(args[argi]) - 10;
setpenattr_int(state, VTERM_ATTR_FONT, state->pen.font);
break;
case 21: // Underline double
state->pen.underline = VTERM_UNDERLINE_DOUBLE;
setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
break;
case 22: // Bold off
state->pen.bold = 0;
setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
break;
case 23: // Italic and Gothic (currently unsupported) off
state->pen.italic = 0;
setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
break;
case 24: // Underline off
state->pen.underline = 0;
setpenattr_int(state, VTERM_ATTR_UNDERLINE, 0);
break;
case 25: // Blink off
state->pen.blink = 0;
setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
break;
case 27: // Reverse off
state->pen.reverse = 0;
setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
break;
case 28: // Conceal off (Reveal)
state->pen.conceal = 0;
setpenattr_bool(state, VTERM_ATTR_CONCEAL, 0);
break;
case 29: // Strikethrough off
state->pen.strike = 0;
setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
break;
case 30: case 31: case 32: case 33:
case 34: case 35: case 36: case 37: // Foreground colour palette
value = CSI_ARG(args[argi]) - 30;
if(state->pen.bold && state->bold_is_highbright)
value += 8;
set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
break;
case 38: // Foreground colour alternative palette
if(argcount - argi < 1)
return;
argi += 1 + lookup_colour(state, CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.fg);
setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
break;
case 39: // Foreground colour default
state->pen.fg = state->default_fg;
setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
break;
case 40: case 41: case 42: case 43:
case 44: case 45: case 46: case 47: // Background colour palette
value = CSI_ARG(args[argi]) - 40;
set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
break;
case 48: // Background colour alternative palette
if(argcount - argi < 1)
return;
argi += 1 + lookup_colour(state, CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.bg);
setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
break;
case 49: // Default background
state->pen.bg = state->default_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;
set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
break;
case 100: case 101: case 102: case 103:
case 104: case 105: case 106: case 107: // Background colour high-intensity palette
value = CSI_ARG(args[argi]) - 100 + 8;
set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
break;
default:
done = 0;
break;
}
if(!done)
DEBUG_LOG("libvterm: Unhandled CSI SGR %ld\n", arg);
while(CSI_ARG_HAS_MORE(args[argi++]));
}
}
static int vterm_state_getpen_color(const VTermColor *col, int argi, long args[], int fg)
{
/* Do nothing if the given color is the default color */
if (( fg && VTERM_COLOR_IS_DEFAULT_FG(col)) ||
(!fg && VTERM_COLOR_IS_DEFAULT_BG(col))) {
return argi;
}
/* Decide whether to send an indexed color or an RGB color */
if (VTERM_COLOR_IS_INDEXED(col)) {
const uint8_t idx = col->indexed.idx;
if (idx < 8) {
args[argi++] = (idx + (fg ? 30 : 40));
}
else if (idx < 16) {
args[argi++] = (idx - 8 + (fg ? 90 : 100));
}
else {
args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
args[argi++] = CSI_ARG_FLAG_MORE | 5;
args[argi++] = idx;
}
}
else if (VTERM_COLOR_IS_RGB(col)) {
args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
args[argi++] = CSI_ARG_FLAG_MORE | 2;
args[argi++] = CSI_ARG_FLAG_MORE | col->rgb.red;
args[argi++] = CSI_ARG_FLAG_MORE | col->rgb.green;
args[argi++] = col->rgb.blue;
}
return argi;
}
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 == VTERM_UNDERLINE_SINGLE)
args[argi++] = 4;
if(state->pen.underline == VTERM_UNDERLINE_CURLY)
args[argi++] = 4 | CSI_ARG_FLAG_MORE, args[argi++] = 3;
if(state->pen.blink)
args[argi++] = 5;
if(state->pen.reverse)
args[argi++] = 7;
if(state->pen.conceal)
args[argi++] = 8;
if(state->pen.strike)
args[argi++] = 9;
if(state->pen.font)
args[argi++] = 10 + state->pen.font;
if(state->pen.underline == VTERM_UNDERLINE_DOUBLE)
args[argi++] = 21;
argi = vterm_state_getpen_color(&state->pen.fg, argi, args, true);
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;
}
int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val)
{
switch(attr) {
case VTERM_ATTR_BOLD:
val->boolean = state->pen.bold;
return 1;
case VTERM_ATTR_UNDERLINE:
val->number = state->pen.underline;
return 1;
case VTERM_ATTR_ITALIC:
val->boolean = state->pen.italic;
return 1;
case VTERM_ATTR_BLINK:
val->boolean = state->pen.blink;
return 1;
case VTERM_ATTR_REVERSE:
val->boolean = state->pen.reverse;
return 1;
case VTERM_ATTR_CONCEAL:
val->boolean = state->pen.conceal;
return 1;
case VTERM_ATTR_STRIKE:
val->boolean = state->pen.strike;
return 1;
case VTERM_ATTR_FONT:
val->number = state->pen.font;
return 1;
case VTERM_ATTR_FOREGROUND:
val->color = state->pen.fg;
return 1;
case VTERM_ATTR_BACKGROUND:
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;
}
return 0;
}

56
third_party/libvterm/src/rect.h vendored Normal file
View File

@@ -0,0 +1,56 @@
/*
* Some utility functions on VTermRect structures
*/
#define STRFrect "(%d,%d-%d,%d)"
#define ARGSrect(r) (r).start_row, (r).start_col, (r).end_row, (r).end_col
/* Expand dst to contain src as well */
static void rect_expand(VTermRect *dst, VTermRect *src)
{
if(dst->start_row > src->start_row) dst->start_row = src->start_row;
if(dst->start_col > src->start_col) dst->start_col = src->start_col;
if(dst->end_row < src->end_row) dst->end_row = src->end_row;
if(dst->end_col < src->end_col) dst->end_col = src->end_col;
}
/* Clip the dst to ensure it does not step outside of bounds */
static void rect_clip(VTermRect *dst, VTermRect *bounds)
{
if(dst->start_row < bounds->start_row) dst->start_row = bounds->start_row;
if(dst->start_col < bounds->start_col) dst->start_col = bounds->start_col;
if(dst->end_row > bounds->end_row) dst->end_row = bounds->end_row;
if(dst->end_col > bounds->end_col) dst->end_col = bounds->end_col;
/* Ensure it doesn't end up negatively-sized */
if(dst->end_row < dst->start_row) dst->end_row = dst->start_row;
if(dst->end_col < dst->start_col) dst->end_col = dst->start_col;
}
/* True if the two rectangles are equal */
static int rect_equal(VTermRect *a, VTermRect *b)
{
return (a->start_row == b->start_row) &&
(a->start_col == b->start_col) &&
(a->end_row == b->end_row) &&
(a->end_col == b->end_col);
}
/* True if small is contained entirely within big */
static int rect_contains(VTermRect *big, VTermRect *small)
{
if(small->start_row < big->start_row) return 0;
if(small->start_col < big->start_col) return 0;
if(small->end_row > big->end_row) return 0;
if(small->end_col > big->end_col) return 0;
return 1;
}
/* True if the rectangles overlap at all */
static int rect_intersects(VTermRect *a, VTermRect *b)
{
if(a->start_row > b->end_row || b->start_row > a->end_row)
return 0;
if(a->start_col > b->end_col || b->start_col > a->end_col)
return 0;
return 1;
}

1214
third_party/libvterm/src/screen.c vendored Normal file

File diff suppressed because it is too large Load Diff

2380
third_party/libvterm/src/state.c vendored Normal file

File diff suppressed because it is too large Load Diff

313
third_party/libvterm/src/unicode.c vendored Normal file
View File

@@ -0,0 +1,313 @@
#include "vterm_internal.h"
// ### The following from http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
// With modifications:
// made functions static
// moved 'combining' table to file scope, so other functions can see it
// ###################################################################
/*
* This is an implementation of wcwidth() and wcswidth() (defined in
* IEEE Std 1002.1-2001) for Unicode.
*
* http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html
* http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html
*
* In fixed-width output devices, Latin characters all occupy a single
* "cell" position of equal width, whereas ideographic CJK characters
* occupy two such cells. Interoperability between terminal-line
* applications and (teletype-style) character terminals using the
* UTF-8 encoding requires agreement on which character should advance
* the cursor by how many cell positions. No established formal
* standards exist at present on which Unicode character shall occupy
* how many cell positions on character terminals. These routines are
* a first attempt of defining such behavior based on simple rules
* applied to data provided by the Unicode Consortium.
*
* For some graphical characters, the Unicode standard explicitly
* defines a character-cell width via the definition of the East Asian
* FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.
* In all these cases, there is no ambiguity about which width a
* terminal shall use. For characters in the East Asian Ambiguous (A)
* class, the width choice depends purely on a preference of backward
* compatibility with either historic CJK or Western practice.
* Choosing single-width for these characters is easy to justify as
* the appropriate long-term solution, as the CJK practice of
* displaying these characters as double-width comes from historic
* implementation simplicity (8-bit encoded characters were displayed
* single-width and 16-bit ones double-width, even for Greek,
* Cyrillic, etc.) and not any typographic considerations.
*
* Much less clear is the choice of width for the Not East Asian
* (Neutral) class. Existing practice does not dictate a width for any
* of these characters. It would nevertheless make sense
* typographically to allocate two character cells to characters such
* as for instance EM SPACE or VOLUME INTEGRAL, which cannot be
* represented adequately with a single-width glyph. The following
* routines at present merely assign a single-cell width to all
* neutral characters, in the interest of simplicity. This is not
* entirely satisfactory and should be reconsidered before
* establishing a formal standard in this area. At the moment, the
* decision which Not East Asian (Neutral) characters should be
* represented by double-width glyphs cannot yet be answered by
* applying a simple rule from the Unicode database content. Setting
* up a proper standard for the behavior of UTF-8 character terminals
* will require a careful analysis not only of each Unicode character,
* but also of each presentation form, something the author of these
* routines has avoided to do so far.
*
* http://www.unicode.org/unicode/reports/tr11/
*
* Markus Kuhn -- 2007-05-26 (Unicode 5.0)
*
* Permission to use, copy, modify, and distribute this software
* for any purpose and without fee is hereby granted. The author
* disclaims all warranties with regard to this software.
*
* Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
*/
struct interval {
int first;
int last;
};
/* sorted list of non-overlapping intervals of non-spacing characters */
/* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
static const struct interval combining[] = {
{ 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 },
{ 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
{ 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 },
{ 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 },
{ 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED },
{ 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A },
{ 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 },
{ 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D },
{ 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 },
{ 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD },
{ 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C },
{ 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D },
{ 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC },
{ 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD },
{ 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C },
{ 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D },
{ 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 },
{ 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 },
{ 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC },
{ 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD },
{ 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },
{ 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },
{ 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },
{ 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },
{ 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },
{ 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },
{ 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 },
{ 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
{ 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },
{ 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F },
{ 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 },
{ 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD },
{ 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD },
{ 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 },
{ 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B },
{ 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 },
{ 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 },
{ 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF },
{ 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 },
{ 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F },
{ 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B },
{ 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F },
{ 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB },
{ 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F },
{ 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 },
{ 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD },
{ 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F },
{ 0xE0100, 0xE01EF }
};
/* auxiliary function for binary search in interval table */
static int bisearch(uint32_t ucs, const struct interval *table, int max) {
int min = 0;
int mid;
if (ucs < table[0].first || ucs > table[max].last)
return 0;
while (max >= min) {
mid = (min + max) / 2;
if (ucs > table[mid].last)
min = mid + 1;
else if (ucs < table[mid].first)
max = mid - 1;
else
return 1;
}
return 0;
}
/* The following two functions define the column width of an ISO 10646
* character as follows:
*
* - The null character (U+0000) has a column width of 0.
*
* - Other C0/C1 control characters and DEL will lead to a return
* value of -1.
*
* - Non-spacing and enclosing combining characters (general
* category code Mn or Me in the Unicode database) have a
* column width of 0.
*
* - SOFT HYPHEN (U+00AD) has a column width of 1.
*
* - Other format characters (general category code Cf in the Unicode
* database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
*
* - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
* have a column width of 0.
*
* - Spacing characters in the East Asian Wide (W) or East Asian
* Full-width (F) category as defined in Unicode Technical
* Report #11 have a column width of 2.
*
* - All remaining characters (including all printable
* ISO 8859-1 and WGL4 characters, Unicode control characters,
* etc.) have a column width of 1.
*
* This implementation assumes that uint32_t characters are encoded
* in ISO 10646.
*/
static int mk_wcwidth(uint32_t ucs)
{
/* test for 8-bit control characters */
if (ucs == 0)
return 0;
if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
return -1;
/* binary search in table of non-spacing characters */
if (bisearch(ucs, combining,
sizeof(combining) / sizeof(struct interval) - 1))
return 0;
/* if we arrive here, ucs is not a combining or C0/C1 control character */
return 1 +
(ucs >= 0x1100 &&
(ucs <= 0x115f || /* Hangul Jamo init. consonants */
ucs == 0x2329 || ucs == 0x232a ||
(ucs >= 0x2e80 && ucs <= 0xa4cf &&
ucs != 0x303f) || /* CJK ... Yi */
(ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */
(ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */
(ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */
(ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */
(ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */
(ucs >= 0xffe0 && ucs <= 0xffe6) ||
(ucs >= 0x20000 && ucs <= 0x2fffd) ||
(ucs >= 0x30000 && ucs <= 0x3fffd)));
}
#ifdef USE_MK_WCWIDTH_CJK
/*
* The following functions are the same as mk_wcwidth() and
* mk_wcswidth(), except that spacing characters in the East Asian
* Ambiguous (A) category as defined in Unicode Technical Report #11
* have a column width of 2. This variant might be useful for users of
* CJK legacy encodings who want to migrate to UCS without changing
* the traditional terminal character-width behaviour. It is not
* otherwise recommended for general use.
*/
static int mk_wcwidth_cjk(uint32_t ucs)
{
/* 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 }
};
/* binary search in table of non-spacing characters */
if (bisearch(ucs, ambiguous,
sizeof(ambiguous) / sizeof(struct interval) - 1))
return 2;
return mk_wcwidth(ucs);
}
#endif
// ################################
// ### The rest added by Paul Evans
static const struct interval fullwidth[] = {
#include "fullwidth.inc"
};
INTERNAL int vterm_unicode_width(uint32_t codepoint)
{
if(bisearch(codepoint, fullwidth, sizeof(fullwidth) / sizeof(fullwidth[0]) - 1))
return 2;
return mk_wcwidth(codepoint);
}
INTERNAL int vterm_unicode_is_combining(uint32_t codepoint)
{
return bisearch(codepoint, combining, sizeof(combining) / sizeof(struct interval) - 1);
}

39
third_party/libvterm/src/utf8.h vendored Normal file
View File

@@ -0,0 +1,39 @@
/* The following functions copied and adapted from libtermkey
*
* http://www.leonerd.org.uk/code/libtermkey/
*/
static inline unsigned int utf8_seqlen(long codepoint)
{
if(codepoint < 0x0000080) return 1;
if(codepoint < 0x0000800) return 2;
if(codepoint < 0x0010000) return 3;
if(codepoint < 0x0200000) return 4;
if(codepoint < 0x4000000) return 5;
return 6;
}
/* Does NOT NUL-terminate the buffer */
static int fill_utf8(long codepoint, char *str)
{
int nbytes = utf8_seqlen(codepoint);
// This is easier done backwards
int b = nbytes;
while(b > 1) {
b--;
str[b] = 0x80 | (codepoint & 0x3f);
codepoint >>= 6;
}
switch(nbytes) {
case 1: str[0] = (codepoint & 0x7f); break;
case 2: str[0] = 0xc0 | (codepoint & 0x1f); break;
case 3: str[0] = 0xe0 | (codepoint & 0x0f); break;
case 4: str[0] = 0xf0 | (codepoint & 0x07); break;
case 5: str[0] = 0xf8 | (codepoint & 0x03); break;
case 6: str[0] = 0xfc | (codepoint & 0x01); break;
}
return nbytes;
}
/* end copy */

430
third_party/libvterm/src/vterm.c vendored Normal file
View File

@@ -0,0 +1,430 @@
#include "vterm_internal.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
/*****************
* API functions *
*****************/
static void *default_malloc(size_t size, void *allocdata)
{
void *ptr = malloc(size);
if(ptr)
memset(ptr, 0, size);
return ptr;
}
static void default_free(void *ptr, void *allocdata)
{
free(ptr);
}
static VTermAllocatorFunctions default_allocator = {
.malloc = &default_malloc,
.free = &default_free,
};
VTerm *vterm_new(int rows, int cols)
{
return vterm_build(&(const struct VTermBuilder){
.rows = rows,
.cols = cols,
});
}
VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *funcs, void *allocdata)
{
return vterm_build(&(const struct VTermBuilder){
.rows = rows,
.cols = cols,
.allocator = funcs,
.allocdata = allocdata,
});
}
/* A handy macro for defaulting values out of builder fields */
#define DEFAULT(v, def) ((v) ? (v) : (def))
VTerm *vterm_build(const struct VTermBuilder *builder)
{
const VTermAllocatorFunctions *allocator = DEFAULT(builder->allocator, &default_allocator);
/* Need to bootstrap using the allocator function directly */
VTerm *vt = (*allocator->malloc)(sizeof(VTerm), builder->allocdata);
vt->allocator = allocator;
vt->allocdata = builder->allocdata;
vt->rows = builder->rows;
vt->cols = builder->cols;
vt->parser.state = NORMAL;
vt->parser.callbacks = NULL;
vt->parser.cbdata = NULL;
vt->parser.emit_nul = false;
vt->outfunc = NULL;
vt->outdata = NULL;
vt->outbuffer_len = DEFAULT(builder->outbuffer_len, 4096);
vt->outbuffer_cur = 0;
vt->outbuffer = vterm_allocator_malloc(vt, vt->outbuffer_len);
vt->tmpbuffer_len = DEFAULT(builder->tmpbuffer_len, 4096);
vt->tmpbuffer = vterm_allocator_malloc(vt, vt->tmpbuffer_len);
return vt;
}
void vterm_free(VTerm *vt)
{
if(vt->screen)
vterm_screen_free(vt->screen);
if(vt->state)
vterm_state_free(vt->state);
vterm_allocator_free(vt, vt->outbuffer);
vterm_allocator_free(vt, vt->tmpbuffer);
vterm_allocator_free(vt, vt);
}
INTERNAL void *vterm_allocator_malloc(VTerm *vt, size_t size)
{
return (*vt->allocator->malloc)(size, vt->allocdata);
}
INTERNAL void vterm_allocator_free(VTerm *vt, void *ptr)
{
(*vt->allocator->free)(ptr, vt->allocdata);
}
void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp)
{
if(rowsp)
*rowsp = vt->rows;
if(colsp)
*colsp = vt->cols;
}
void vterm_set_size(VTerm *vt, int rows, int cols)
{
if(rows < 1 || cols < 1)
return;
vt->rows = rows;
vt->cols = cols;
if(vt->parser.callbacks && vt->parser.callbacks->resize)
(*vt->parser.callbacks->resize)(rows, cols, vt->parser.cbdata);
}
int vterm_get_utf8(const VTerm *vt)
{
return vt->mode.utf8;
}
void vterm_set_utf8(VTerm *vt, int is_utf8)
{
vt->mode.utf8 = is_utf8;
}
void vterm_output_set_callback(VTerm *vt, VTermOutputCallback *func, void *user)
{
vt->outfunc = func;
vt->outdata = user;
}
INTERNAL void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len)
{
if(vt->outfunc) {
(vt->outfunc)(bytes, len, vt->outdata);
return;
}
if(len > vt->outbuffer_len - vt->outbuffer_cur)
return;
memcpy(vt->outbuffer + vt->outbuffer_cur, bytes, len);
vt->outbuffer_cur += len;
}
INTERNAL void vterm_push_output_vsprintf(VTerm *vt, const char *format, va_list args)
{
size_t len = vsnprintf(vt->tmpbuffer, vt->tmpbuffer_len,
format, args);
vterm_push_output_bytes(vt, vt->tmpbuffer, len);
}
INTERNAL void vterm_push_output_sprintf(VTerm *vt, const char *format, ...)
{
va_list args;
va_start(args, format);
vterm_push_output_vsprintf(vt, format, args);
va_end(args);
}
INTERNAL void vterm_push_output_sprintf_ctrl(VTerm *vt, unsigned char ctrl, const char *fmt, ...)
{
size_t cur;
if(ctrl >= 0x80 && !vt->mode.ctrl8bit)
cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len,
ESC_S "%c", ctrl - 0x40);
else
cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len,
"%c", ctrl);
if(cur >= vt->tmpbuffer_len)
return;
va_list args;
va_start(args, fmt);
cur += vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
fmt, args);
va_end(args);
if(cur >= vt->tmpbuffer_len)
return;
vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
}
INTERNAL void vterm_push_output_sprintf_str(VTerm *vt, unsigned char ctrl, bool term, const char *fmt, ...)
{
size_t cur = 0;
if(ctrl) {
if(ctrl >= 0x80 && !vt->mode.ctrl8bit)
cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len,
ESC_S "%c", ctrl - 0x40);
else
cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len,
"%c", ctrl);
if(cur >= vt->tmpbuffer_len)
return;
}
va_list args;
va_start(args, fmt);
cur += vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
fmt, args);
va_end(args);
if(cur >= vt->tmpbuffer_len)
return;
if(term) {
cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
vt->mode.ctrl8bit ? "\x9C" : ESC_S "\\"); // ST
if(cur >= vt->tmpbuffer_len)
return;
}
vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
}
size_t vterm_output_get_buffer_size(const VTerm *vt)
{
return vt->outbuffer_len;
}
size_t vterm_output_get_buffer_current(const VTerm *vt)
{
return vt->outbuffer_cur;
}
size_t vterm_output_get_buffer_remaining(const VTerm *vt)
{
return vt->outbuffer_len - vt->outbuffer_cur;
}
size_t vterm_output_read(VTerm *vt, char *buffer, size_t len)
{
if(len > vt->outbuffer_cur)
len = vt->outbuffer_cur;
memcpy(buffer, vt->outbuffer, len);
if(len < vt->outbuffer_cur)
memmove(vt->outbuffer, vt->outbuffer + len, vt->outbuffer_cur - len);
vt->outbuffer_cur -= len;
return len;
}
VTermValueType vterm_get_attr_type(VTermAttr attr)
{
switch(attr) {
case VTERM_ATTR_BOLD: return VTERM_VALUETYPE_BOOL;
case VTERM_ATTR_UNDERLINE: return VTERM_VALUETYPE_INT;
case VTERM_ATTR_ITALIC: return VTERM_VALUETYPE_BOOL;
case VTERM_ATTR_BLINK: return VTERM_VALUETYPE_BOOL;
case VTERM_ATTR_REVERSE: return VTERM_VALUETYPE_BOOL;
case VTERM_ATTR_CONCEAL: return VTERM_VALUETYPE_BOOL;
case VTERM_ATTR_STRIKE: return VTERM_VALUETYPE_BOOL;
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;
}
return 0; /* UNREACHABLE */
}
VTermValueType vterm_get_prop_type(VTermProp prop)
{
switch(prop) {
case VTERM_PROP_CURSORVISIBLE: return VTERM_VALUETYPE_BOOL;
case VTERM_PROP_CURSORBLINK: return VTERM_VALUETYPE_BOOL;
case VTERM_PROP_ALTSCREEN: return VTERM_VALUETYPE_BOOL;
case VTERM_PROP_TITLE: return VTERM_VALUETYPE_STRING;
case VTERM_PROP_ICONNAME: return VTERM_VALUETYPE_STRING;
case VTERM_PROP_REVERSE: return VTERM_VALUETYPE_BOOL;
case VTERM_PROP_CURSORSHAPE: return VTERM_VALUETYPE_INT;
case VTERM_PROP_MOUSE: return VTERM_VALUETYPE_INT;
case VTERM_PROP_FOCUSREPORT: return VTERM_VALUETYPE_BOOL;
case VTERM_N_PROPS: return 0;
}
return 0; /* UNREACHABLE */
}
void vterm_scroll_rect(VTermRect rect,
int downward,
int rightward,
int (*moverect)(VTermRect src, VTermRect dest, void *user),
int (*eraserect)(VTermRect rect, int selective, void *user),
void *user)
{
VTermRect src;
VTermRect dest;
if(abs(downward) >= rect.end_row - rect.start_row ||
abs(rightward) >= rect.end_col - rect.start_col) {
/* Scroll more than area; just erase the lot */
(*eraserect)(rect, 0, user);
return;
}
if(rightward >= 0) {
/* rect: [XXX................]
* src: [----------------]
* dest: [----------------]
*/
dest.start_col = rect.start_col;
dest.end_col = rect.end_col - rightward;
src.start_col = rect.start_col + rightward;
src.end_col = rect.end_col;
}
else {
/* rect: [................XXX]
* src: [----------------]
* dest: [----------------]
*/
int leftward = -rightward;
dest.start_col = rect.start_col + leftward;
dest.end_col = rect.end_col;
src.start_col = rect.start_col;
src.end_col = rect.end_col - leftward;
}
if(downward >= 0) {
dest.start_row = rect.start_row;
dest.end_row = rect.end_row - downward;
src.start_row = rect.start_row + downward;
src.end_row = rect.end_row;
}
else {
int upward = -downward;
dest.start_row = rect.start_row + upward;
dest.end_row = rect.end_row;
src.start_row = rect.start_row;
src.end_row = rect.end_row - upward;
}
if(moverect)
(*moverect)(dest, src, user);
if(downward > 0)
rect.start_row = rect.end_row - downward;
else if(downward < 0)
rect.end_row = rect.start_row - downward;
if(rightward > 0)
rect.start_col = rect.end_col - rightward;
else if(rightward < 0)
rect.end_col = rect.start_col - rightward;
(*eraserect)(rect, 0, user);
}
void vterm_copy_cells(VTermRect dest,
VTermRect src,
void (*copycell)(VTermPos dest, VTermPos src, void *user),
void *user)
{
int downward = src.start_row - dest.start_row;
int rightward = src.start_col - dest.start_col;
int init_row, test_row, init_col, test_col;
int inc_row, inc_col;
if(downward < 0) {
init_row = dest.end_row - 1;
test_row = dest.start_row - 1;
inc_row = -1;
}
else /* downward >= 0 */ {
init_row = dest.start_row;
test_row = dest.end_row;
inc_row = +1;
}
if(rightward < 0) {
init_col = dest.end_col - 1;
test_col = dest.start_col - 1;
inc_col = -1;
}
else /* rightward >= 0 */ {
init_col = dest.start_col;
test_col = dest.end_col;
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 = { pos.row + downward, pos.col + rightward };
(*copycell)(pos, srcpos, user);
}
}
void vterm_check_version(int major, int minor)
{
if(major != VTERM_VERSION_MAJOR) {
fprintf(stderr, "libvterm major version mismatch; %d (wants) != %d (library)\n",
major, VTERM_VERSION_MAJOR);
exit(1);
}
if(minor > VTERM_VERSION_MINOR) {
fprintf(stderr, "libvterm minor version mismatch; %d (wants) > %d (library)\n",
minor, VTERM_VERSION_MINOR);
exit(1);
}
// Happy
}

View File

@@ -0,0 +1,298 @@
#ifndef __VTERM_INTERNAL_H__
#define __VTERM_INTERNAL_H__
#include "vterm.h"
#include <stdarg.h>
#if defined(__GNUC__)
# define INTERNAL __attribute__((visibility("internal")))
#else
# define INTERNAL
#endif
#ifdef DEBUG
# define DEBUG_LOG(...) fprintf(stderr, __VA_ARGS__)
#else
# define DEBUG_LOG(...)
#endif
#define ESC_S "\x1b"
#define INTERMED_MAX 16
#define CSI_ARGS_MAX 16
#define CSI_LEADER_MAX 16
#define BUFIDX_PRIMARY 0
#define BUFIDX_ALTSCREEN 1
typedef struct VTermEncoding VTermEncoding;
typedef struct {
VTermEncoding *enc;
// This size should be increased if required by other stateful encodings
char data[4*sizeof(uint32_t)];
} VTermEncodingInstance;
struct VTermPen
{
VTermColor fg;
VTermColor bg;
unsigned int bold:1;
unsigned int underline:2;
unsigned int italic:1;
unsigned int blink:1;
unsigned int reverse:1;
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
{
VTerm *vt;
const VTermStateCallbacks *callbacks;
void *cbdata;
bool callbacks_has_premove;
const VTermStateFallbacks *fallbacks;
void *fbdata;
int rows;
int cols;
/* Current cursor position */
VTermPos pos;
int at_phantom; /* True if we're on the "81st" phantom column to defer a wraparound */
int scrollregion_top;
int scrollregion_bottom; /* -1 means unbounded */
#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 */
unsigned char *tabstops;
/* Primary and Altscreen; lineinfos[1] is lazily allocated as needed */
VTermLineInfo *lineinfos[2];
/* lineinfo will == lineinfos[0] or lineinfos[1], depending on altscreen */
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 */
int mouse_col, mouse_row;
int mouse_buttons;
int mouse_flags;
#define MOUSE_WANT_CLICK 0x01
#define MOUSE_WANT_DRAG 0x02
#define MOUSE_WANT_MOVE 0x04
enum { MOUSE_X10, MOUSE_UTF8, MOUSE_SGR, MOUSE_RXVT } mouse_protocol;
/* Last glyph output, for Unicode recombining purposes */
uint32_t *combine_chars;
size_t combine_chars_size; // Number of ELEMENTS in the above
int combine_width; // The width of the glyph above
VTermPos combine_pos; // Position before movement
struct {
unsigned int keypad:1;
unsigned int cursor:1;
unsigned int autowrap:1;
unsigned int insert:1;
unsigned int newline:1;
unsigned int cursor_visible:1;
unsigned int cursor_blink:1;
unsigned int cursor_shape:2;
unsigned int alt_screen:1;
unsigned int origin:1;
unsigned int screen:1;
unsigned int leftrightmargin:1;
unsigned int bracketpaste:1;
unsigned int report_focus:1;
} mode;
VTermEncodingInstance encoding[4], encoding_utf8;
int gl_set, gr_set, gsingle_set;
struct VTermPen pen;
VTermColor default_fg;
VTermColor default_bg;
VTermColor colors[16]; // Store the 8 ANSI and the 8 ANSI high-brights only
int bold_is_highbright;
unsigned int protected_cell : 1;
/* Saved state under DEC mode 1048/1049 */
struct {
VTermPos pos;
struct VTermPen pen;
struct {
unsigned int cursor_visible:1;
unsigned int cursor_blink:1;
unsigned int cursor_shape:2;
} mode;
} saved;
/* Temporary state for DECRQSS parsing */
union {
char decrqss[4];
struct {
uint16_t mask;
enum {
SELECTION_INITIAL,
SELECTION_SELECTED,
SELECTION_QUERY,
SELECTION_SET_INITIAL,
SELECTION_SET,
SELECTION_INVALID,
} state : 8;
uint32_t recvpartial;
uint32_t sendpartial;
} selection;
} tmp;
struct {
const VTermSelectionCallbacks *callbacks;
void *user;
char *buffer;
size_t buflen;
} selection;
};
struct VTerm
{
const VTermAllocatorFunctions *allocator;
void *allocdata;
int rows;
int cols;
struct {
unsigned int utf8:1;
unsigned int ctrl8bit:1;
} mode;
struct {
enum VTermParserState {
NORMAL,
CSI_LEADER,
CSI_ARGS,
CSI_INTERMED,
DCS_COMMAND,
/* below here are the "string states" */
OSC_COMMAND,
OSC,
DCS,
APC,
PM,
SOS,
} state;
bool in_esc : 1;
int intermedlen;
char intermed[INTERMED_MAX];
union {
struct {
int leaderlen;
char leader[CSI_LEADER_MAX];
int argi;
long args[CSI_ARGS_MAX];
} csi;
struct {
int command;
} osc;
struct {
int commandlen;
char command[CSI_LEADER_MAX];
} dcs;
} v;
const VTermParserCallbacks *callbacks;
void *cbdata;
bool string_initial;
bool emit_nul;
} parser;
/* len == malloc()ed size; cur == number of valid bytes */
VTermOutputCallback *outfunc;
void *outdata;
char *outbuffer;
size_t outbuffer_len;
size_t outbuffer_cur;
char *tmpbuffer;
size_t tmpbuffer_len;
VTermState *state;
VTermScreen *screen;
};
struct VTermEncoding {
void (*init) (VTermEncoding *enc, void *data);
void (*decode)(VTermEncoding *enc, void *data,
uint32_t cp[], int *cpi, int cplen,
const char bytes[], size_t *pos, size_t len);
};
typedef enum {
ENC_UTF8,
ENC_SINGLE_94
} VTermEncodingType;
void *vterm_allocator_malloc(VTerm *vt, size_t size);
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_vsprintf(VTerm *vt, const char *format, va_list args);
void vterm_push_output_sprintf(VTerm *vt, const char *format, ...);
void vterm_push_output_sprintf_ctrl(VTerm *vt, unsigned char ctrl, const char *fmt, ...);
void vterm_push_output_sprintf_str(VTerm *vt, unsigned char ctrl, bool term, const char *fmt, ...);
void vterm_state_free(VTermState *state);
void vterm_state_newpen(VTermState *state);
void vterm_state_resetpen(VTermState *state);
void vterm_state_setpen(VTermState *state, const long args[], int argcount);
int vterm_state_getpen(VTermState *state, long args[], int argcount);
void vterm_state_savepen(VTermState *state, int save);
enum {
C1_SS3 = 0x8f,
C1_DCS = 0x90,
C1_CSI = 0x9b,
C1_ST = 0x9c,
C1_OSC = 0x9d,
};
void vterm_state_push_output_sprintf_CSI(VTermState *vts, const char *format, ...);
void vterm_screen_free(VTermScreen *screen);
VTermEncoding *vterm_lookup_encoding(VTermEncodingType type, char designation);
int vterm_unicode_width(uint32_t codepoint);
int vterm_unicode_is_combining(uint32_t codepoint);
#endif