diff options
-rw-r--r-- | Android.bp | 45 | ||||
-rw-r--r-- | LICENSE | 23 | ||||
-rw-r--r-- | METADATA | 3 | ||||
-rw-r--r-- | MODULE_LICENSE_MIT | 0 | ||||
-rw-r--r-- | OWNERS | 1 | ||||
-rw-r--r-- | include/vterm.h | 511 | ||||
-rw-r--r-- | include/vterm_keycodes.h | 61 | ||||
-rw-r--r-- | src/encoding.c | 230 | ||||
-rw-r--r-- | src/encoding/DECdrawing.inc | 36 | ||||
-rw-r--r-- | src/encoding/DECdrawing.tbl | 31 | ||||
-rw-r--r-- | src/encoding/uk.inc | 6 | ||||
-rw-r--r-- | src/encoding/uk.tbl | 1 | ||||
-rw-r--r-- | src/fullwidth.inc | 104 | ||||
-rw-r--r-- | src/input.c | 210 | ||||
-rw-r--r-- | src/keyboard.c | 226 | ||||
-rw-r--r-- | src/mouse.c | 96 | ||||
-rw-r--r-- | src/parser.c | 340 | ||||
-rw-r--r-- | src/pen.c | 535 | ||||
-rw-r--r-- | src/rect.h | 56 | ||||
-rw-r--r-- | src/screen.c | 936 | ||||
-rw-r--r-- | src/state.c | 1868 | ||||
-rw-r--r-- | src/unicode.c | 337 | ||||
-rw-r--r-- | src/utf8.h | 39 | ||||
-rw-r--r-- | src/vterm.c | 367 | ||||
-rw-r--r-- | src/vterm_internal.h | 246 |
25 files changed, 0 insertions, 6308 deletions
diff --git a/Android.bp b/Android.bp deleted file mode 100644 index 217eca0..0000000 --- a/Android.bp +++ /dev/null @@ -1,45 +0,0 @@ -package { - default_applicable_licenses: ["external_libvterm_license"], -} - -license { - name: "external_libvterm_license", - visibility: [":__subpackages__"], - license_kinds: [ - // src/unicode.c: - "SPDX-license-identifier-0BSD", - // Everything else: - "SPDX-license-identifier-MIT", - ], - license_text: [ - "LICENSE", - ], -} - -cc_library_static { - name: "libvterm", - - export_include_dirs: ["include"], - - srcs: [ - "src/encoding.c", - "src/input.c", - "src/keyboard.c", - "src/parser.c", - "src/pen.c", - "src/screen.c", - "src/state.c", - "src/unicode.c", - "src/vterm.c", - ], - - cflags: [ - "-std=c99", - "-Wall", - "-Werror", - "-Wno-missing-field-initializers", - "-Wno-sign-compare", - "-Wno-unused-function", - "-Wno-unused-parameter", - ], -} diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 0d05163..0000000 --- a/LICENSE +++ /dev/null @@ -1,23 +0,0 @@ - - -The MIT License - -Copyright (c) 2008 Paul Evans <leonerd@leonerd.org.uk> - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/METADATA b/METADATA deleted file mode 100644 index d97975c..0000000 --- a/METADATA +++ /dev/null @@ -1,3 +0,0 @@ -third_party { - license_type: NOTICE -} diff --git a/MODULE_LICENSE_MIT b/MODULE_LICENSE_MIT deleted file mode 100644 index e69de29..0000000 --- a/MODULE_LICENSE_MIT +++ /dev/null @@ -1,2 +1 @@ -# only used by the Terminal app? include platform/system/core:/janitors/OWNERS diff --git a/include/vterm.h b/include/vterm.h deleted file mode 100644 index 51dc970..0000000 --- a/include/vterm.h +++ /dev/null @@ -1,511 +0,0 @@ -#ifndef __VTERM_H__ -#define __VTERM_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -#include <stdint.h> -#include <stdlib.h> -#include <stdbool.h> - -#include "vterm_keycodes.h" - -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 union { - int boolean; - int number; - char *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_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_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_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 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) */ -} VTermLineInfo; - -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; - -VTerm *vterm_new(int rows, int cols); -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); - -size_t vterm_output_get_buffer_size(const VTerm *vt); -size_t vterm_output_get_buffer_current(const VTerm *vt); -size_t vterm_output_get_buffer_remaining(const VTerm *vt); - -size_t vterm_output_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)(const char *command, size_t cmdlen, void *user); - int (*dcs)(const char *command, size_t cmdlen, void *user); - int (*resize)(int rows, int cols, void *user); -} VTermParserCallbacks; - -void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user); -void *vterm_parser_get_cbdata(VTerm *vt); - -// ----------- -// 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, VTermPos *delta, void *user); - int (*setlineinfo)(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user); -} VTermStateCallbacks; - -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); - -// Only invokes control, csi, osc, dcs -void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermParserCallbacks *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); - -// ------------ -// 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 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) */ -} VTermScreenCellAttrs; - -typedef struct { -#define VTERM_MAX_CHARS_PER_CELL 6 - 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); -} 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); - -// Only invokes control, csi, osc, dcs -void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermParserCallbacks *fallbacks, void *user); -void *vterm_screen_get_unrecognised_fbdata(VTermScreen *screen); - -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_ALL_ATTRS_MASK = (1 << 9) - 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); - -// --------- -// 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 diff --git a/include/vterm_keycodes.h b/include/vterm_keycodes.h deleted file mode 100644 index 661759f..0000000 --- a/include/vterm_keycodes.h +++ /dev/null @@ -1,61 +0,0 @@ -#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 diff --git a/src/encoding.c b/src/encoding.c deleted file mode 100644 index 434ac3f..0000000 --- a/src/encoding.c +++ /dev/null @@ -1,230 +0,0 @@ -#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; -} diff --git a/src/encoding/DECdrawing.inc b/src/encoding/DECdrawing.inc deleted file mode 100644 index 47093ed..0000000 --- a/src/encoding/DECdrawing.inc +++ /dev/null @@ -1,36 +0,0 @@ -static const struct StaticTableEncoding encoding_DECdrawing = { - { .decode = &decode_table }, - { - [0x60] = 0x25C6, - [0x61] = 0x2592, - [0x62] = 0x2409, - [0x63] = 0x240C, - [0x64] = 0x240D, - [0x65] = 0x240A, - [0x66] = 0x00B0, - [0x67] = 0x00B1, - [0x68] = 0x2424, - [0x69] = 0x240B, - [0x6a] = 0x2518, - [0x6b] = 0x2510, - [0x6c] = 0x250C, - [0x6d] = 0x2514, - [0x6e] = 0x253C, - [0x6f] = 0x23BA, - [0x70] = 0x23BB, - [0x71] = 0x2500, - [0x72] = 0x23BC, - [0x73] = 0x23BD, - [0x74] = 0x251C, - [0x75] = 0x2524, - [0x76] = 0x2534, - [0x77] = 0x252C, - [0x78] = 0x2502, - [0x79] = 0x2A7D, - [0x7a] = 0x2A7E, - [0x7b] = 0x03C0, - [0x7c] = 0x2260, - [0x7d] = 0x00A3, - [0x7e] = 0x00B7, - } -}; diff --git a/src/encoding/DECdrawing.tbl b/src/encoding/DECdrawing.tbl deleted file mode 100644 index 6e19c50..0000000 --- a/src/encoding/DECdrawing.tbl +++ /dev/null @@ -1,31 +0,0 @@ -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 diff --git a/src/encoding/uk.inc b/src/encoding/uk.inc deleted file mode 100644 index da1445d..0000000 --- a/src/encoding/uk.inc +++ /dev/null @@ -1,6 +0,0 @@ -static const struct StaticTableEncoding encoding_uk = { - { .decode = &decode_table }, - { - [0x23] = 0x00a3, - } -}; diff --git a/src/encoding/uk.tbl b/src/encoding/uk.tbl deleted file mode 100644 index b27b1a2..0000000 --- a/src/encoding/uk.tbl +++ /dev/null @@ -1 +0,0 @@ -2/3 = "£" diff --git a/src/fullwidth.inc b/src/fullwidth.inc deleted file mode 100644 index 7ff142f..0000000 --- a/src/fullwidth.inc +++ /dev/null @@ -1,104 +0,0 @@ - { 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, 0x312d }, - { 0x3131, 0x318e }, - { 0x3190, 0x31ba }, - { 0x31c0, 0x31e3 }, - { 0x31f0, 0x321e }, - { 0x3220, 0x3247 }, - { 0x3250, 0x32fe }, - { 0x3300, 0x4dbf }, - { 0x4e00, 0xa48c }, - { 0xa490, 0xa4c6 }, - { 0xa960, 0xa97c }, - { 0xac00, 0xd7a3 }, - { 0xf900, 0xfaff }, - { 0xfe10, 0xfe19 }, - { 0xfe30, 0xfe52 }, - { 0xfe54, 0xfe66 }, - { 0xfe68, 0xfe6b }, - { 0xff01, 0xff60 }, - { 0xffe0, 0xffe6 }, - { 0x16fe0, 0x16fe0 }, - { 0x17000, 0x187ec }, - { 0x18800, 0x18af2 }, - { 0x1b000, 0x1b001 }, - { 0x1f004, 0x1f004 }, - { 0x1f0cf, 0x1f0cf }, - { 0x1f18e, 0x1f18e }, - { 0x1f191, 0x1f19a }, - { 0x1f200, 0x1f202 }, - { 0x1f210, 0x1f23b }, - { 0x1f240, 0x1f248 }, - { 0x1f250, 0x1f251 }, - { 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 }, - { 0x1f6eb, 0x1f6ec }, - { 0x1f6f4, 0x1f6f6 }, - { 0x1f910, 0x1f91e }, - { 0x1f920, 0x1f927 }, - { 0x1f930, 0x1f930 }, - { 0x1f933, 0x1f93e }, - { 0x1f940, 0x1f94b }, - { 0x1f950, 0x1f95e }, - { 0x1f980, 0x1f991 }, - { 0x1f9c0, 0x1f9c0 }, diff --git a/src/input.c b/src/input.c deleted file mode 100644 index 0eaf0e9..0000000 --- a/src/input.c +++ /dev/null @@ -1,210 +0,0 @@ -#include "vterm_internal.h" - -#include <stdio.h> - -#include "utf8.h" - -void vterm_input_push_char(VTerm *vt, VTermModifier mod, uint32_t c) -{ - /* 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 'h': case 'i': case 'j': case 'm': case '[': - needs_CSIu = 1; - break; - /* Ctrl-\ ] ^ _ don't need CSUu */ - case '\\': case ']': case '^': case '_': - needs_CSIu = 0; - 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 ? "\e" : "", 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, '\e' }, // 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_CSI_CURSOR, 'P' }, // F1 - { KEYCODE_CSI_CURSOR, 'Q' }, // F2 - { KEYCODE_CSI_CURSOR, 'R' }, // F3 - { KEYCODE_CSI_CURSOR, '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_input_push_key(VTerm *vt, VTermModifier mod, VTermKey key) -{ - 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 ? "\e%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; - } -} diff --git a/src/keyboard.c b/src/keyboard.c deleted file mode 100644 index b541fb1..0000000 --- a/src/keyboard.c +++ /dev/null @@ -1,226 +0,0 @@ -#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_CSI_CURSOR, 'P' }, // F1 - { KEYCODE_CSI_CURSOR, 'Q' }, // F2 - { KEYCODE_CSI_CURSOR, 'R' }, // F3 - { KEYCODE_CSI_CURSOR, '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~"); -} diff --git a/src/mouse.c b/src/mouse.c deleted file mode 100644 index 9962e4f..0000000 --- a/src/mouse.c +++ /dev/null @@ -1,96 +0,0 @@ -#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(button < 4) { - output_mouse(state, button-1, pressed, mod, state->mouse_col, state->mouse_row); - } - else if(button < 6) { - output_mouse(state, button-4 + 0x40, pressed, mod, state->mouse_col, state->mouse_row); - } -} diff --git a/src/parser.c b/src/parser.c deleted file mode 100644 index a01cd71..0000000 --- a/src/parser.c +++ /dev/null @@ -1,340 +0,0 @@ -#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.csi_leader); - for(int argi = 0; argi < vt->parser.csi_argi; argi++) { - printf(" %lu", CSI_ARG(vt->parser.csi_args[argi])); - if(!CSI_ARG_HAS_MORE(vt->parser.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.csi_leaderlen ? vt->parser.csi_leader : NULL, - vt->parser.csi_args, - vt->parser.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 append_strbuffer(VTerm *vt, const char *str, size_t len) -{ - if(len > vt->parser.strbuffer_len - vt->parser.strbuffer_cur) { - len = vt->parser.strbuffer_len - vt->parser.strbuffer_cur; - DEBUG_LOG("Truncating strbuffer preserve to %zd bytes\n", len); - } - - if(len > 0) { - strncpy(vt->parser.strbuffer + vt->parser.strbuffer_cur, str, len); - vt->parser.strbuffer_cur += len; - } -} - -static void start_string(VTerm *vt, VTermParserStringType type) -{ - vt->parser.stringtype = type; - - vt->parser.strbuffer_cur = 0; -} - -static void more_string(VTerm *vt, const char *str, size_t len) -{ - append_strbuffer(vt, str, len); -} - -static void done_string(VTerm *vt, const char *str, size_t len) -{ - if(vt->parser.strbuffer_cur) { - if(str) - append_strbuffer(vt, str, len); - - str = vt->parser.strbuffer; - len = vt->parser.strbuffer_cur; - } - else if(!str) { - DEBUG_LOG("parser.c: TODO: No strbuffer _and_ no final fragment???\n"); - len = 0; - } - - switch(vt->parser.stringtype) { - case VTERM_PARSER_OSC: - if(vt->parser.callbacks && vt->parser.callbacks->osc) - if((*vt->parser.callbacks->osc)(str, len, vt->parser.cbdata)) - return; - - DEBUG_LOG("libvterm: Unhandled OSC %.*s\n", (int)len, str); - return; - - case VTERM_PARSER_DCS: - if(vt->parser.callbacks && vt->parser.callbacks->dcs) - if((*vt->parser.callbacks->dcs)(str, len, vt->parser.cbdata)) - return; - - DEBUG_LOG("libvterm: Unhandled DCS %.*s\n", (int)len, str); - return; - - case VTERM_N_PARSER_TYPES: - return; - } -} - -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 ESC: - string_start = NULL; - break; - case STRING: - case ESC_IN_STRING: - string_start = bytes; - break; - } - -#define ENTER_STRING_STATE(st) do { vt->parser.state = STRING; string_start = bytes + pos + 1; } while(0) -#define ENTER_STATE(st) do { vt->parser.state = st; string_start = NULL; } while(0) -#define ENTER_NORMAL_STATE() ENTER_STATE(NORMAL) - - for( ; pos < len; pos++) { - unsigned char c = bytes[pos]; - - if(c == 0x00 || c == 0x7f) { // NUL, DEL - if(vt->parser.state >= STRING) { - more_string(vt, string_start, bytes + pos - string_start); - string_start = bytes + pos + 1; - } - continue; - } - if(c == 0x18 || c == 0x1a) { // CAN, SUB - ENTER_NORMAL_STATE(); - continue; - } - else if(c == 0x1b) { // ESC - vt->parser.intermedlen = 0; - if(vt->parser.state == STRING) - vt->parser.state = ESC_IN_STRING; - else - ENTER_STATE(ESC); - continue; - } - else if(c == 0x07 && // BEL, can stand for ST in OSC or DCS state - vt->parser.state == STRING) { - // fallthrough - } - else if(c < 0x20) { // other C0 - if(vt->parser.state >= STRING) - more_string(vt, string_start, bytes + pos - string_start); - do_control(vt, c); - if(vt->parser.state >= STRING) - string_start = bytes + pos + 1; - continue; - } - // else fallthrough - - switch(vt->parser.state) { - case ESC_IN_STRING: - if(c == 0x5c) { // ST - vt->parser.state = STRING; - done_string(vt, string_start, bytes + pos - string_start - 1); - ENTER_NORMAL_STATE(); - break; - } - vt->parser.state = ESC; - // else fallthrough - - case ESC: - switch(c) { - case 0x50: // DCS - start_string(vt, VTERM_PARSER_DCS); - ENTER_STRING_STATE(); - break; - case 0x5b: // CSI - vt->parser.csi_leaderlen = 0; - ENTER_STATE(CSI_LEADER); - break; - case 0x5d: // OSC - start_string(vt, VTERM_PARSER_OSC); - ENTER_STRING_STATE(); - break; - default: - if(is_intermed(c)) { - if(vt->parser.intermedlen < INTERMED_MAX-1) - vt->parser.intermed[vt->parser.intermedlen++] = c; - } - else if(!vt->parser.intermedlen && c >= 0x40 && c < 0x60) { - do_control(vt, c + 0x40); - ENTER_NORMAL_STATE(); - } - else if(c >= 0x30 && c < 0x7f) { - do_escape(vt, c); - ENTER_NORMAL_STATE(); - } - else { - DEBUG_LOG("TODO: Unhandled byte %02x in Escape\n", c); - } - } - break; - - case CSI_LEADER: - /* Extract leader bytes 0x3c to 0x3f */ - if(c >= 0x3c && c <= 0x3f) { - if(vt->parser.csi_leaderlen < CSI_LEADER_MAX-1) - vt->parser.csi_leader[vt->parser.csi_leaderlen++] = c; - break; - } - - /* else fallthrough */ - vt->parser.csi_leader[vt->parser.csi_leaderlen] = 0; - - vt->parser.csi_argi = 0; - vt->parser.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.csi_args[vt->parser.csi_argi] == CSI_ARG_MISSING) - vt->parser.csi_args[vt->parser.csi_argi] = 0; - vt->parser.csi_args[vt->parser.csi_argi] *= 10; - vt->parser.csi_args[vt->parser.csi_argi] += c - '0'; - break; - } - if(c == ':') { - vt->parser.csi_args[vt->parser.csi_argi] |= CSI_ARG_FLAG_MORE; - c = ';'; - } - if(c == ';') { - vt->parser.csi_argi++; - vt->parser.csi_args[vt->parser.csi_argi] = CSI_ARG_MISSING; - break; - } - - /* else fallthrough */ - vt->parser.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 STRING: - if(c == 0x07 || (c == 0x9c && !vt->mode.utf8)) { - done_string(vt, string_start, bytes + pos - string_start); - ENTER_NORMAL_STATE(); - } - break; - - case NORMAL: - if(c >= 0x80 && c < 0xa0 && !vt->mode.utf8) { - switch(c) { - case 0x90: // DCS - start_string(vt, VTERM_PARSER_DCS); - ENTER_STRING_STATE(); - break; - case 0x9b: // CSI - ENTER_STATE(CSI_LEADER); - break; - case 0x9d: // OSC - start_string(vt, VTERM_PARSER_OSC); - ENTER_STRING_STATE(); - 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; - } - } - - 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; -} diff --git a/src/pen.c b/src/pen.c deleted file mode 100644 index 7488203..0000000 --- a/src/pen.c +++ /dev/null @@ -1,535 +0,0 @@ -#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.strike = 0; setpenattr_bool(state, VTERM_ATTR_STRIKE, 0); - state->pen.font = 0; setpenattr_int( state, VTERM_ATTR_FONT, 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_STRIKE, state->pen.strike); - setpenattr_int( state, VTERM_ATTR_FONT, state->pen.font); - 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) -{ - /* Copy the given colors */ - state->default_fg = *default_fg; - state->default_bg = *default_bg; - - /* Make sure the correct type flags are set */ - state->default_fg.type = (state->default_fg.type & ~VTERM_COLOR_DEFAULT_MASK) - | VTERM_COLOR_DEFAULT_FG; - 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 single - state->pen.underline = 1; - setpenattr_int(state, VTERM_ATTR_UNDERLINE, 1); - 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 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 = 2; - setpenattr_int(state, VTERM_ATTR_UNDERLINE, 2); - 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 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 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 %lu\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 == 1) - args[argi++] = 4; - - if(state->pen.blink) - args[argi++] = 5; - - if(state->pen.reverse) - args[argi++] = 7; - - if(state->pen.strike) - args[argi++] = 9; - - if(state->pen.font) - args[argi++] = 10 + state->pen.font; - - if(state->pen.underline == 2) - args[argi++] = 21; - - argi = vterm_state_getpen_color(&state->pen.fg, argi, args, true); - - argi = vterm_state_getpen_color(&state->pen.bg, argi, args, false); - - 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_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_N_ATTRS: - return 0; - } - - return 0; -} diff --git a/src/rect.h b/src/rect.h deleted file mode 100644 index 2114f24..0000000 --- a/src/rect.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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; -} diff --git a/src/screen.c b/src/screen.c deleted file mode 100644 index 1d4d86c..0000000 --- a/src/screen.c +++ /dev/null @@ -1,936 +0,0 @@ -#include "vterm_internal.h" - -#include <stdio.h> -#include <string.h> - -#include "rect.h" -#include "utf8.h" - -#define UNICODE_SPACE 0x20 -#define UNICODE_LINEFEED 0x0a - -/* State of the pen at some moment in time, also used in a cell */ -typedef struct -{ - /* After the bitfield */ - VTermColor fg, bg; - - unsigned int bold : 1; - unsigned int underline : 2; - unsigned int italic : 1; - unsigned int blink : 1; - unsigned int reverse : 1; - unsigned int strike : 1; - unsigned int font : 4; /* 0 to 9 */ - - /* Extra state storage that isn't strictly pen-related */ - unsigned int protected_cell : 1; - unsigned int dwl : 1; /* on a DECDWL or DECDHL line */ - unsigned int dhl : 2; /* on a DECDHL line (1=top 2=bottom) */ -} ScreenPen; - -/* Internal representation of a screen cell */ -typedef struct -{ - uint32_t chars[VTERM_MAX_CHARS_PER_CELL]; - ScreenPen pen; -} ScreenCell; - -static int vterm_screen_set_cell(VTermScreen *screen, VTermPos pos, const VTermScreenCell *cell); - -struct VTermScreen -{ - VTerm *vt; - VTermState *state; - - const VTermScreenCallbacks *callbacks; - void *cbdata; - - VTermDamageSize damage_merge; - /* start_row == -1 => no damage */ - VTermRect damaged; - VTermRect pending_scrollrect; - int pending_scroll_downward, pending_scroll_rightward; - - int rows; - int cols; - int global_reverse; - - /* Primary and Altscreen. buffers[1] is lazily allocated as needed */ - ScreenCell *buffers[2]; - - /* buffer will == buffers[0] or buffers[1], depending on altscreen */ - ScreenCell *buffer; - - /* buffer for a single screen row used in scrollback storage callbacks */ - VTermScreenCell *sb_buffer; - - ScreenPen pen; -}; - -static inline ScreenCell *getcell(const VTermScreen *screen, int row, int col) -{ - if(row < 0 || row >= screen->rows) - return NULL; - if(col < 0 || col >= screen->cols) - return NULL; - return screen->buffer + (screen->cols * row) + col; -} - -static ScreenCell *realloc_buffer(VTermScreen *screen, ScreenCell *buffer, int new_rows, int new_cols) -{ - ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt, sizeof(ScreenCell) * new_rows * new_cols); - - for(int row = 0; row < new_rows; row++) { - for(int col = 0; col < new_cols; col++) { - ScreenCell *new_cell = new_buffer + row*new_cols + col; - - if(buffer && row < screen->rows && col < screen->cols) - *new_cell = buffer[row * screen->cols + col]; - else { - new_cell->chars[0] = 0; - new_cell->pen = screen->pen; - } - } - } - - if(buffer) - vterm_allocator_free(screen->vt, buffer); - - return new_buffer; -} - -static void damagerect(VTermScreen *screen, VTermRect rect) -{ - VTermRect emit; - - switch(screen->damage_merge) { - case VTERM_DAMAGE_CELL: - /* Always emit damage event */ - emit = rect; - break; - - case VTERM_DAMAGE_ROW: - /* Emit damage longer than one row. Try to merge with existing damage in - * the same row */ - if(rect.end_row > rect.start_row + 1) { - // Bigger than 1 line - flush existing, emit this - vterm_screen_flush_damage(screen); - emit = rect; - } - else if(screen->damaged.start_row == -1) { - // None stored yet - screen->damaged = rect; - return; - } - else if(rect.start_row == screen->damaged.start_row) { - // Merge with the stored line - if(screen->damaged.start_col > rect.start_col) - screen->damaged.start_col = rect.start_col; - if(screen->damaged.end_col < rect.end_col) - screen->damaged.end_col = rect.end_col; - return; - } - else { - // Emit the currently stored line, store a new one - emit = screen->damaged; - screen->damaged = rect; - } - break; - - case VTERM_DAMAGE_SCREEN: - case VTERM_DAMAGE_SCROLL: - /* Never emit damage event */ - if(screen->damaged.start_row == -1) - screen->damaged = rect; - else { - rect_expand(&screen->damaged, &rect); - } - return; - - default: - DEBUG_LOG("TODO: Maybe merge damage for level %d\n", screen->damage_merge); - return; - } - - if(screen->callbacks && screen->callbacks->damage) - (*screen->callbacks->damage)(emit, screen->cbdata); -} - -static void damagescreen(VTermScreen *screen) -{ - VTermRect rect = { - .start_row = 0, - .end_row = screen->rows, - .start_col = 0, - .end_col = screen->cols, - }; - - damagerect(screen, rect); -} - -static int putglyph(VTermGlyphInfo *info, VTermPos pos, void *user) -{ - VTermScreen *screen = user; - ScreenCell *cell = getcell(screen, pos.row, pos.col); - - if(!cell) - return 0; - - int i; - for(i = 0; i < VTERM_MAX_CHARS_PER_CELL && info->chars[i]; i++) { - cell->chars[i] = info->chars[i]; - cell->pen = screen->pen; - } - if(i < VTERM_MAX_CHARS_PER_CELL) - cell->chars[i] = 0; - - for(int col = 1; col < info->width; col++) - getcell(screen, pos.row, pos.col + col)->chars[0] = (uint32_t)-1; - - VTermRect rect = { - .start_row = pos.row, - .end_row = pos.row+1, - .start_col = pos.col, - .end_col = pos.col+info->width, - }; - - cell->pen.protected_cell = info->protected_cell; - cell->pen.dwl = info->dwl; - cell->pen.dhl = info->dhl; - - damagerect(screen, rect); - - return 1; -} - -static int moverect_internal(VTermRect dest, VTermRect src, void *user) -{ - VTermScreen *screen = user; - - if(screen->callbacks && screen->callbacks->sb_pushline && - dest.start_row == 0 && dest.start_col == 0 && // starts top-left corner - dest.end_col == screen->cols && // full width - screen->buffer == screen->buffers[0]) { // not altscreen - VTermPos pos; - for(pos.row = 0; pos.row < src.start_row; pos.row++) { - for(pos.col = 0; pos.col < screen->cols; pos.col++) - vterm_screen_get_cell(screen, pos, screen->sb_buffer + pos.col); - - (screen->callbacks->sb_pushline)(screen->cols, screen->sb_buffer, screen->cbdata); - } - } - - int cols = src.end_col - src.start_col; - int downward = src.start_row - dest.start_row; - - int init_row, test_row, inc_row; - if(downward < 0) { - init_row = dest.end_row - 1; - test_row = dest.start_row - 1; - inc_row = -1; - } - else { - init_row = dest.start_row; - test_row = dest.end_row; - inc_row = +1; - } - - for(int row = init_row; row != test_row; row += inc_row) - memmove(getcell(screen, row, dest.start_col), - getcell(screen, row + downward, src.start_col), - cols * sizeof(ScreenCell)); - - return 1; -} - -static int moverect_user(VTermRect dest, VTermRect src, void *user) -{ - VTermScreen *screen = user; - - if(screen->callbacks && screen->callbacks->moverect) { - if(screen->damage_merge != VTERM_DAMAGE_SCROLL) - // Avoid an infinite loop - vterm_screen_flush_damage(screen); - - if((*screen->callbacks->moverect)(dest, src, screen->cbdata)) - return 1; - } - - damagerect(screen, dest); - - return 1; -} - -static int erase_internal(VTermRect rect, int selective, void *user) -{ - VTermScreen *screen = user; - - for(int row = rect.start_row; row < screen->state->rows && row < rect.end_row; row++) { - const VTermLineInfo *info = vterm_state_get_lineinfo(screen->state, row); - - for(int col = rect.start_col; col < rect.end_col; col++) { - ScreenCell *cell = getcell(screen, row, col); - - if(selective && cell->pen.protected_cell) - continue; - - cell->chars[0] = 0; - cell->pen = screen->pen; - cell->pen.dwl = info->doublewidth; - cell->pen.dhl = info->doubleheight; - } - } - - return 1; -} - -static int erase_user(VTermRect rect, int selective, void *user) -{ - VTermScreen *screen = user; - - damagerect(screen, rect); - - return 1; -} - -static int erase(VTermRect rect, int selective, void *user) -{ - erase_internal(rect, selective, user); - return erase_user(rect, 0, user); -} - -static int scrollrect(VTermRect rect, int downward, int rightward, void *user) -{ - VTermScreen *screen = user; - - if(screen->damage_merge != VTERM_DAMAGE_SCROLL) { - vterm_scroll_rect(rect, downward, rightward, - moverect_internal, erase_internal, screen); - - vterm_screen_flush_damage(screen); - - vterm_scroll_rect(rect, downward, rightward, - moverect_user, erase_user, screen); - - return 1; - } - - if(screen->damaged.start_row != -1 && - !rect_intersects(&rect, &screen->damaged)) { - vterm_screen_flush_damage(screen); - } - - if(screen->pending_scrollrect.start_row == -1) { - screen->pending_scrollrect = rect; - screen->pending_scroll_downward = downward; - screen->pending_scroll_rightward = rightward; - } - else if(rect_equal(&screen->pending_scrollrect, &rect) && - ((screen->pending_scroll_downward == 0 && downward == 0) || - (screen->pending_scroll_rightward == 0 && rightward == 0))) { - screen->pending_scroll_downward += downward; - screen->pending_scroll_rightward += rightward; - } - else { - vterm_screen_flush_damage(screen); - - screen->pending_scrollrect = rect; - screen->pending_scroll_downward = downward; - screen->pending_scroll_rightward = rightward; - } - - vterm_scroll_rect(rect, downward, rightward, - moverect_internal, erase_internal, screen); - - if(screen->damaged.start_row == -1) - return 1; - - if(rect_contains(&rect, &screen->damaged)) { - /* Scroll region entirely contains the damage; just move it */ - vterm_rect_move(&screen->damaged, -downward, -rightward); - rect_clip(&screen->damaged, &rect); - } - /* There are a number of possible cases here, but lets restrict this to only - * the common case where we might actually gain some performance by - * optimising it. Namely, a vertical scroll that neatly cuts the damage - * region in half. - */ - else if(rect.start_col <= screen->damaged.start_col && - rect.end_col >= screen->damaged.end_col && - rightward == 0) { - if(screen->damaged.start_row >= rect.start_row && - screen->damaged.start_row < rect.end_row) { - screen->damaged.start_row -= downward; - if(screen->damaged.start_row < rect.start_row) - screen->damaged.start_row = rect.start_row; - if(screen->damaged.start_row > rect.end_row) - screen->damaged.start_row = rect.end_row; - } - if(screen->damaged.end_row >= rect.start_row && - screen->damaged.end_row < rect.end_row) { - screen->damaged.end_row -= downward; - if(screen->damaged.end_row < rect.start_row) - screen->damaged.end_row = rect.start_row; - if(screen->damaged.end_row > rect.end_row) - screen->damaged.end_row = rect.end_row; - } - } - else { - DEBUG_LOG("TODO: Just flush and redo damaged=" STRFrect " rect=" STRFrect "\n", - ARGSrect(screen->damaged), ARGSrect(rect)); - } - - return 1; -} - -static int movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user) -{ - VTermScreen *screen = user; - - if(screen->callbacks && screen->callbacks->movecursor) - return (*screen->callbacks->movecursor)(pos, oldpos, visible, screen->cbdata); - - return 0; -} - -static int setpenattr(VTermAttr attr, VTermValue *val, void *user) -{ - VTermScreen *screen = user; - - switch(attr) { - case VTERM_ATTR_BOLD: - screen->pen.bold = val->boolean; - return 1; - case VTERM_ATTR_UNDERLINE: - screen->pen.underline = val->number; - return 1; - case VTERM_ATTR_ITALIC: - screen->pen.italic = val->boolean; - return 1; - case VTERM_ATTR_BLINK: - screen->pen.blink = val->boolean; - return 1; - case VTERM_ATTR_REVERSE: - screen->pen.reverse = val->boolean; - return 1; - case VTERM_ATTR_STRIKE: - screen->pen.strike = val->boolean; - return 1; - case VTERM_ATTR_FONT: - screen->pen.font = val->number; - return 1; - case VTERM_ATTR_FOREGROUND: - screen->pen.fg = val->color; - return 1; - case VTERM_ATTR_BACKGROUND: - screen->pen.bg = val->color; - return 1; - - case VTERM_N_ATTRS: - return 0; - } - - return 0; -} - -static int settermprop(VTermProp prop, VTermValue *val, void *user) -{ - VTermScreen *screen = user; - - switch(prop) { - case VTERM_PROP_ALTSCREEN: - if(val->boolean && !screen->buffers[1]) - return 0; - - screen->buffer = val->boolean ? screen->buffers[1] : screen->buffers[0]; - /* only send a damage event on disable; because during enable there's an - * erase that sends a damage anyway - */ - if(!val->boolean) - damagescreen(screen); - break; - case VTERM_PROP_REVERSE: - screen->global_reverse = val->boolean; - damagescreen(screen); - break; - default: - ; /* ignore */ - } - - if(screen->callbacks && screen->callbacks->settermprop) - return (*screen->callbacks->settermprop)(prop, val, screen->cbdata); - - return 1; -} - -static int bell(void *user) -{ - VTermScreen *screen = user; - - if(screen->callbacks && screen->callbacks->bell) - return (*screen->callbacks->bell)(screen->cbdata); - - return 0; -} - -static int resize(int new_rows, int new_cols, VTermPos *delta, void *user) -{ - VTermScreen *screen = user; - - int is_altscreen = (screen->buffers[1] && screen->buffer == screen->buffers[1]); - - int old_rows = screen->rows; - int old_cols = screen->cols; - - if(!is_altscreen && new_rows < old_rows) { - // Fewer rows - determine if we're going to scroll at all, and if so, push - // those lines to scrollback - VTermPos pos = { 0, 0 }; - VTermPos cursor = screen->state->pos; - // Find the first blank row after the cursor. - for(pos.row = old_rows - 1; pos.row >= new_rows; pos.row--) - if(!vterm_screen_is_eol(screen, pos) || cursor.row == pos.row) - break; - - int first_blank_row = pos.row + 1; - if(first_blank_row > new_rows) { - VTermRect rect = { - .start_row = 0, - .end_row = old_rows, - .start_col = 0, - .end_col = old_cols, - }; - scrollrect(rect, first_blank_row - new_rows, 0, user); - vterm_screen_flush_damage(screen); - - delta->row -= first_blank_row - new_rows; - } - } - - screen->buffers[0] = realloc_buffer(screen, screen->buffers[0], new_rows, new_cols); - if(screen->buffers[1]) - screen->buffers[1] = realloc_buffer(screen, screen->buffers[1], new_rows, new_cols); - - screen->buffer = is_altscreen ? screen->buffers[1] : screen->buffers[0]; - - screen->rows = new_rows; - screen->cols = new_cols; - - if(screen->sb_buffer) - vterm_allocator_free(screen->vt, screen->sb_buffer); - - screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * new_cols); - - if(new_cols > old_cols) { - VTermRect rect = { - .start_row = 0, - .end_row = old_rows, - .start_col = old_cols, - .end_col = new_cols, - }; - damagerect(screen, rect); - } - - if(new_rows > old_rows) { - if(!is_altscreen && screen->callbacks && screen->callbacks->sb_popline) { - int rows = new_rows - old_rows; - while(rows) { - if(!(screen->callbacks->sb_popline(screen->cols, screen->sb_buffer, screen->cbdata))) - break; - - VTermRect rect = { - .start_row = 0, - .end_row = screen->rows, - .start_col = 0, - .end_col = screen->cols, - }; - scrollrect(rect, -1, 0, user); - - VTermPos pos = { 0, 0 }; - for(pos.col = 0; pos.col < screen->cols; pos.col += screen->sb_buffer[pos.col].width) - vterm_screen_set_cell(screen, pos, screen->sb_buffer + pos.col); - - rect.end_row = 1; - damagerect(screen, rect); - - vterm_screen_flush_damage(screen); - - rows--; - delta->row++; - } - } - - VTermRect rect = { - .start_row = old_rows, - .end_row = new_rows, - .start_col = 0, - .end_col = new_cols, - }; - damagerect(screen, rect); - } - - if(screen->callbacks && screen->callbacks->resize) - return (*screen->callbacks->resize)(new_rows, new_cols, screen->cbdata); - - return 1; -} - -static int setlineinfo(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user) -{ - VTermScreen *screen = user; - - if(newinfo->doublewidth != oldinfo->doublewidth || - newinfo->doubleheight != oldinfo->doubleheight) { - for(int col = 0; col < screen->cols; col++) { - ScreenCell *cell = getcell(screen, row, col); - cell->pen.dwl = newinfo->doublewidth; - cell->pen.dhl = newinfo->doubleheight; - } - - VTermRect rect = { - .start_row = row, - .end_row = row + 1, - .start_col = 0, - .end_col = newinfo->doublewidth ? screen->cols / 2 : screen->cols, - }; - damagerect(screen, rect); - - if(newinfo->doublewidth) { - rect.start_col = screen->cols / 2; - rect.end_col = screen->cols; - - erase_internal(rect, 0, user); - } - } - - return 1; -} - -static VTermStateCallbacks state_cbs = { - .putglyph = &putglyph, - .movecursor = &movecursor, - .scrollrect = &scrollrect, - .erase = &erase, - .setpenattr = &setpenattr, - .settermprop = &settermprop, - .bell = &bell, - .resize = &resize, - .setlineinfo = &setlineinfo, -}; - -static VTermScreen *screen_new(VTerm *vt) -{ - VTermState *state = vterm_obtain_state(vt); - if(!state) - return NULL; - - VTermScreen *screen = vterm_allocator_malloc(vt, sizeof(VTermScreen)); - int rows, cols; - - vterm_get_size(vt, &rows, &cols); - - screen->vt = vt; - screen->state = state; - - screen->damage_merge = VTERM_DAMAGE_CELL; - screen->damaged.start_row = -1; - screen->pending_scrollrect.start_row = -1; - - screen->rows = rows; - screen->cols = cols; - - screen->callbacks = NULL; - screen->cbdata = NULL; - - screen->buffers[0] = realloc_buffer(screen, NULL, rows, cols); - - screen->buffer = screen->buffers[0]; - - screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * cols); - - vterm_state_set_callbacks(screen->state, &state_cbs, screen); - - return screen; -} - -INTERNAL void vterm_screen_free(VTermScreen *screen) -{ - vterm_allocator_free(screen->vt, screen->buffers[0]); - if(screen->buffers[1]) - vterm_allocator_free(screen->vt, screen->buffers[1]); - - vterm_allocator_free(screen->vt, screen->sb_buffer); - - vterm_allocator_free(screen->vt, screen); -} - -void vterm_screen_reset(VTermScreen *screen, int hard) -{ - screen->damaged.start_row = -1; - screen->pending_scrollrect.start_row = -1; - vterm_state_reset(screen->state, hard); - vterm_screen_flush_damage(screen); -} - -static size_t _get_chars(const VTermScreen *screen, const int utf8, void *buffer, size_t len, const VTermRect rect) -{ - size_t outpos = 0; - int padding = 0; - -#define PUT(c) \ - if(utf8) { \ - size_t thislen = utf8_seqlen(c); \ - if(buffer && outpos + thislen <= len) \ - outpos += fill_utf8((c), (char *)buffer + outpos); \ - else \ - outpos += thislen; \ - } \ - else { \ - if(buffer && outpos + 1 <= len) \ - ((uint32_t*)buffer)[outpos++] = (c); \ - else \ - outpos++; \ - } - - for(int row = rect.start_row; row < rect.end_row; row++) { - for(int col = rect.start_col; col < rect.end_col; col++) { - ScreenCell *cell = getcell(screen, row, col); - - if(cell->chars[0] == 0) - // Erased cell, might need a space - padding++; - else if(cell->chars[0] == (uint32_t)-1) - // Gap behind a double-width char, do nothing - ; - else { - while(padding) { - PUT(UNICODE_SPACE); - padding--; - } - for(int i = 0; i < VTERM_MAX_CHARS_PER_CELL && cell->chars[i]; i++) { - PUT(cell->chars[i]); - } - } - } - - if(row < rect.end_row - 1) { - PUT(UNICODE_LINEFEED); - padding = 0; - } - } - - return outpos; -} - -size_t vterm_screen_get_chars(const VTermScreen *screen, uint32_t *chars, size_t len, const VTermRect rect) -{ - return _get_chars(screen, 0, chars, len, rect); -} - -size_t vterm_screen_get_text(const VTermScreen *screen, char *str, size_t len, const VTermRect rect) -{ - return _get_chars(screen, 1, str, len, rect); -} - -/* Copy internal to external representation of a screen cell */ -int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCell *cell) -{ - ScreenCell *intcell = getcell(screen, pos.row, pos.col); - if(!intcell) - return 0; - - for(int i = 0; ; i++) { - cell->chars[i] = intcell->chars[i]; - if(!intcell->chars[i]) - break; - } - - cell->attrs.bold = intcell->pen.bold; - cell->attrs.underline = intcell->pen.underline; - cell->attrs.italic = intcell->pen.italic; - cell->attrs.blink = intcell->pen.blink; - cell->attrs.reverse = intcell->pen.reverse ^ screen->global_reverse; - cell->attrs.strike = intcell->pen.strike; - cell->attrs.font = intcell->pen.font; - - cell->attrs.dwl = intcell->pen.dwl; - cell->attrs.dhl = intcell->pen.dhl; - - cell->fg = intcell->pen.fg; - cell->bg = intcell->pen.bg; - - if(pos.col < (screen->cols - 1) && - getcell(screen, pos.row, pos.col + 1)->chars[0] == (uint32_t)-1) - cell->width = 2; - else - cell->width = 1; - - return 1; -} - -/* Copy external to internal representation of a screen cell */ -/* static because it's only used internally for sb_popline during resize */ -static int vterm_screen_set_cell(VTermScreen *screen, VTermPos pos, const VTermScreenCell *cell) -{ - ScreenCell *intcell = getcell(screen, pos.row, pos.col); - if(!intcell) - return 0; - - for(int i = 0; ; i++) { - intcell->chars[i] = cell->chars[i]; - if(!cell->chars[i]) - break; - } - - intcell->pen.bold = cell->attrs.bold; - intcell->pen.underline = cell->attrs.underline; - intcell->pen.italic = cell->attrs.italic; - intcell->pen.blink = cell->attrs.blink; - intcell->pen.reverse = cell->attrs.reverse ^ screen->global_reverse; - intcell->pen.strike = cell->attrs.strike; - intcell->pen.font = cell->attrs.font; - - intcell->pen.fg = cell->fg; - intcell->pen.bg = cell->bg; - - if(cell->width == 2) - getcell(screen, pos.row, pos.col + 1)->chars[0] = (uint32_t)-1; - - return 1; -} - -int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos) -{ - /* This cell is EOL if this and every cell to the right is black */ - for(; pos.col < screen->cols; pos.col++) { - ScreenCell *cell = getcell(screen, pos.row, pos.col); - if(cell->chars[0] != 0) - return 0; - } - - return 1; -} - -VTermScreen *vterm_obtain_screen(VTerm *vt) -{ - if(vt->screen) - return vt->screen; - - VTermScreen *screen = screen_new(vt); - vt->screen = screen; - - return screen; -} - -void vterm_screen_enable_altscreen(VTermScreen *screen, int altscreen) -{ - - if(!screen->buffers[1] && altscreen) { - int rows, cols; - vterm_get_size(screen->vt, &rows, &cols); - - screen->buffers[1] = realloc_buffer(screen, NULL, rows, cols); - } -} - -void vterm_screen_set_callbacks(VTermScreen *screen, const VTermScreenCallbacks *callbacks, void *user) -{ - screen->callbacks = callbacks; - screen->cbdata = user; -} - -void *vterm_screen_get_cbdata(VTermScreen *screen) -{ - return screen->cbdata; -} - -void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermParserCallbacks *fallbacks, void *user) -{ - vterm_state_set_unrecognised_fallbacks(screen->state, fallbacks, user); -} - -void *vterm_screen_get_unrecognised_fbdata(VTermScreen *screen) -{ - return vterm_state_get_unrecognised_fbdata(screen->state); -} - -void vterm_screen_flush_damage(VTermScreen *screen) -{ - if(screen->pending_scrollrect.start_row != -1) { - vterm_scroll_rect(screen->pending_scrollrect, screen->pending_scroll_downward, screen->pending_scroll_rightward, - moverect_user, erase_user, screen); - - screen->pending_scrollrect.start_row = -1; - } - - if(screen->damaged.start_row != -1) { - if(screen->callbacks && screen->callbacks->damage) - (*screen->callbacks->damage)(screen->damaged, screen->cbdata); - - screen->damaged.start_row = -1; - } -} - -void vterm_screen_set_damage_merge(VTermScreen *screen, VTermDamageSize size) -{ - vterm_screen_flush_damage(screen); - screen->damage_merge = size; -} - -static int attrs_differ(VTermAttrMask attrs, ScreenCell *a, ScreenCell *b) -{ - if((attrs & VTERM_ATTR_BOLD_MASK) && (a->pen.bold != b->pen.bold)) - return 1; - if((attrs & VTERM_ATTR_UNDERLINE_MASK) && (a->pen.underline != b->pen.underline)) - return 1; - if((attrs & VTERM_ATTR_ITALIC_MASK) && (a->pen.italic != b->pen.italic)) - return 1; - if((attrs & VTERM_ATTR_BLINK_MASK) && (a->pen.blink != b->pen.blink)) - return 1; - if((attrs & VTERM_ATTR_REVERSE_MASK) && (a->pen.reverse != b->pen.reverse)) - return 1; - if((attrs & VTERM_ATTR_STRIKE_MASK) && (a->pen.strike != b->pen.strike)) - return 1; - if((attrs & VTERM_ATTR_FONT_MASK) && (a->pen.font != b->pen.font)) - return 1; - if((attrs & VTERM_ATTR_FOREGROUND_MASK) && !vterm_color_is_equal(&a->pen.fg, &b->pen.fg)) - return 1; - if((attrs & VTERM_ATTR_BACKGROUND_MASK) && !vterm_color_is_equal(&a->pen.bg, &b->pen.bg)) - return 1; - - return 0; -} - -int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs) -{ - ScreenCell *target = getcell(screen, pos.row, pos.col); - - // TODO: bounds check - extent->start_row = pos.row; - extent->end_row = pos.row + 1; - - if(extent->start_col < 0) - extent->start_col = 0; - if(extent->end_col < 0) - extent->end_col = screen->cols; - - int col; - - for(col = pos.col - 1; col >= extent->start_col; col--) - if(attrs_differ(attrs, target, getcell(screen, pos.row, col))) - break; - extent->start_col = col + 1; - - for(col = pos.col + 1; col < extent->end_col; col++) - if(attrs_differ(attrs, target, getcell(screen, pos.row, col))) - break; - extent->end_col = col - 1; - - return 1; -} - -void vterm_screen_convert_color_to_rgb(const VTermScreen *screen, VTermColor *col) -{ - vterm_state_convert_color_to_rgb(screen->state, col); -} diff --git a/src/state.c b/src/state.c deleted file mode 100644 index 68cc4f6..0000000 --- a/src/state.c +++ /dev/null @@ -1,1868 +0,0 @@ -#include "vterm_internal.h" - -#include <stdio.h> -#include <string.h> - -#define strneq(a,b,n) (strncmp(a,b,n)==0) - -#if defined(DEBUG) && DEBUG > 1 -# define DEBUG_GLYPH_COMBINE -#endif - -/* Some convenient wrappers to make callback functions easier */ - -static void putglyph(VTermState *state, const uint32_t chars[], int width, VTermPos pos) -{ - VTermGlyphInfo info = { - .chars = chars, - .width = width, - .protected_cell = state->protected_cell, - .dwl = state->lineinfo[pos.row].doublewidth, - .dhl = state->lineinfo[pos.row].doubleheight, - }; - - if(state->callbacks && state->callbacks->putglyph) - if((*state->callbacks->putglyph)(&info, pos, state->cbdata)) - return; - - DEBUG_LOG("libvterm: Unhandled putglyph U+%04x at (%d,%d)\n", chars[0], pos.col, pos.row); -} - -static void updatecursor(VTermState *state, VTermPos *oldpos, int cancel_phantom) -{ - if(state->pos.col == oldpos->col && state->pos.row == oldpos->row) - return; - - if(cancel_phantom) - state->at_phantom = 0; - - if(state->callbacks && state->callbacks->movecursor) - if((*state->callbacks->movecursor)(state->pos, *oldpos, state->mode.cursor_visible, state->cbdata)) - return; -} - -static void erase(VTermState *state, VTermRect rect, int selective) -{ - if(state->callbacks && state->callbacks->erase) - if((*state->callbacks->erase)(rect, selective, state->cbdata)) - return; -} - -static VTermState *vterm_state_new(VTerm *vt) -{ - VTermState *state = vterm_allocator_malloc(vt, sizeof(VTermState)); - - state->vt = vt; - - state->rows = vt->rows; - state->cols = vt->cols; - - state->mouse_col = 0; - state->mouse_row = 0; - state->mouse_buttons = 0; - - state->mouse_protocol = MOUSE_X10; - - state->callbacks = NULL; - state->cbdata = NULL; - - vterm_state_newpen(state); - - state->bold_is_highbright = 0; - - return state; -} - -INTERNAL void vterm_state_free(VTermState *state) -{ - vterm_allocator_free(state->vt, state->tabstops); - vterm_allocator_free(state->vt, state->lineinfo); - vterm_allocator_free(state->vt, state->combine_chars); - vterm_allocator_free(state->vt, state); -} - -static void scroll(VTermState *state, VTermRect rect, int downward, int rightward) -{ - if(!downward && !rightward) - return; - - int rows = rect.end_row - rect.start_row; - if(downward > rows) - downward = rows; - else if(downward < -rows) - downward = -rows; - - int cols = rect.end_col - rect.start_col; - if(rightward > cols) - rightward = cols; - else if(rightward < -cols) - rightward = -cols; - - // Update lineinfo if full line - if(rect.start_col == 0 && rect.end_col == state->cols && rightward == 0) { - int height = rect.end_row - rect.start_row - abs(downward); - - if(downward > 0) - memmove(state->lineinfo + rect.start_row, - state->lineinfo + rect.start_row + downward, - height * sizeof(state->lineinfo[0])); - else - memmove(state->lineinfo + rect.start_row - downward, - state->lineinfo + rect.start_row, - height * sizeof(state->lineinfo[0])); - } - - if(state->callbacks && state->callbacks->scrollrect) - if((*state->callbacks->scrollrect)(rect, downward, rightward, state->cbdata)) - return; - - if(state->callbacks) - vterm_scroll_rect(rect, downward, rightward, - state->callbacks->moverect, state->callbacks->erase, state->cbdata); -} - -static void linefeed(VTermState *state) -{ - if(state->pos.row == SCROLLREGION_BOTTOM(state) - 1) { - VTermRect rect = { - .start_row = state->scrollregion_top, - .end_row = SCROLLREGION_BOTTOM(state), - .start_col = SCROLLREGION_LEFT(state), - .end_col = SCROLLREGION_RIGHT(state), - }; - - scroll(state, rect, 1, 0); - } - else if(state->pos.row < state->rows-1) - state->pos.row++; -} - -static void grow_combine_buffer(VTermState *state) -{ - size_t new_size = state->combine_chars_size * 2; - uint32_t *new_chars = vterm_allocator_malloc(state->vt, new_size * sizeof(new_chars[0])); - - memcpy(new_chars, state->combine_chars, state->combine_chars_size * sizeof(new_chars[0])); - - vterm_allocator_free(state->vt, state->combine_chars); - - state->combine_chars = new_chars; - state->combine_chars_size = new_size; -} - -static void set_col_tabstop(VTermState *state, int col) -{ - unsigned char mask = 1 << (col & 7); - state->tabstops[col >> 3] |= mask; -} - -static void clear_col_tabstop(VTermState *state, int col) -{ - unsigned char mask = 1 << (col & 7); - state->tabstops[col >> 3] &= ~mask; -} - -static int is_col_tabstop(VTermState *state, int col) -{ - unsigned char mask = 1 << (col & 7); - return state->tabstops[col >> 3] & mask; -} - -static int is_cursor_in_scrollregion(const VTermState *state) -{ - if(state->pos.row < state->scrollregion_top || - state->pos.row >= SCROLLREGION_BOTTOM(state)) - return 0; - if(state->pos.col < SCROLLREGION_LEFT(state) || - state->pos.col >= SCROLLREGION_RIGHT(state)) - return 0; - - return 1; -} - -static void tab(VTermState *state, int count, int direction) -{ - while(count > 0) { - if(direction > 0) { - if(state->pos.col >= THISROWWIDTH(state)-1) - return; - - state->pos.col++; - } - else if(direction < 0) { - if(state->pos.col < 1) - return; - - state->pos.col--; - } - - if(is_col_tabstop(state, state->pos.col)) - count--; - } -} - -#define NO_FORCE 0 -#define FORCE 1 - -#define DWL_OFF 0 -#define DWL_ON 1 - -#define DHL_OFF 0 -#define DHL_TOP 1 -#define DHL_BOTTOM 2 - -static void set_lineinfo(VTermState *state, int row, int force, int dwl, int dhl) -{ - VTermLineInfo info = state->lineinfo[row]; - - if(dwl == DWL_OFF) - info.doublewidth = DWL_OFF; - else if(dwl == DWL_ON) - info.doublewidth = DWL_ON; - // else -1 to ignore - - if(dhl == DHL_OFF) - info.doubleheight = DHL_OFF; - else if(dhl == DHL_TOP) - info.doubleheight = DHL_TOP; - else if(dhl == DHL_BOTTOM) - info.doubleheight = DHL_BOTTOM; - - if((state->callbacks && - state->callbacks->setlineinfo && - (*state->callbacks->setlineinfo)(row, &info, state->lineinfo + row, state->cbdata)) - || force) - state->lineinfo[row] = info; -} - -static int on_text(const char bytes[], size_t len, void *user) -{ - VTermState *state = user; - - VTermPos oldpos = state->pos; - - // We'll have at most len codepoints - uint32_t codepoints[len]; - int npoints = 0; - size_t eaten = 0; - - VTermEncodingInstance *encoding = - state->gsingle_set ? &state->encoding[state->gsingle_set] : - !(bytes[eaten] & 0x80) ? &state->encoding[state->gl_set] : - state->vt->mode.utf8 ? &state->encoding_utf8 : - &state->encoding[state->gr_set]; - - (*encoding->enc->decode)(encoding->enc, encoding->data, - codepoints, &npoints, state->gsingle_set ? 1 : len, - bytes, &eaten, len); - - /* There's a chance an encoding (e.g. UTF-8) hasn't found enough bytes yet - * for even a single codepoint - */ - if(!npoints) - return eaten; - - if(state->gsingle_set && npoints) - state->gsingle_set = 0; - - int i = 0; - - /* This is a combining char. that needs to be merged with the previous - * glyph output */ - if(vterm_unicode_is_combining(codepoints[i])) { - /* See if the cursor has moved since */ - if(state->pos.row == state->combine_pos.row && state->pos.col == state->combine_pos.col + state->combine_width) { -#ifdef DEBUG_GLYPH_COMBINE - int printpos; - printf("DEBUG: COMBINING SPLIT GLYPH of chars {"); - for(printpos = 0; state->combine_chars[printpos]; printpos++) - printf("U+%04x ", state->combine_chars[printpos]); - printf("} + {"); -#endif - - /* Find where we need to append these combining chars */ - int saved_i = 0; - while(state->combine_chars[saved_i]) - saved_i++; - - /* Add extra ones */ - while(i < npoints && vterm_unicode_is_combining(codepoints[i])) { - if(saved_i >= state->combine_chars_size) - grow_combine_buffer(state); - state->combine_chars[saved_i++] = codepoints[i++]; - } - if(saved_i >= state->combine_chars_size) - grow_combine_buffer(state); - state->combine_chars[saved_i] = 0; - -#ifdef DEBUG_GLYPH_COMBINE - for(; state->combine_chars[printpos]; printpos++) - printf("U+%04x ", state->combine_chars[printpos]); - printf("}\n"); -#endif - - /* Now render it */ - putglyph(state, state->combine_chars, state->combine_width, state->combine_pos); - } - else { - DEBUG_LOG("libvterm: TODO: Skip over split char+combining\n"); - } - } - - for(; i < npoints; i++) { - // Try to find combining characters following this - int glyph_starts = i; - int glyph_ends; - for(glyph_ends = i + 1; glyph_ends < npoints; glyph_ends++) - if(!vterm_unicode_is_combining(codepoints[glyph_ends])) - break; - - int width = 0; - - uint32_t chars[glyph_ends - glyph_starts + 1]; - - for( ; i < glyph_ends; i++) { - chars[i - glyph_starts] = codepoints[i]; - int this_width = vterm_unicode_width(codepoints[i]); -#ifdef DEBUG - if(this_width < 0) { - fprintf(stderr, "Text with negative-width codepoint U+%04x\n", codepoints[i]); - abort(); - } -#endif - width += this_width; - } - - chars[glyph_ends - glyph_starts] = 0; - i--; - -#ifdef DEBUG_GLYPH_COMBINE - int printpos; - printf("DEBUG: COMBINED GLYPH of %d chars {", glyph_ends - glyph_starts); - for(printpos = 0; printpos < glyph_ends - glyph_starts; printpos++) - printf("U+%04x ", chars[printpos]); - printf("}, onscreen width %d\n", width); -#endif - - if(state->at_phantom || state->pos.col + width > THISROWWIDTH(state)) { - linefeed(state); - state->pos.col = 0; - state->at_phantom = 0; - } - - if(state->mode.insert) { - /* TODO: This will be a little inefficient for large bodies of text, as - * it'll have to 'ICH' effectively before every glyph. We should scan - * ahead and ICH as many times as required - */ - VTermRect rect = { - .start_row = state->pos.row, - .end_row = state->pos.row + 1, - .start_col = state->pos.col, - .end_col = THISROWWIDTH(state), - }; - scroll(state, rect, 0, -1); - } - - putglyph(state, chars, width, state->pos); - - if(i == npoints - 1) { - /* End of the buffer. Save the chars in case we have to combine with - * more on the next call */ - int save_i; - for(save_i = 0; chars[save_i]; save_i++) { - if(save_i >= state->combine_chars_size) - grow_combine_buffer(state); - state->combine_chars[save_i] = chars[save_i]; - } - if(save_i >= state->combine_chars_size) - grow_combine_buffer(state); - state->combine_chars[save_i] = 0; - state->combine_width = width; - state->combine_pos = state->pos; - } - - if(state->pos.col + width >= THISROWWIDTH(state)) { - if(state->mode.autowrap) - state->at_phantom = 1; - } - else { - state->pos.col += width; - } - } - - updatecursor(state, &oldpos, 0); - -#ifdef DEBUG - if(state->pos.row < 0 || state->pos.row >= state->rows || - state->pos.col < 0 || state->pos.col >= state->cols) { - fprintf(stderr, "Position out of bounds after text: (%d,%d)\n", - state->pos.row, state->pos.col); - abort(); - } -#endif - - return eaten; -} - -static int on_control(unsigned char control, void *user) -{ - VTermState *state = user; - - VTermPos oldpos = state->pos; - - switch(control) { - case 0x07: // BEL - ECMA-48 8.3.3 - if(state->callbacks && state->callbacks->bell) - (*state->callbacks->bell)(state->cbdata); - break; - - case 0x08: // BS - ECMA-48 8.3.5 - if(state->pos.col > 0) - state->pos.col--; - break; - - case 0x09: // HT - ECMA-48 8.3.60 - tab(state, 1, +1); - break; - - case 0x0a: // LF - ECMA-48 8.3.74 - case 0x0b: // VT - case 0x0c: // FF - linefeed(state); - if(state->mode.newline) - state->pos.col = 0; - break; - - case 0x0d: // CR - ECMA-48 8.3.15 - state->pos.col = 0; - break; - - case 0x0e: // LS1 - ECMA-48 8.3.76 - state->gl_set = 1; - break; - - case 0x0f: // LS0 - ECMA-48 8.3.75 - state->gl_set = 0; - break; - - case 0x84: // IND - DEPRECATED but implemented for completeness - linefeed(state); - break; - - case 0x85: // NEL - ECMA-48 8.3.86 - linefeed(state); - state->pos.col = 0; - break; - - case 0x88: // HTS - ECMA-48 8.3.62 - set_col_tabstop(state, state->pos.col); - break; - - case 0x8d: // RI - ECMA-48 8.3.104 - if(state->pos.row == state->scrollregion_top) { - VTermRect rect = { - .start_row = state->scrollregion_top, - .end_row = SCROLLREGION_BOTTOM(state), - .start_col = SCROLLREGION_LEFT(state), - .end_col = SCROLLREGION_RIGHT(state), - }; - - scroll(state, rect, -1, 0); - } - else if(state->pos.row > 0) - state->pos.row--; - break; - - case 0x8e: // SS2 - ECMA-48 8.3.141 - state->gsingle_set = 2; - break; - - case 0x8f: // SS3 - ECMA-48 8.3.142 - state->gsingle_set = 3; - break; - - default: - if(state->fallbacks && state->fallbacks->control) - if((*state->fallbacks->control)(control, state->fbdata)) - return 1; - - return 0; - } - - updatecursor(state, &oldpos, 1); - -#ifdef DEBUG - if(state->pos.row < 0 || state->pos.row >= state->rows || - state->pos.col < 0 || state->pos.col >= state->cols) { - fprintf(stderr, "Position out of bounds after Ctrl %02x: (%d,%d)\n", - control, state->pos.row, state->pos.col); - abort(); - } -#endif - - return 1; -} - -static int settermprop_bool(VTermState *state, VTermProp prop, int v) -{ - VTermValue val = { .boolean = v }; - return vterm_state_set_termprop(state, prop, &val); -} - -static int settermprop_int(VTermState *state, VTermProp prop, int v) -{ - VTermValue val = { .number = v }; - return vterm_state_set_termprop(state, prop, &val); -} - -static int settermprop_string(VTermState *state, VTermProp prop, const char *str, size_t len) -{ - char strvalue[len+1]; - strncpy(strvalue, str, len); - strvalue[len] = 0; - - VTermValue val = { .string = strvalue }; - return vterm_state_set_termprop(state, prop, &val); -} - -static void savecursor(VTermState *state, int save) -{ - if(save) { - state->saved.pos = state->pos; - state->saved.mode.cursor_visible = state->mode.cursor_visible; - state->saved.mode.cursor_blink = state->mode.cursor_blink; - state->saved.mode.cursor_shape = state->mode.cursor_shape; - - vterm_state_savepen(state, 1); - } - else { - VTermPos oldpos = state->pos; - - state->pos = state->saved.pos; - - settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, state->saved.mode.cursor_visible); - settermprop_bool(state, VTERM_PROP_CURSORBLINK, state->saved.mode.cursor_blink); - settermprop_int (state, VTERM_PROP_CURSORSHAPE, state->saved.mode.cursor_shape); - - vterm_state_savepen(state, 0); - - updatecursor(state, &oldpos, 1); - } -} - -static int on_escape(const char *bytes, size_t len, void *user) -{ - VTermState *state = user; - - /* Easier to decode this from the first byte, even though the final - * byte terminates it - */ - switch(bytes[0]) { - case ' ': - if(len != 2) - return 0; - - switch(bytes[1]) { - case 'F': // S7C1T - state->vt->mode.ctrl8bit = 0; - break; - - case 'G': // S8C1T - state->vt->mode.ctrl8bit = 1; - break; - - default: - return 0; - } - return 2; - - case '#': - if(len != 2) - return 0; - - switch(bytes[1]) { - case '3': // DECDHL top - if(state->mode.leftrightmargin) - break; - set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_TOP); - break; - - case '4': // DECDHL bottom - if(state->mode.leftrightmargin) - break; - set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_BOTTOM); - break; - - case '5': // DECSWL - if(state->mode.leftrightmargin) - break; - set_lineinfo(state, state->pos.row, NO_FORCE, DWL_OFF, DHL_OFF); - break; - - case '6': // DECDWL - if(state->mode.leftrightmargin) - break; - set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_OFF); - break; - - case '8': // DECALN - { - VTermPos pos; - uint32_t E[] = { 'E', 0 }; - for(pos.row = 0; pos.row < state->rows; pos.row++) - for(pos.col = 0; pos.col < ROWWIDTH(state, pos.row); pos.col++) - putglyph(state, E, 1, pos); - break; - } - - default: - return 0; - } - return 2; - - case '(': case ')': case '*': case '+': // SCS - if(len != 2) - return 0; - - { - int setnum = bytes[0] - 0x28; - VTermEncoding *newenc = vterm_lookup_encoding(ENC_SINGLE_94, bytes[1]); - - if(newenc) { - state->encoding[setnum].enc = newenc; - - if(newenc->init) - (*newenc->init)(newenc, state->encoding[setnum].data); - } - } - - return 2; - - case '7': // DECSC - savecursor(state, 1); - return 1; - - case '8': // DECRC - savecursor(state, 0); - return 1; - - case '<': // Ignored by VT100. Used in VT52 mode to switch up to VT100 - return 1; - - case '=': // DECKPAM - state->mode.keypad = 1; - return 1; - - case '>': // DECKPNM - state->mode.keypad = 0; - return 1; - - case 'c': // RIS - ECMA-48 8.3.105 - { - VTermPos oldpos = state->pos; - vterm_state_reset(state, 1); - if(state->callbacks && state->callbacks->movecursor) - (*state->callbacks->movecursor)(state->pos, oldpos, state->mode.cursor_visible, state->cbdata); - return 1; - } - - case 'n': // LS2 - ECMA-48 8.3.78 - state->gl_set = 2; - return 1; - - case 'o': // LS3 - ECMA-48 8.3.80 - state->gl_set = 3; - return 1; - - case '~': // LS1R - ECMA-48 8.3.77 - state->gr_set = 1; - return 1; - - case '}': // LS2R - ECMA-48 8.3.79 - state->gr_set = 2; - return 1; - - case '|': // LS3R - ECMA-48 8.3.81 - state->gr_set = 3; - return 1; - - default: - return 0; - } -} - -static void set_mode(VTermState *state, int num, int val) -{ - switch(num) { - case 4: // IRM - ECMA-48 7.2.10 - state->mode.insert = val; - break; - - case 20: // LNM - ANSI X3.4-1977 - state->mode.newline = val; - break; - - default: - DEBUG_LOG("libvterm: Unknown mode %d\n", num); - return; - } -} - -static void set_dec_mode(VTermState *state, int num, int val) -{ - switch(num) { - case 1: - state->mode.cursor = val; - break; - - case 5: // DECSCNM - screen mode - settermprop_bool(state, VTERM_PROP_REVERSE, val); - break; - - case 6: // DECOM - origin mode - { - VTermPos oldpos = state->pos; - state->mode.origin = val; - state->pos.row = state->mode.origin ? state->scrollregion_top : 0; - state->pos.col = state->mode.origin ? SCROLLREGION_LEFT(state) : 0; - updatecursor(state, &oldpos, 1); - } - break; - - case 7: - state->mode.autowrap = val; - break; - - case 12: - settermprop_bool(state, VTERM_PROP_CURSORBLINK, val); - break; - - case 25: - settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, val); - break; - - case 69: // DECVSSM - vertical split screen mode - // DECLRMM - left/right margin mode - state->mode.leftrightmargin = val; - if(val) { - // Setting DECVSSM must clear doublewidth/doubleheight state of every line - for(int row = 0; row < state->rows; row++) - set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF); - } - - break; - - case 1000: - case 1002: - case 1003: - settermprop_int(state, VTERM_PROP_MOUSE, - !val ? VTERM_PROP_MOUSE_NONE : - (num == 1000) ? VTERM_PROP_MOUSE_CLICK : - (num == 1002) ? VTERM_PROP_MOUSE_DRAG : - VTERM_PROP_MOUSE_MOVE); - break; - - case 1004: - state->mode.report_focus = val; - break; - - case 1005: - state->mouse_protocol = val ? MOUSE_UTF8 : MOUSE_X10; - break; - - case 1006: - state->mouse_protocol = val ? MOUSE_SGR : MOUSE_X10; - break; - - case 1015: - state->mouse_protocol = val ? MOUSE_RXVT : MOUSE_X10; - break; - - case 1047: - settermprop_bool(state, VTERM_PROP_ALTSCREEN, val); - break; - - case 1048: - savecursor(state, val); - break; - - case 1049: - settermprop_bool(state, VTERM_PROP_ALTSCREEN, val); - savecursor(state, val); - break; - - case 2004: - state->mode.bracketpaste = val; - break; - - default: - DEBUG_LOG("libvterm: Unknown DEC mode %d\n", num); - return; - } -} - -static void request_dec_mode(VTermState *state, int num) -{ - int reply; - - switch(num) { - case 1: - reply = state->mode.cursor; - break; - - case 5: - reply = state->mode.screen; - break; - - case 6: - reply = state->mode.origin; - break; - - case 7: - reply = state->mode.autowrap; - break; - - case 12: - reply = state->mode.cursor_blink; - break; - - case 25: - reply = state->mode.cursor_visible; - break; - - case 69: - reply = state->mode.leftrightmargin; - break; - - case 1000: - reply = state->mouse_flags == MOUSE_WANT_CLICK; - break; - - case 1002: - reply = state->mouse_flags == (MOUSE_WANT_CLICK|MOUSE_WANT_DRAG); - break; - - case 1003: - reply = state->mouse_flags == (MOUSE_WANT_CLICK|MOUSE_WANT_MOVE); - break; - - case 1004: - reply = state->mode.report_focus; - break; - - case 1005: - reply = state->mouse_protocol == MOUSE_UTF8; - break; - - case 1006: - reply = state->mouse_protocol == MOUSE_SGR; - break; - - case 1015: - reply = state->mouse_protocol == MOUSE_RXVT; - break; - - case 1047: - reply = state->mode.alt_screen; - break; - - case 2004: - reply = state->mode.bracketpaste; - break; - - default: - vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?%d;%d$y", num, 0); - return; - } - - vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?%d;%d$y", num, reply ? 1 : 2); -} - -static int on_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user) -{ - VTermState *state = user; - int leader_byte = 0; - int intermed_byte = 0; - int cancel_phantom = 1; - - if(leader && leader[0]) { - if(leader[1]) // longer than 1 char - return 0; - - switch(leader[0]) { - case '?': - case '>': - leader_byte = leader[0]; - break; - default: - return 0; - } - } - - if(intermed && intermed[0]) { - if(intermed[1]) // longer than 1 char - return 0; - - switch(intermed[0]) { - case ' ': - case '"': - case '$': - case '\'': - intermed_byte = intermed[0]; - break; - default: - return 0; - } - } - - VTermPos oldpos = state->pos; - - // Some temporaries for later code - int count, val; - int row, col; - VTermRect rect; - int selective; - -#define LBOUND(v,min) if((v) < (min)) (v) = (min) -#define UBOUND(v,max) if((v) > (max)) (v) = (max) - -#define LEADER(l,b) ((l << 8) | b) -#define INTERMED(i,b) ((i << 16) | b) - - switch(intermed_byte << 16 | leader_byte << 8 | command) { - case 0x40: // ICH - ECMA-48 8.3.64 - count = CSI_ARG_COUNT(args[0]); - - if(!is_cursor_in_scrollregion(state)) - break; - - rect.start_row = state->pos.row; - rect.end_row = state->pos.row + 1; - rect.start_col = state->pos.col; - if(state->mode.leftrightmargin) - rect.end_col = SCROLLREGION_RIGHT(state); - else - rect.end_col = THISROWWIDTH(state); - - scroll(state, rect, 0, -count); - - break; - - case 0x41: // CUU - ECMA-48 8.3.22 - count = CSI_ARG_COUNT(args[0]); - state->pos.row -= count; - state->at_phantom = 0; - break; - - case 0x42: // CUD - ECMA-48 8.3.19 - count = CSI_ARG_COUNT(args[0]); - state->pos.row += count; - state->at_phantom = 0; - break; - - case 0x43: // CUF - ECMA-48 8.3.20 - count = CSI_ARG_COUNT(args[0]); - state->pos.col += count; - state->at_phantom = 0; - break; - - case 0x44: // CUB - ECMA-48 8.3.18 - count = CSI_ARG_COUNT(args[0]); - state->pos.col -= count; - state->at_phantom = 0; - break; - - case 0x45: // CNL - ECMA-48 8.3.12 - count = CSI_ARG_COUNT(args[0]); - state->pos.col = 0; - state->pos.row += count; - state->at_phantom = 0; - break; - - case 0x46: // CPL - ECMA-48 8.3.13 - count = CSI_ARG_COUNT(args[0]); - state->pos.col = 0; - state->pos.row -= count; - state->at_phantom = 0; - break; - - case 0x47: // CHA - ECMA-48 8.3.9 - val = CSI_ARG_OR(args[0], 1); - state->pos.col = val-1; - state->at_phantom = 0; - break; - - case 0x48: // CUP - ECMA-48 8.3.21 - row = CSI_ARG_OR(args[0], 1); - col = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? 1 : CSI_ARG(args[1]); - // zero-based - state->pos.row = row-1; - state->pos.col = col-1; - if(state->mode.origin) { - state->pos.row += state->scrollregion_top; - state->pos.col += SCROLLREGION_LEFT(state); - } - state->at_phantom = 0; - break; - - case 0x49: // CHT - ECMA-48 8.3.10 - count = CSI_ARG_COUNT(args[0]); - tab(state, count, +1); - break; - - case 0x4a: // ED - ECMA-48 8.3.39 - case LEADER('?', 0x4a): // DECSED - Selective Erase in Display - selective = (leader_byte == '?'); - switch(CSI_ARG(args[0])) { - case CSI_ARG_MISSING: - case 0: - rect.start_row = state->pos.row; rect.end_row = state->pos.row + 1; - rect.start_col = state->pos.col; rect.end_col = state->cols; - if(rect.end_col > rect.start_col) - erase(state, rect, selective); - - rect.start_row = state->pos.row + 1; rect.end_row = state->rows; - rect.start_col = 0; - for(int row = rect.start_row; row < rect.end_row; row++) - set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF); - if(rect.end_row > rect.start_row) - erase(state, rect, selective); - break; - - case 1: - rect.start_row = 0; rect.end_row = state->pos.row; - rect.start_col = 0; rect.end_col = state->cols; - for(int row = rect.start_row; row < rect.end_row; row++) - set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF); - if(rect.end_col > rect.start_col) - erase(state, rect, selective); - - rect.start_row = state->pos.row; rect.end_row = state->pos.row + 1; - rect.end_col = state->pos.col + 1; - if(rect.end_row > rect.start_row) - erase(state, rect, selective); - break; - - case 2: - rect.start_row = 0; rect.end_row = state->rows; - rect.start_col = 0; rect.end_col = state->cols; - for(int row = rect.start_row; row < rect.end_row; row++) - set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF); - erase(state, rect, selective); - break; - } - break; - - case 0x4b: // EL - ECMA-48 8.3.41 - case LEADER('?', 0x4b): // DECSEL - Selective Erase in Line - selective = (leader_byte == '?'); - rect.start_row = state->pos.row; - rect.end_row = state->pos.row + 1; - - switch(CSI_ARG(args[0])) { - case CSI_ARG_MISSING: - case 0: - rect.start_col = state->pos.col; rect.end_col = THISROWWIDTH(state); break; - case 1: - rect.start_col = 0; rect.end_col = state->pos.col + 1; break; - case 2: - rect.start_col = 0; rect.end_col = THISROWWIDTH(state); break; - default: - return 0; - } - - if(rect.end_col > rect.start_col) - erase(state, rect, selective); - - break; - - case 0x4c: // IL - ECMA-48 8.3.67 - count = CSI_ARG_COUNT(args[0]); - - if(!is_cursor_in_scrollregion(state)) - break; - - rect.start_row = state->pos.row; - rect.end_row = SCROLLREGION_BOTTOM(state); - rect.start_col = SCROLLREGION_LEFT(state); - rect.end_col = SCROLLREGION_RIGHT(state); - - scroll(state, rect, -count, 0); - - break; - - case 0x4d: // DL - ECMA-48 8.3.32 - count = CSI_ARG_COUNT(args[0]); - - if(!is_cursor_in_scrollregion(state)) - break; - - rect.start_row = state->pos.row; - rect.end_row = SCROLLREGION_BOTTOM(state); - rect.start_col = SCROLLREGION_LEFT(state); - rect.end_col = SCROLLREGION_RIGHT(state); - - scroll(state, rect, count, 0); - - break; - - case 0x50: // DCH - ECMA-48 8.3.26 - count = CSI_ARG_COUNT(args[0]); - - if(!is_cursor_in_scrollregion(state)) - break; - - rect.start_row = state->pos.row; - rect.end_row = state->pos.row + 1; - rect.start_col = state->pos.col; - if(state->mode.leftrightmargin) - rect.end_col = SCROLLREGION_RIGHT(state); - else - rect.end_col = THISROWWIDTH(state); - - scroll(state, rect, 0, count); - - break; - - case 0x53: // SU - ECMA-48 8.3.147 - count = CSI_ARG_COUNT(args[0]); - - rect.start_row = state->scrollregion_top; - rect.end_row = SCROLLREGION_BOTTOM(state); - rect.start_col = SCROLLREGION_LEFT(state); - rect.end_col = SCROLLREGION_RIGHT(state); - - scroll(state, rect, count, 0); - - break; - - case 0x54: // SD - ECMA-48 8.3.113 - count = CSI_ARG_COUNT(args[0]); - - rect.start_row = state->scrollregion_top; - rect.end_row = SCROLLREGION_BOTTOM(state); - rect.start_col = SCROLLREGION_LEFT(state); - rect.end_col = SCROLLREGION_RIGHT(state); - - scroll(state, rect, -count, 0); - - break; - - case 0x58: // ECH - ECMA-48 8.3.38 - count = CSI_ARG_COUNT(args[0]); - - rect.start_row = state->pos.row; - rect.end_row = state->pos.row + 1; - rect.start_col = state->pos.col; - rect.end_col = state->pos.col + count; - UBOUND(rect.end_col, THISROWWIDTH(state)); - - erase(state, rect, 0); - break; - - case 0x5a: // CBT - ECMA-48 8.3.7 - count = CSI_ARG_COUNT(args[0]); - tab(state, count, -1); - break; - - case 0x60: // HPA - ECMA-48 8.3.57 - col = CSI_ARG_OR(args[0], 1); - state->pos.col = col-1; - state->at_phantom = 0; - break; - - case 0x61: // HPR - ECMA-48 8.3.59 - count = CSI_ARG_COUNT(args[0]); - state->pos.col += count; - state->at_phantom = 0; - break; - - case 0x62: { // REP - ECMA-48 8.3.103 - const int row_width = THISROWWIDTH(state); - count = CSI_ARG_COUNT(args[0]); - col = state->pos.col + count; - UBOUND(col, row_width); - while (state->pos.col < col) { - putglyph(state, state->combine_chars, state->combine_width, state->pos); - state->pos.col += state->combine_width; - } - if (state->pos.col + state->combine_width >= row_width) { - if (state->mode.autowrap) { - state->at_phantom = 1; - cancel_phantom = 0; - } - } - break; - } - - case 0x63: // DA - ECMA-48 8.3.24 - val = CSI_ARG_OR(args[0], 0); - if(val == 0) - // DEC VT100 response - vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?1;2c"); - break; - - case LEADER('>', 0x63): // DEC secondary Device Attributes - vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, ">%d;%d;%dc", 0, 100, 0); - break; - - case 0x64: // VPA - ECMA-48 8.3.158 - row = CSI_ARG_OR(args[0], 1); - state->pos.row = row-1; - if(state->mode.origin) - state->pos.row += state->scrollregion_top; - state->at_phantom = 0; - break; - - case 0x65: // VPR - ECMA-48 8.3.160 - count = CSI_ARG_COUNT(args[0]); - state->pos.row += count; - state->at_phantom = 0; - break; - - case 0x66: // HVP - ECMA-48 8.3.63 - row = CSI_ARG_OR(args[0], 1); - col = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? 1 : CSI_ARG(args[1]); - // zero-based - state->pos.row = row-1; - state->pos.col = col-1; - if(state->mode.origin) { - state->pos.row += state->scrollregion_top; - state->pos.col += SCROLLREGION_LEFT(state); - } - state->at_phantom = 0; - break; - - case 0x67: // TBC - ECMA-48 8.3.154 - val = CSI_ARG_OR(args[0], 0); - - switch(val) { - case 0: - clear_col_tabstop(state, state->pos.col); - break; - case 3: - case 5: - for(col = 0; col < state->cols; col++) - clear_col_tabstop(state, col); - break; - case 1: - case 2: - case 4: - break; - /* TODO: 1, 2 and 4 aren't meaningful yet without line tab stops */ - default: - return 0; - } - break; - - case 0x68: // SM - ECMA-48 8.3.125 - if(!CSI_ARG_IS_MISSING(args[0])) - set_mode(state, CSI_ARG(args[0]), 1); - break; - - case LEADER('?', 0x68): // DEC private mode set - if(!CSI_ARG_IS_MISSING(args[0])) - set_dec_mode(state, CSI_ARG(args[0]), 1); - break; - - case 0x6a: // HPB - ECMA-48 8.3.58 - count = CSI_ARG_COUNT(args[0]); - state->pos.col -= count; - state->at_phantom = 0; - break; - - case 0x6b: // VPB - ECMA-48 8.3.159 - count = CSI_ARG_COUNT(args[0]); - state->pos.row -= count; - state->at_phantom = 0; - break; - - case 0x6c: // RM - ECMA-48 8.3.106 - if(!CSI_ARG_IS_MISSING(args[0])) - set_mode(state, CSI_ARG(args[0]), 0); - break; - - case LEADER('?', 0x6c): // DEC private mode reset - if(!CSI_ARG_IS_MISSING(args[0])) - set_dec_mode(state, CSI_ARG(args[0]), 0); - break; - - case 0x6d: // SGR - ECMA-48 8.3.117 - vterm_state_setpen(state, args, argcount); - break; - - case 0x6e: // DSR - ECMA-48 8.3.35 - case LEADER('?', 0x6e): // DECDSR - val = CSI_ARG_OR(args[0], 0); - - { - char *qmark = (leader_byte == '?') ? "?" : ""; - - switch(val) { - case 0: case 1: case 2: case 3: case 4: - // ignore - these are replies - break; - case 5: - vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%s0n", qmark); - break; - case 6: // CPR - cursor position report - vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%s%d;%dR", qmark, state->pos.row + 1, state->pos.col + 1); - break; - } - } - break; - - - case LEADER('!', 0x70): // DECSTR - DEC soft terminal reset - vterm_state_reset(state, 0); - break; - - case LEADER('?', INTERMED('$', 0x70)): - request_dec_mode(state, CSI_ARG(args[0])); - break; - - case INTERMED(' ', 0x71): // DECSCUSR - DEC set cursor shape - val = CSI_ARG_OR(args[0], 1); - - switch(val) { - case 0: case 1: - settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1); - settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK); - break; - case 2: - settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0); - settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK); - break; - case 3: - settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1); - settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_UNDERLINE); - break; - case 4: - settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0); - settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_UNDERLINE); - break; - case 5: - settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1); - settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BAR_LEFT); - break; - case 6: - settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0); - settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BAR_LEFT); - break; - } - - break; - - case INTERMED('"', 0x71): // DECSCA - DEC select character protection attribute - val = CSI_ARG_OR(args[0], 0); - - switch(val) { - case 0: case 2: - state->protected_cell = 0; - break; - case 1: - state->protected_cell = 1; - break; - } - - break; - - case 0x72: // DECSTBM - DEC custom - state->scrollregion_top = CSI_ARG_OR(args[0], 1) - 1; - state->scrollregion_bottom = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? -1 : CSI_ARG(args[1]); - LBOUND(state->scrollregion_top, 0); - UBOUND(state->scrollregion_top, state->rows); - LBOUND(state->scrollregion_bottom, -1); - if(state->scrollregion_top == 0 && state->scrollregion_bottom == state->rows) - state->scrollregion_bottom = -1; - else - UBOUND(state->scrollregion_bottom, state->rows); - - if(SCROLLREGION_BOTTOM(state) <= state->scrollregion_top) { - // Invalid - state->scrollregion_top = 0; - state->scrollregion_bottom = -1; - } - - break; - - case 0x73: // DECSLRM - DEC custom - // Always allow setting these margins, just they won't take effect without DECVSSM - state->scrollregion_left = CSI_ARG_OR(args[0], 1) - 1; - state->scrollregion_right = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? -1 : CSI_ARG(args[1]); - LBOUND(state->scrollregion_left, 0); - UBOUND(state->scrollregion_left, state->cols); - LBOUND(state->scrollregion_right, -1); - if(state->scrollregion_left == 0 && state->scrollregion_right == state->cols) - state->scrollregion_right = -1; - else - UBOUND(state->scrollregion_right, state->cols); - - if(state->scrollregion_right > -1 && - state->scrollregion_right <= state->scrollregion_left) { - // Invalid - state->scrollregion_left = 0; - state->scrollregion_right = -1; - } - - break; - - case INTERMED('\'', 0x7D): // DECIC - count = CSI_ARG_COUNT(args[0]); - - if(!is_cursor_in_scrollregion(state)) - break; - - rect.start_row = state->scrollregion_top; - rect.end_row = SCROLLREGION_BOTTOM(state); - rect.start_col = state->pos.col; - rect.end_col = SCROLLREGION_RIGHT(state); - - scroll(state, rect, 0, -count); - - break; - - case INTERMED('\'', 0x7E): // DECDC - count = CSI_ARG_COUNT(args[0]); - - if(!is_cursor_in_scrollregion(state)) - break; - - rect.start_row = state->scrollregion_top; - rect.end_row = SCROLLREGION_BOTTOM(state); - rect.start_col = state->pos.col; - rect.end_col = SCROLLREGION_RIGHT(state); - - scroll(state, rect, 0, count); - - break; - - default: - if(state->fallbacks && state->fallbacks->csi) - if((*state->fallbacks->csi)(leader, args, argcount, intermed, command, state->fbdata)) - return 1; - - return 0; - } - - if(state->mode.origin) { - LBOUND(state->pos.row, state->scrollregion_top); - UBOUND(state->pos.row, SCROLLREGION_BOTTOM(state)-1); - LBOUND(state->pos.col, SCROLLREGION_LEFT(state)); - UBOUND(state->pos.col, SCROLLREGION_RIGHT(state)-1); - } - else { - LBOUND(state->pos.row, 0); - UBOUND(state->pos.row, state->rows-1); - LBOUND(state->pos.col, 0); - UBOUND(state->pos.col, THISROWWIDTH(state)-1); - } - - updatecursor(state, &oldpos, cancel_phantom); - -#ifdef DEBUG - if(state->pos.row < 0 || state->pos.row >= state->rows || - state->pos.col < 0 || state->pos.col >= state->cols) { - fprintf(stderr, "Position out of bounds after CSI %c: (%d,%d)\n", - command, state->pos.row, state->pos.col); - abort(); - } - - if(SCROLLREGION_BOTTOM(state) <= state->scrollregion_top) { - fprintf(stderr, "Scroll region height out of bounds after CSI %c: %d <= %d\n", - command, SCROLLREGION_BOTTOM(state), state->scrollregion_top); - abort(); - } - - if(SCROLLREGION_RIGHT(state) <= SCROLLREGION_LEFT(state)) { - fprintf(stderr, "Scroll region width out of bounds after CSI %c: %d <= %d\n", - command, SCROLLREGION_RIGHT(state), SCROLLREGION_LEFT(state)); - abort(); - } -#endif - - return 1; -} - -static int on_osc(const char *command, size_t cmdlen, void *user) -{ - VTermState *state = user; - - if(cmdlen < 2) - return 0; - - if(strneq(command, "0;", 2)) { - settermprop_string(state, VTERM_PROP_ICONNAME, command + 2, cmdlen - 2); - settermprop_string(state, VTERM_PROP_TITLE, command + 2, cmdlen - 2); - return 1; - } - else if(strneq(command, "1;", 2)) { - settermprop_string(state, VTERM_PROP_ICONNAME, command + 2, cmdlen - 2); - return 1; - } - else if(strneq(command, "2;", 2)) { - settermprop_string(state, VTERM_PROP_TITLE, command + 2, cmdlen - 2); - return 1; - } - else if(state->fallbacks && state->fallbacks->osc) - if((*state->fallbacks->osc)(command, cmdlen, state->fbdata)) - return 1; - - return 0; -} - -static void request_status_string(VTermState *state, const char *command, size_t cmdlen) -{ - if(cmdlen == 1) - switch(command[0]) { - case 'm': // Query SGR - { - long args[20]; - int argc = vterm_state_getpen(state, args, sizeof(args)/sizeof(args[0])); - vterm_push_output_sprintf_ctrl(state->vt, C1_DCS, "1$r"); - for(int argi = 0; argi < argc; argi++) - vterm_push_output_sprintf(state->vt, - argi == argc - 1 ? "%d" : - CSI_ARG_HAS_MORE(args[argi]) ? "%d:" : - "%d;", - CSI_ARG(args[argi])); - vterm_push_output_sprintf(state->vt, "m"); - vterm_push_output_sprintf_ctrl(state->vt, C1_ST, ""); - } - return; - case 'r': // Query DECSTBM - vterm_push_output_sprintf_dcs(state->vt, "1$r%d;%dr", state->scrollregion_top+1, SCROLLREGION_BOTTOM(state)); - return; - case 's': // Query DECSLRM - vterm_push_output_sprintf_dcs(state->vt, "1$r%d;%ds", SCROLLREGION_LEFT(state)+1, SCROLLREGION_RIGHT(state)); - return; - } - - if(cmdlen == 2) { - if(strneq(command, " q", 2)) { - int reply; - switch(state->mode.cursor_shape) { - case VTERM_PROP_CURSORSHAPE_BLOCK: reply = 2; break; - case VTERM_PROP_CURSORSHAPE_UNDERLINE: reply = 4; break; - case VTERM_PROP_CURSORSHAPE_BAR_LEFT: reply = 6; break; - } - if(state->mode.cursor_blink) - reply--; - vterm_push_output_sprintf_dcs(state->vt, "1$r%d q", reply); - return; - } - else if(strneq(command, "\"q", 2)) { - vterm_push_output_sprintf_dcs(state->vt, "1$r%d\"q", state->protected_cell ? 1 : 2); - return; - } - } - - vterm_push_output_sprintf_dcs(state->vt, "0$r%.s", (int)cmdlen, command); -} - -static int on_dcs(const char *command, size_t cmdlen, void *user) -{ - VTermState *state = user; - - if(cmdlen >= 2 && strneq(command, "$q", 2)) { - request_status_string(state, command+2, cmdlen-2); - return 1; - } - else if(state->fallbacks && state->fallbacks->dcs) - if((*state->fallbacks->dcs)(command, cmdlen, state->fbdata)) - return 1; - - return 0; -} - -static int on_resize(int rows, int cols, void *user) -{ - VTermState *state = user; - VTermPos oldpos = state->pos; - - if(cols != state->cols) { - unsigned char *newtabstops = vterm_allocator_malloc(state->vt, (cols + 7) / 8); - - /* TODO: This can all be done much more efficiently bytewise */ - int col; - for(col = 0; col < state->cols && col < cols; col++) { - unsigned char mask = 1 << (col & 7); - if(state->tabstops[col >> 3] & mask) - newtabstops[col >> 3] |= mask; - else - newtabstops[col >> 3] &= ~mask; - } - - for( ; col < cols; col++) { - unsigned char mask = 1 << (col & 7); - if(col % 8 == 0) - newtabstops[col >> 3] |= mask; - else - newtabstops[col >> 3] &= ~mask; - } - - vterm_allocator_free(state->vt, state->tabstops); - state->tabstops = newtabstops; - } - - if(rows != state->rows) { - VTermLineInfo *newlineinfo = vterm_allocator_malloc(state->vt, rows * sizeof(VTermLineInfo)); - - int row; - for(row = 0; row < state->rows && row < rows; row++) { - newlineinfo[row] = state->lineinfo[row]; - } - - for( ; row < rows; row++) { - newlineinfo[row] = (VTermLineInfo){ - .doublewidth = 0, - }; - } - - vterm_allocator_free(state->vt, state->lineinfo); - state->lineinfo = newlineinfo; - } - - state->rows = rows; - state->cols = cols; - - if(state->scrollregion_bottom > -1) - UBOUND(state->scrollregion_bottom, state->rows); - if(state->scrollregion_right > -1) - UBOUND(state->scrollregion_right, state->cols); - - VTermPos delta = { 0, 0 }; - - if(state->callbacks && state->callbacks->resize) - (*state->callbacks->resize)(rows, cols, &delta, state->cbdata); - - if(state->at_phantom && state->pos.col < cols-1) { - state->at_phantom = 0; - state->pos.col++; - } - - state->pos.row += delta.row; - state->pos.col += delta.col; - - if(state->pos.row >= rows) - state->pos.row = rows - 1; - if(state->pos.col >= cols) - state->pos.col = cols - 1; - - updatecursor(state, &oldpos, 1); - - return 1; -} - -static const VTermParserCallbacks parser_callbacks = { - .text = on_text, - .control = on_control, - .escape = on_escape, - .csi = on_csi, - .osc = on_osc, - .dcs = on_dcs, - .resize = on_resize, -}; - -VTermState *vterm_obtain_state(VTerm *vt) -{ - if(vt->state) - return vt->state; - - VTermState *state = vterm_state_new(vt); - vt->state = state; - - state->combine_chars_size = 16; - state->combine_chars = vterm_allocator_malloc(state->vt, state->combine_chars_size * sizeof(state->combine_chars[0])); - - state->tabstops = vterm_allocator_malloc(state->vt, (state->cols + 7) / 8); - - state->lineinfo = vterm_allocator_malloc(state->vt, state->rows * sizeof(VTermLineInfo)); - - state->encoding_utf8.enc = vterm_lookup_encoding(ENC_UTF8, 'u'); - if(*state->encoding_utf8.enc->init) - (*state->encoding_utf8.enc->init)(state->encoding_utf8.enc, state->encoding_utf8.data); - - vterm_parser_set_callbacks(vt, &parser_callbacks, state); - - return state; -} - -void vterm_state_reset(VTermState *state, int hard) -{ - state->scrollregion_top = 0; - state->scrollregion_bottom = -1; - state->scrollregion_left = 0; - state->scrollregion_right = -1; - - state->mode.keypad = 0; - state->mode.cursor = 0; - state->mode.autowrap = 1; - state->mode.insert = 0; - state->mode.newline = 0; - state->mode.alt_screen = 0; - state->mode.origin = 0; - state->mode.leftrightmargin = 0; - state->mode.bracketpaste = 0; - state->mode.report_focus = 0; - - state->vt->mode.ctrl8bit = 0; - - for(int col = 0; col < state->cols; col++) - if(col % 8 == 0) - set_col_tabstop(state, col); - else - clear_col_tabstop(state, col); - - for(int row = 0; row < state->rows; row++) - set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF); - - if(state->callbacks && state->callbacks->initpen) - (*state->callbacks->initpen)(state->cbdata); - - vterm_state_resetpen(state); - - VTermEncoding *default_enc = state->vt->mode.utf8 ? - vterm_lookup_encoding(ENC_UTF8, 'u') : - vterm_lookup_encoding(ENC_SINGLE_94, 'B'); - - for(int i = 0; i < 4; i++) { - state->encoding[i].enc = default_enc; - if(default_enc->init) - (*default_enc->init)(default_enc, state->encoding[i].data); - } - - state->gl_set = 0; - state->gr_set = 1; - state->gsingle_set = 0; - - state->protected_cell = 0; - - // Initialise the props - settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, 1); - settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1); - settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK); - - if(hard) { - state->pos.row = 0; - state->pos.col = 0; - state->at_phantom = 0; - - VTermRect rect = { 0, state->rows, 0, state->cols }; - erase(state, rect, 0); - } -} - -void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos) -{ - *cursorpos = state->pos; -} - -void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user) -{ - if(callbacks) { - state->callbacks = callbacks; - state->cbdata = user; - - if(state->callbacks && state->callbacks->initpen) - (*state->callbacks->initpen)(state->cbdata); - } - else { - state->callbacks = NULL; - state->cbdata = NULL; - } -} - -void *vterm_state_get_cbdata(VTermState *state) -{ - return state->cbdata; -} - -void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermParserCallbacks *fallbacks, void *user) -{ - if(fallbacks) { - state->fallbacks = fallbacks; - state->fbdata = user; - } - else { - state->fallbacks = NULL; - state->fbdata = NULL; - } -} - -void *vterm_state_get_unrecognised_fbdata(VTermState *state) -{ - return state->fbdata; -} - -int vterm_state_set_termprop(VTermState *state, VTermProp prop, VTermValue *val) -{ - /* Only store the new value of the property if usercode said it was happy. - * This is especially important for altscreen switching */ - if(state->callbacks && state->callbacks->settermprop) - if(!(*state->callbacks->settermprop)(prop, val, state->cbdata)) - return 0; - - switch(prop) { - case VTERM_PROP_TITLE: - case VTERM_PROP_ICONNAME: - // we don't store these, just transparently pass through - return 1; - case VTERM_PROP_CURSORVISIBLE: - state->mode.cursor_visible = val->boolean; - return 1; - case VTERM_PROP_CURSORBLINK: - state->mode.cursor_blink = val->boolean; - return 1; - case VTERM_PROP_CURSORSHAPE: - state->mode.cursor_shape = val->number; - return 1; - case VTERM_PROP_REVERSE: - state->mode.screen = val->boolean; - return 1; - case VTERM_PROP_ALTSCREEN: - state->mode.alt_screen = val->boolean; - if(state->mode.alt_screen) { - VTermRect rect = { - .start_row = 0, - .start_col = 0, - .end_row = state->rows, - .end_col = state->cols, - }; - erase(state, rect, 0); - } - return 1; - case VTERM_PROP_MOUSE: - state->mouse_flags = 0; - if(val->number) - state->mouse_flags |= MOUSE_WANT_CLICK; - if(val->number == VTERM_PROP_MOUSE_DRAG) - state->mouse_flags |= MOUSE_WANT_DRAG; - if(val->number == VTERM_PROP_MOUSE_MOVE) - state->mouse_flags |= MOUSE_WANT_MOVE; - return 1; - - case VTERM_N_PROPS: - return 0; - } - - return 0; -} - -void vterm_state_focus_in(VTermState *state) -{ - if(state->mode.report_focus) - vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "I"); -} - -void vterm_state_focus_out(VTermState *state) -{ - if(state->mode.report_focus) - vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "O"); -} - -const VTermLineInfo *vterm_state_get_lineinfo(const VTermState *state, int row) -{ - return state->lineinfo + row; -} diff --git a/src/unicode.c b/src/unicode.c deleted file mode 100644 index 0d1b5ff..0000000 --- a/src/unicode.c +++ /dev/null @@ -1,337 +0,0 @@ -#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))); -} - - -static int mk_wcswidth(const uint32_t *pwcs, size_t n) -{ - int w, width = 0; - - for (;*pwcs && n-- > 0; pwcs++) - if ((w = mk_wcwidth(*pwcs)) < 0) - return -1; - else - width += w; - - return width; -} - - -/* - * 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); -} - - -static int mk_wcswidth_cjk(const uint32_t *pwcs, size_t n) -{ - int w, width = 0; - - for (;*pwcs && n-- > 0; pwcs++) - if ((w = mk_wcwidth_cjk(*pwcs)) < 0) - return -1; - else - width += w; - - return width; -} - -// ################################ -// ### 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); -} diff --git a/src/utf8.h b/src/utf8.h deleted file mode 100644 index 9a336d3..0000000 --- a/src/utf8.h +++ /dev/null @@ -1,39 +0,0 @@ -/* 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 */ diff --git a/src/vterm.c b/src/vterm.c deleted file mode 100644 index 843bb47..0000000 --- a/src/vterm.c +++ /dev/null @@ -1,367 +0,0 @@ -#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_new_with_allocator(rows, cols, &default_allocator, NULL); -} - -VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *funcs, void *allocdata) -{ - /* Need to bootstrap using the allocator function directly */ - VTerm *vt = (*funcs->malloc)(sizeof(VTerm), allocdata); - - vt->allocator = funcs; - vt->allocdata = allocdata; - - vt->rows = rows; - vt->cols = cols; - - vt->parser.state = NORMAL; - - vt->parser.callbacks = NULL; - vt->parser.cbdata = NULL; - - vt->parser.strbuffer_len = 64; - vt->parser.strbuffer_cur = 0; - vt->parser.strbuffer = vterm_allocator_malloc(vt, vt->parser.strbuffer_len); - - vt->outbuffer_len = 64; - vt->outbuffer_cur = 0; - vt->outbuffer = vterm_allocator_malloc(vt, vt->outbuffer_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->parser.strbuffer); - vterm_allocator_free(vt, vt->outbuffer); - - 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) -{ - 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; -} - -INTERNAL void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len) -{ - if(len > vt->outbuffer_len - vt->outbuffer_cur) { - DEBUG_LOG("vterm_push_output(): buffer overflow; truncating output\n"); - len = vt->outbuffer_len - vt->outbuffer_cur; - } - - memcpy(vt->outbuffer + vt->outbuffer_cur, bytes, len); - vt->outbuffer_cur += len; -} - -static int outbuffer_is_full(VTerm *vt) -{ - return vt->outbuffer_cur >= vt->outbuffer_len - 1; -} - -INTERNAL void vterm_push_output_vsprintf(VTerm *vt, const char *format, va_list args) -{ - if(outbuffer_is_full(vt)) { - DEBUG_LOG("vterm_push_output(): buffer overflow; truncating output\n"); - return; - } - - int written = vsnprintf(vt->outbuffer + vt->outbuffer_cur, - vt->outbuffer_len - vt->outbuffer_cur, - format, args); - - if(written == vt->outbuffer_len - vt->outbuffer_cur) { - /* output was truncated */ - vt->outbuffer_cur = vt->outbuffer_len - 1; - } - else - vt->outbuffer_cur += written; -} - -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 orig_cur = vt->outbuffer_cur; - - if(ctrl >= 0x80 && !vt->mode.ctrl8bit) - vterm_push_output_sprintf(vt, ESC_S "%c", ctrl - 0x40); - else - vterm_push_output_sprintf(vt, "%c", ctrl); - - va_list args; - va_start(args, fmt); - vterm_push_output_vsprintf(vt, fmt, args); - va_end(args); - - if(outbuffer_is_full(vt)) - vt->outbuffer_cur = orig_cur; -} - -INTERNAL void vterm_push_output_sprintf_dcs(VTerm *vt, const char *fmt, ...) -{ - size_t orig_cur = vt->outbuffer_cur; - - if(!vt->mode.ctrl8bit) - vterm_push_output_sprintf(vt, ESC_S "%c", C1_DCS - 0x40); - else - vterm_push_output_sprintf(vt, "%c", C1_DCS); - - va_list args; - va_start(args, fmt); - vterm_push_output_vsprintf(vt, fmt, args); - va_end(args); - - vterm_push_output_sprintf_ctrl(vt, C1_ST, ""); - - if(outbuffer_is_full(vt)) - vt->outbuffer_cur = orig_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_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_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_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); - } -} diff --git a/src/vterm_internal.h b/src/vterm_internal.h deleted file mode 100644 index 363faee..0000000 --- a/src/vterm_internal.h +++ /dev/null @@ -1,246 +0,0 @@ -#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 - -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 strike:1; - unsigned int font:4; /* To store 0-9 */ -}; - -struct VTermState -{ - VTerm *vt; - - const VTermStateCallbacks *callbacks; - void *cbdata; - - const VTermParserCallbacks *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; - - 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; -}; - -typedef enum { - VTERM_PARSER_OSC, - VTERM_PARSER_DCS, - - VTERM_N_PARSER_TYPES -} VTermParserStringType; - -struct VTerm -{ - 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, - ESC, - /* below here are the "string states" */ - STRING, - ESC_IN_STRING, - } state; - - int intermedlen; - char intermed[INTERMED_MAX]; - - int csi_leaderlen; - char csi_leader[CSI_LEADER_MAX]; - - int csi_argi; - long csi_args[CSI_ARGS_MAX]; - - const VTermParserCallbacks *callbacks; - void *cbdata; - - VTermParserStringType stringtype; - char *strbuffer; - size_t strbuffer_len; - size_t strbuffer_cur; - } parser; - - /* len == malloc()ed size; cur == number of valid bytes */ - - char *outbuffer; - size_t outbuffer_len; - size_t outbuffer_cur; - - 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_dcs(VTerm *vt, 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, -}; - -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 |