mirror of
https://github.com/neovim/neovim
synced 2025-07-15 16:51:49 +00:00
refactor: adopt vterm
We have changed too much to consider it a mere bundled dependency (such
as unicode handling in e3bfcf2fd4
), and
can consider it our own at this point.
This commit is contained in:
@ -148,8 +148,6 @@ These dependencies are "vendored" (inlined), we must update the sources manually
|
||||
* `src/xdiff/`: [xdiff](https://github.com/git/git/tree/master/xdiff)
|
||||
* `src/cjson/`: [lua-cjson](https://github.com/openresty/lua-cjson)
|
||||
* `src/klib/`: [Klib](https://github.com/attractivechaos/klib)
|
||||
* `src/vterm/`: [libvterm](https://www.leonerd.org.uk/code/libvterm/),
|
||||
[mirror](https://github.com/neovim/libvterm)
|
||||
* `runtime/lua/vim/inspect.lua`: [inspect.lua](https://github.com/kikito/inspect.lua)
|
||||
* `src/nvim/tui/terminfo_defs.h`: terminfo definitions
|
||||
* Run `scripts/update_terminfo.sh` to update these definitions.
|
||||
|
@ -361,8 +361,8 @@ file(MAKE_DIRECTORY ${TOUCHES_DIR} ${GENERATED_DIR} ${GENERATED_INCLUDES_DIR})
|
||||
|
||||
file(GLOB NVIM_SOURCES CONFIGURE_DEPENDS *.c)
|
||||
file(GLOB NVIM_HEADERS CONFIGURE_DEPENDS *.h)
|
||||
file(GLOB EXTERNAL_SOURCES CONFIGURE_DEPENDS ../xdiff/*.c ../mpack/*.c ../cjson/*.c ../klib/*.c ../vterm/*.c)
|
||||
file(GLOB EXTERNAL_HEADERS CONFIGURE_DEPENDS ../xdiff/*.h ../mpack/*.h ../cjson/*.h ../klib/*.h ../vterm/*.h)
|
||||
file(GLOB EXTERNAL_SOURCES CONFIGURE_DEPENDS ../xdiff/*.c ../mpack/*.c ../cjson/*.c ../klib/*.c)
|
||||
file(GLOB EXTERNAL_HEADERS CONFIGURE_DEPENDS ../xdiff/*.h ../mpack/*.h ../cjson/*.h ../klib/*.h)
|
||||
|
||||
file(GLOB NLUA0_SOURCES CONFIGURE_DEPENDS ../mpack/*.c)
|
||||
|
||||
@ -391,6 +391,7 @@ foreach(subdir
|
||||
msgpack_rpc
|
||||
tui
|
||||
tui/termkey
|
||||
vterm
|
||||
event
|
||||
eval
|
||||
lua
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include "nvim/mbyte_defs.h" // IWYU pragma: keep
|
||||
#include "nvim/types_defs.h" // IWYU pragma: keep
|
||||
|
||||
typedef utf8proc_int32_t GraphemeState;
|
||||
#define GRAPHEME_STATE_INIT 0
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <utf8proc.h>
|
||||
|
||||
#include "nvim/iconv_defs.h"
|
||||
|
||||
@ -71,3 +72,5 @@ typedef struct {
|
||||
int8_t begin_off; ///< Offset to the first byte of the codepoint.
|
||||
int8_t end_off; ///< Offset to one past the end byte of the codepoint.
|
||||
} CharBoundsOff;
|
||||
|
||||
typedef utf8proc_int32_t GraphemeState;
|
||||
|
@ -93,9 +93,15 @@
|
||||
#include "nvim/types_defs.h"
|
||||
#include "nvim/ui.h"
|
||||
#include "nvim/vim_defs.h"
|
||||
#include "nvim/vterm/keyboard.h"
|
||||
#include "nvim/vterm/mouse.h"
|
||||
#include "nvim/vterm/parser.h"
|
||||
#include "nvim/vterm/pen.h"
|
||||
#include "nvim/vterm/screen.h"
|
||||
#include "nvim/vterm/state.h"
|
||||
#include "nvim/vterm/vterm.h"
|
||||
#include "nvim/vterm/vterm_keycodes_defs.h"
|
||||
#include "nvim/window.h"
|
||||
#include "vterm/vterm.h"
|
||||
#include "vterm/vterm_keycodes.h"
|
||||
|
||||
typedef struct {
|
||||
VimState state;
|
||||
|
1
src/nvim/vterm/README.md
Normal file
1
src/nvim/vterm/README.md
Normal file
@ -0,0 +1 @@
|
||||
Adopted from [libvterm](https://www.leonerd.org.uk/code/libvterm/)
|
278
src/nvim/vterm/encoding.c
Normal file
278
src/nvim/vterm/encoding.c
Normal file
@ -0,0 +1,278 @@
|
||||
#include "nvim/vterm/encoding.h"
|
||||
#include "nvim/vterm/vterm_internal_defs.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "vterm/encoding.c.generated.h"
|
||||
#endif
|
||||
|
||||
#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)++) {
|
||||
uint8_t c = (uint8_t)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)++] = (uint32_t)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)++) {
|
||||
uint8_t c = (uint8_t)(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)++) {
|
||||
uint8_t c = (uint8_t)(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const struct StaticTableEncoding encoding_DECdrawing = {
|
||||
{ .decode = &decode_table },
|
||||
{
|
||||
[0x60] = 0x25C6, // BLACK DIAMOND
|
||||
[0x61] = 0x2592, // MEDIUM SHADE (checkerboard)
|
||||
[0x62] = 0x2409, // SYMBOL FOR HORIZONTAL TAB
|
||||
[0x63] = 0x240C, // SYMBOL FOR FORM FEED
|
||||
[0x64] = 0x240D, // SYMBOL FOR CARRIAGE RETURN
|
||||
[0x65] = 0x240A, // SYMBOL FOR LINE FEED
|
||||
[0x66] = 0x00B0, // DEGREE SIGN
|
||||
[0x67] = 0x00B1, // PLUS-MINUS SIGN (plus or minus)
|
||||
[0x68] = 0x2424, // SYMBOL FOR NEW LINE
|
||||
[0x69] = 0x240B, // SYMBOL FOR VERTICAL TAB
|
||||
[0x6a] = 0x2518, // BOX DRAWINGS LIGHT UP AND LEFT (bottom-right corner)
|
||||
[0x6b] = 0x2510, // BOX DRAWINGS LIGHT DOWN AND LEFT (top-right corner)
|
||||
[0x6c] = 0x250C, // BOX DRAWINGS LIGHT DOWN AND RIGHT (top-left corner)
|
||||
[0x6d] = 0x2514, // BOX DRAWINGS LIGHT UP AND RIGHT (bottom-left corner)
|
||||
[0x6e] = 0x253C, // BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL (crossing lines)
|
||||
[0x6f] = 0x23BA, // HORIZONTAL SCAN LINE-1
|
||||
[0x70] = 0x23BB, // HORIZONTAL SCAN LINE-3
|
||||
[0x71] = 0x2500, // BOX DRAWINGS LIGHT HORIZONTAL
|
||||
[0x72] = 0x23BC, // HORIZONTAL SCAN LINE-7
|
||||
[0x73] = 0x23BD, // HORIZONTAL SCAN LINE-9
|
||||
[0x74] = 0x251C, // BOX DRAWINGS LIGHT VERTICAL AND RIGHT
|
||||
[0x75] = 0x2524, // BOX DRAWINGS LIGHT VERTICAL AND LEFT
|
||||
[0x76] = 0x2534, // BOX DRAWINGS LIGHT UP AND HORIZONTAL
|
||||
[0x77] = 0x252C, // BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
|
||||
[0x78] = 0x2502, // BOX DRAWINGS LIGHT VERTICAL
|
||||
[0x79] = 0x2A7D, // LESS-THAN OR SLANTED EQUAL-TO
|
||||
[0x7a] = 0x2A7E, // GREATER-THAN OR SLANTED EQUAL-TO
|
||||
[0x7b] = 0x03C0, // GREEK SMALL LETTER PI
|
||||
[0x7c] = 0x2260, // NOT EQUAL TO
|
||||
[0x7d] = 0x00A3, // POUND SIGN
|
||||
[0x7e] = 0x00B7, // MIDDLE DOT
|
||||
}
|
||||
};
|
||||
|
||||
static const struct StaticTableEncoding encoding_uk = {
|
||||
{ .decode = &decode_table },
|
||||
{
|
||||
[0x23] = 0x00a3, // £
|
||||
}
|
||||
};
|
||||
|
||||
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 },
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
10
src/nvim/vterm/encoding.h
Normal file
10
src/nvim/vterm/encoding.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "nvim/vterm/vterm_defs.h"
|
||||
#include "nvim/vterm/vterm_internal_defs.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "vterm/encoding.h.generated.h"
|
||||
#endif
|
252
src/nvim/vterm/keyboard.c
Normal file
252
src/nvim/vterm/keyboard.c
Normal file
@ -0,0 +1,252 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nvim/ascii_defs.h"
|
||||
#include "nvim/tui/termkey/termkey.h"
|
||||
#include "nvim/vterm/keyboard.h"
|
||||
#include "nvim/vterm/vterm.h"
|
||||
#include "nvim/vterm/vterm_internal_defs.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "vterm/keyboard.c.generated.h"
|
||||
#endif
|
||||
|
||||
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 &= (unsigned)~VTERM_MOD_SHIFT;
|
||||
}
|
||||
|
||||
if (mod == 0) {
|
||||
// Normal text - ignore just shift
|
||||
char str[6];
|
||||
int seqlen = fill_utf8((int)c, str);
|
||||
vterm_push_output_bytes(vt, str, (size_t)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 & (unsigned)~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, NUL, 0 }, // NONE
|
||||
|
||||
{ KEYCODE_ENTER, '\r', 0 }, // ENTER
|
||||
{ KEYCODE_TAB, '\t', 0 }, // TAB
|
||||
{ KEYCODE_LITERAL, '\x7f', 0 }, // BACKSPACE == ASCII DEL
|
||||
{ KEYCODE_LITERAL, '\x1b', 0 }, // ESCAPE
|
||||
|
||||
{ KEYCODE_CSI_CURSOR, 'A', 0 }, // UP
|
||||
{ KEYCODE_CSI_CURSOR, 'B', 0 }, // DOWN
|
||||
{ KEYCODE_CSI_CURSOR, 'D', 0 }, // LEFT
|
||||
{ KEYCODE_CSI_CURSOR, 'C', 0 }, // RIGHT
|
||||
|
||||
{ KEYCODE_CSINUM, '~', 2 }, // INS
|
||||
{ KEYCODE_CSINUM, '~', 3 }, // DEL
|
||||
{ KEYCODE_CSI_CURSOR, 'H', 0 }, // HOME
|
||||
{ KEYCODE_CSI_CURSOR, 'F', 0 }, // END
|
||||
{ KEYCODE_CSINUM, '~', 5 }, // PAGEUP
|
||||
{ KEYCODE_CSINUM, '~', 6 }, // PAGEDOWN
|
||||
};
|
||||
|
||||
static keycodes_s keycodes_fn[] = {
|
||||
{ KEYCODE_NONE, NUL, 0 }, // F0 - shouldn't happen
|
||||
{ KEYCODE_SS3, 'P', 0 }, // F1
|
||||
{ KEYCODE_SS3, 'Q', 0 }, // F2
|
||||
{ KEYCODE_SS3, 'R', 0 }, // F3
|
||||
{ KEYCODE_SS3, 'S', 0 }, // F4
|
||||
{ KEYCODE_CSINUM, '~', 15 }, // F5
|
||||
{ KEYCODE_CSINUM, '~', 17 }, // F6
|
||||
{ KEYCODE_CSINUM, '~', 18 }, // F7
|
||||
{ KEYCODE_CSINUM, '~', 19 }, // F8
|
||||
{ KEYCODE_CSINUM, '~', 20 }, // F9
|
||||
{ KEYCODE_CSINUM, '~', 21 }, // F10
|
||||
{ KEYCODE_CSINUM, '~', 23 }, // F11
|
||||
{ KEYCODE_CSINUM, '~', 24 }, // F12
|
||||
};
|
||||
|
||||
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 = (char)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~");
|
||||
}
|
||||
}
|
10
src/nvim/vterm/keyboard.h
Normal file
10
src/nvim/vterm/keyboard.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "nvim/vterm/vterm_defs.h"
|
||||
#include "nvim/vterm/vterm_keycodes_defs.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "vterm/keyboard.h.generated.h"
|
||||
#endif
|
113
src/nvim/vterm/mouse.c
Normal file
113
src/nvim/vterm/mouse.c
Normal file
@ -0,0 +1,113 @@
|
||||
#include "nvim/tui/termkey/termkey.h"
|
||||
#include "nvim/vterm/mouse.h"
|
||||
#include "nvim/vterm/vterm.h"
|
||||
#include "nvim/vterm/vterm_internal_defs.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "vterm/mouse.c.generated.h"
|
||||
#endif
|
||||
|
||||
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 += (size_t)fill_utf8((code | modifiers) + 0x20, utf8 + len);
|
||||
len += (size_t)fill_utf8(col + 0x21, utf8 + len);
|
||||
len += (size_t)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, (int)mod, col, row);
|
||||
}
|
||||
}
|
||||
|
||||
void vterm_mouse_button(VTerm *vt, int button, bool pressed, VTermModifier mod)
|
||||
{
|
||||
VTermState *state = vt->state;
|
||||
|
||||
int old_buttons = state->mouse_buttons;
|
||||
|
||||
if (button > 0 && button <= 3) {
|
||||
if (pressed) {
|
||||
state->mouse_buttons |= (1 << (button - 1));
|
||||
} else {
|
||||
state->mouse_buttons &= ~(1 << (button - 1));
|
||||
}
|
||||
}
|
||||
|
||||
// Most of the time we don't get button releases from 4/5
|
||||
if (state->mouse_buttons == old_buttons && button < 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!state->mouse_flags) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (button < 4) {
|
||||
output_mouse(state, button - 1, pressed, (int)mod, state->mouse_col, state->mouse_row);
|
||||
} else if (button < 8) {
|
||||
output_mouse(state, button - 4 + 0x40, pressed, (int)mod, state->mouse_col, state->mouse_row);
|
||||
}
|
||||
}
|
10
src/nvim/vterm/mouse.h
Normal file
10
src/nvim/vterm/mouse.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "nvim/vterm/vterm_defs.h"
|
||||
#include "nvim/vterm/vterm_keycodes_defs.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "vterm/mouse.h.generated.h"
|
||||
#endif
|
411
src/nvim/vterm/parser.c
Normal file
411
src/nvim/vterm/parser.c
Normal file
@ -0,0 +1,411 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "nvim/vterm/parser.h"
|
||||
#include "nvim/vterm/vterm.h"
|
||||
#include "nvim/vterm/vterm_internal_defs.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "vterm/parser.c.generated.h"
|
||||
#endif
|
||||
|
||||
#undef DEBUG_PARSER
|
||||
|
||||
static bool is_intermed(uint8_t c)
|
||||
{
|
||||
return c >= 0x20 && c <= 0x2f;
|
||||
}
|
||||
|
||||
static void do_control(VTerm *vt, uint8_t control)
|
||||
{
|
||||
if (vt->parser.callbacks && vt->parser.callbacks->control) {
|
||||
if ((*vt->parser.callbacks->control)(control, vt->parser.cbdata)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_LOG("libvterm: Unhandled control 0x%02x\n", control);
|
||||
}
|
||||
|
||||
static void do_csi(VTerm *vt, char command)
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
printf("Parsed CSI args as:\n", arglen, args);
|
||||
printf(" leader: %s\n", vt->parser.v.csi.leader);
|
||||
for (int argi = 0; argi < vt->parser.v.csi.argi; argi++) {
|
||||
printf(" %lu", CSI_ARG(vt->parser.v.csi.args[argi]));
|
||||
if (!CSI_ARG_HAS_MORE(vt->parser.v.csi.args[argi])) {
|
||||
printf("\n");
|
||||
}
|
||||
printf(" intermed: %s\n", vt->parser.intermed);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (vt->parser.callbacks && vt->parser.callbacks->csi) {
|
||||
if ((*vt->parser.callbacks->csi)(vt->parser.v.csi.leaderlen ? vt->parser.v.csi.leader : NULL,
|
||||
vt->parser.v.csi.args,
|
||||
vt->parser.v.csi.argi,
|
||||
vt->parser.intermedlen ? vt->parser.intermed : NULL,
|
||||
command,
|
||||
vt->parser.cbdata)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_LOG("libvterm: Unhandled CSI %c\n", command);
|
||||
}
|
||||
|
||||
static void do_escape(VTerm *vt, char command)
|
||||
{
|
||||
char seq[INTERMED_MAX + 1];
|
||||
|
||||
size_t len = (size_t)vt->parser.intermedlen;
|
||||
strncpy(seq, vt->parser.intermed, len); // NOLINT(runtime/printf)
|
||||
seq[len++] = command;
|
||||
seq[len] = 0;
|
||||
|
||||
if (vt->parser.callbacks && vt->parser.callbacks->escape) {
|
||||
if ((*vt->parser.callbacks->escape)(seq, len, vt->parser.cbdata)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_LOG("libvterm: Unhandled escape ESC 0x%02x\n", command);
|
||||
}
|
||||
|
||||
static void string_fragment(VTerm *vt, const char *str, size_t len, bool final)
|
||||
{
|
||||
VTermStringFragment frag = {
|
||||
.str = str,
|
||||
.len = len,
|
||||
.initial = vt->parser.string_initial,
|
||||
.final = final,
|
||||
};
|
||||
|
||||
switch (vt->parser.state) {
|
||||
case OSC:
|
||||
if (vt->parser.callbacks && vt->parser.callbacks->osc) {
|
||||
(*vt->parser.callbacks->osc)(vt->parser.v.osc.command, frag, vt->parser.cbdata);
|
||||
}
|
||||
break;
|
||||
|
||||
case DCS_VTERM:
|
||||
if (vt->parser.callbacks && vt->parser.callbacks->dcs) {
|
||||
(*vt->parser.callbacks->dcs)(vt->parser.v.dcs.command, (size_t)vt->parser.v.dcs.commandlen,
|
||||
frag,
|
||||
vt->parser.cbdata);
|
||||
}
|
||||
break;
|
||||
|
||||
case APC:
|
||||
if (vt->parser.callbacks && vt->parser.callbacks->apc) {
|
||||
(*vt->parser.callbacks->apc)(frag, vt->parser.cbdata);
|
||||
}
|
||||
break;
|
||||
|
||||
case PM:
|
||||
if (vt->parser.callbacks && vt->parser.callbacks->pm) {
|
||||
(*vt->parser.callbacks->pm)(frag, vt->parser.cbdata);
|
||||
}
|
||||
break;
|
||||
|
||||
case SOS:
|
||||
if (vt->parser.callbacks && vt->parser.callbacks->sos) {
|
||||
(*vt->parser.callbacks->sos)(frag, vt->parser.cbdata);
|
||||
}
|
||||
break;
|
||||
|
||||
case NORMAL:
|
||||
case CSI_LEADER:
|
||||
case CSI_ARGS:
|
||||
case CSI_INTERMED:
|
||||
case OSC_COMMAND:
|
||||
case DCS_COMMAND:
|
||||
break;
|
||||
}
|
||||
|
||||
vt->parser.string_initial = false;
|
||||
}
|
||||
|
||||
size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len)
|
||||
{
|
||||
size_t pos = 0;
|
||||
const char *string_start;
|
||||
|
||||
switch (vt->parser.state) {
|
||||
case NORMAL:
|
||||
case CSI_LEADER:
|
||||
case CSI_ARGS:
|
||||
case CSI_INTERMED:
|
||||
case OSC_COMMAND:
|
||||
case DCS_COMMAND:
|
||||
string_start = NULL;
|
||||
break;
|
||||
case OSC:
|
||||
case DCS_VTERM:
|
||||
case APC:
|
||||
case PM:
|
||||
case SOS:
|
||||
string_start = bytes;
|
||||
break;
|
||||
}
|
||||
|
||||
#define ENTER_STATE(st) do { vt->parser.state = st; string_start = NULL; } while (0)
|
||||
#define ENTER_NORMAL_STATE() ENTER_STATE(NORMAL)
|
||||
|
||||
#define IS_STRING_STATE() (vt->parser.state >= OSC_COMMAND)
|
||||
|
||||
for (; pos < len; pos++) {
|
||||
uint8_t c = (uint8_t)bytes[pos];
|
||||
bool c1_allowed = !vt->mode.utf8;
|
||||
|
||||
if (c == 0x00 || c == 0x7f) { // NUL, DEL
|
||||
if (IS_STRING_STATE()) {
|
||||
string_fragment(vt, string_start, (size_t)(bytes + pos - string_start), false);
|
||||
string_start = bytes + pos + 1;
|
||||
}
|
||||
if (vt->parser.emit_nul) {
|
||||
do_control(vt, c);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (c == 0x18 || c == 0x1a) { // CAN, SUB
|
||||
vt->parser.in_esc = false;
|
||||
ENTER_NORMAL_STATE();
|
||||
if (vt->parser.emit_nul) {
|
||||
do_control(vt, c);
|
||||
}
|
||||
continue;
|
||||
} else if (c == 0x1b) { // ESC
|
||||
vt->parser.intermedlen = 0;
|
||||
if (!IS_STRING_STATE()) {
|
||||
vt->parser.state = NORMAL;
|
||||
}
|
||||
vt->parser.in_esc = true;
|
||||
continue;
|
||||
} else if (c == 0x07 // BEL, can stand for ST in OSC or DCS state
|
||||
&& IS_STRING_STATE()) {} else if (c < 0x20) { // other C0
|
||||
if (vt->parser.state == SOS) {
|
||||
continue; // All other C0s permitted in SOS
|
||||
}
|
||||
if (IS_STRING_STATE()) {
|
||||
string_fragment(vt, string_start, (size_t)(bytes + pos - string_start), false);
|
||||
}
|
||||
do_control(vt, c);
|
||||
if (IS_STRING_STATE()) {
|
||||
string_start = bytes + pos + 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t string_len = (size_t)(bytes + pos - string_start);
|
||||
|
||||
if (vt->parser.in_esc) {
|
||||
// Hoist an ESC letter into a C1 if we're not in a string mode
|
||||
// Always accept ESC \ == ST even in string mode
|
||||
if (!vt->parser.intermedlen
|
||||
&& c >= 0x40 && c < 0x60
|
||||
&& ((!IS_STRING_STATE() || c == 0x5c))) {
|
||||
c += 0x40;
|
||||
c1_allowed = true;
|
||||
if (string_len) {
|
||||
assert(string_len > 0);
|
||||
string_len -= 1;
|
||||
}
|
||||
vt->parser.in_esc = false;
|
||||
} else {
|
||||
string_start = NULL;
|
||||
vt->parser.state = NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
switch (vt->parser.state) {
|
||||
case CSI_LEADER:
|
||||
// Extract leader bytes 0x3c to 0x3f
|
||||
if (c >= 0x3c && c <= 0x3f) {
|
||||
if (vt->parser.v.csi.leaderlen < CSI_LEADER_MAX - 1) {
|
||||
vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen++] = (char)c;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen] = 0;
|
||||
|
||||
vt->parser.v.csi.argi = 0;
|
||||
vt->parser.v.csi.args[0] = CSI_ARG_MISSING;
|
||||
vt->parser.state = CSI_ARGS;
|
||||
|
||||
FALLTHROUGH;
|
||||
case CSI_ARGS:
|
||||
// Numerical value of argument
|
||||
if (c >= '0' && c <= '9') {
|
||||
if (vt->parser.v.csi.args[vt->parser.v.csi.argi] == CSI_ARG_MISSING) {
|
||||
vt->parser.v.csi.args[vt->parser.v.csi.argi] = 0;
|
||||
}
|
||||
vt->parser.v.csi.args[vt->parser.v.csi.argi] *= 10;
|
||||
vt->parser.v.csi.args[vt->parser.v.csi.argi] += c - '0';
|
||||
break;
|
||||
}
|
||||
if (c == ':') {
|
||||
vt->parser.v.csi.args[vt->parser.v.csi.argi] |= CSI_ARG_FLAG_MORE;
|
||||
c = ';';
|
||||
}
|
||||
if (c == ';') {
|
||||
vt->parser.v.csi.argi++;
|
||||
vt->parser.v.csi.args[vt->parser.v.csi.argi] = CSI_ARG_MISSING;
|
||||
break;
|
||||
}
|
||||
|
||||
vt->parser.v.csi.argi++;
|
||||
vt->parser.intermedlen = 0;
|
||||
vt->parser.state = CSI_INTERMED;
|
||||
FALLTHROUGH;
|
||||
case CSI_INTERMED:
|
||||
if (is_intermed(c)) {
|
||||
if (vt->parser.intermedlen < INTERMED_MAX - 1) {
|
||||
vt->parser.intermed[vt->parser.intermedlen++] = (char)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, (char)c);
|
||||
}
|
||||
// else was invalid CSI
|
||||
|
||||
ENTER_NORMAL_STATE();
|
||||
break;
|
||||
|
||||
case OSC_COMMAND:
|
||||
// Numerical value of command
|
||||
if (c >= '0' && c <= '9') {
|
||||
if (vt->parser.v.osc.command == -1) {
|
||||
vt->parser.v.osc.command = 0;
|
||||
} else {
|
||||
vt->parser.v.osc.command *= 10;
|
||||
}
|
||||
vt->parser.v.osc.command += c - '0';
|
||||
break;
|
||||
}
|
||||
if (c == ';') {
|
||||
vt->parser.state = OSC;
|
||||
string_start = bytes + pos + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
string_start = bytes + pos;
|
||||
string_len = 0;
|
||||
vt->parser.state = OSC;
|
||||
goto string_state;
|
||||
|
||||
case DCS_COMMAND:
|
||||
if (vt->parser.v.dcs.commandlen < CSI_LEADER_MAX) {
|
||||
vt->parser.v.dcs.command[vt->parser.v.dcs.commandlen++] = (char)c;
|
||||
}
|
||||
|
||||
if (c >= 0x40 && c <= 0x7e) {
|
||||
string_start = bytes + pos + 1;
|
||||
vt->parser.state = DCS_VTERM;
|
||||
}
|
||||
break;
|
||||
|
||||
string_state:
|
||||
case OSC:
|
||||
case DCS_VTERM:
|
||||
case APC:
|
||||
case PM:
|
||||
case SOS:
|
||||
if (c == 0x07 || (c1_allowed && c == 0x9c)) {
|
||||
string_fragment(vt, string_start, string_len, true);
|
||||
ENTER_NORMAL_STATE();
|
||||
}
|
||||
break;
|
||||
|
||||
case NORMAL:
|
||||
if (vt->parser.in_esc) {
|
||||
if (is_intermed(c)) {
|
||||
if (vt->parser.intermedlen < INTERMED_MAX - 1) {
|
||||
vt->parser.intermed[vt->parser.intermedlen++] = (char)c;
|
||||
}
|
||||
} else if (c >= 0x30 && c < 0x7f) {
|
||||
do_escape(vt, (char)c);
|
||||
vt->parser.in_esc = 0;
|
||||
ENTER_NORMAL_STATE();
|
||||
} else {
|
||||
DEBUG_LOG("TODO: Unhandled byte %02x in Escape\n", c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (c1_allowed && c >= 0x80 && c < 0xa0) {
|
||||
switch (c) {
|
||||
case 0x90: // DCS
|
||||
vt->parser.string_initial = true;
|
||||
vt->parser.v.dcs.commandlen = 0;
|
||||
ENTER_STATE(DCS_COMMAND);
|
||||
break;
|
||||
case 0x98: // SOS
|
||||
vt->parser.string_initial = true;
|
||||
ENTER_STATE(SOS);
|
||||
string_start = bytes + pos + 1;
|
||||
break;
|
||||
case 0x9b: // CSI
|
||||
vt->parser.v.csi.leaderlen = 0;
|
||||
ENTER_STATE(CSI_LEADER);
|
||||
break;
|
||||
case 0x9d: // OSC
|
||||
vt->parser.v.osc.command = -1;
|
||||
vt->parser.string_initial = true;
|
||||
ENTER_STATE(OSC_COMMAND);
|
||||
break;
|
||||
case 0x9e: // PM
|
||||
vt->parser.string_initial = true;
|
||||
ENTER_STATE(PM);
|
||||
string_start = bytes + pos + 1;
|
||||
break;
|
||||
case 0x9f: // APC
|
||||
vt->parser.string_initial = true;
|
||||
ENTER_STATE(APC);
|
||||
string_start = bytes + pos + 1;
|
||||
break;
|
||||
default:
|
||||
do_control(vt, c);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
size_t eaten = 0;
|
||||
if (vt->parser.callbacks && vt->parser.callbacks->text) {
|
||||
eaten = (size_t)(*vt->parser.callbacks->text)(bytes + pos, len - pos, vt->parser.cbdata);
|
||||
}
|
||||
|
||||
if (!eaten) {
|
||||
DEBUG_LOG("libvterm: Text callback did not consume any input\n");
|
||||
// force it to make progress
|
||||
eaten = 1;
|
||||
}
|
||||
|
||||
pos += (eaten - 1); // we'll ++ it again in a moment
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (string_start) {
|
||||
size_t string_len = (size_t)(bytes + pos - string_start);
|
||||
if (string_len > 0) {
|
||||
if (vt->parser.in_esc) {
|
||||
string_len -= 1;
|
||||
}
|
||||
string_fragment(vt, string_start, string_len, false);
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user)
|
||||
{
|
||||
vt->parser.callbacks = callbacks;
|
||||
vt->parser.cbdata = user;
|
||||
}
|
9
src/nvim/vterm/parser.h
Normal file
9
src/nvim/vterm/parser.h
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "nvim/vterm/vterm_defs.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "vterm/parser.h.generated.h"
|
||||
#endif
|
644
src/nvim/vterm/pen.c
Normal file
644
src/nvim/vterm/pen.c
Normal file
@ -0,0 +1,644 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nvim/vterm/pen.h"
|
||||
#include "nvim/vterm/vterm.h"
|
||||
#include "nvim/vterm/vterm_internal_defs.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "vterm/pen.c.generated.h"
|
||||
#endif
|
||||
|
||||
// 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 uint8_t ramp6[] = {
|
||||
0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF,
|
||||
};
|
||||
|
||||
static uint8_t 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, (uint8_t)CSI_ARG(args[0]), (uint8_t)CSI_ARG(args[1]),
|
||||
(uint8_t)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, (uint8_t)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, (uint8_t)col);
|
||||
|
||||
setpenattr_col(state, attr, *colp);
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
void vterm_state_resetpen(VTermState *state)
|
||||
{
|
||||
state->pen.bold = 0; setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
|
||||
state->pen.underline = 0; setpenattr_int(state, VTERM_ATTR_UNDERLINE, 0);
|
||||
state->pen.italic = 0; setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
|
||||
state->pen.blink = 0; setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
|
||||
state->pen.reverse = 0; setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
|
||||
state->pen.conceal = 0; setpenattr_bool(state, VTERM_ATTR_CONCEAL, 0);
|
||||
state->pen.strike = 0; setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
|
||||
state->pen.font = 0; setpenattr_int(state, VTERM_ATTR_FONT, 0);
|
||||
state->pen.small = 0; setpenattr_bool(state, VTERM_ATTR_SMALL, 0);
|
||||
state->pen.baseline = 0; setpenattr_int(state, VTERM_ATTR_BASELINE, 0);
|
||||
|
||||
state->pen.fg = state->default_fg;
|
||||
setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->default_fg);
|
||||
state->pen.bg = state->default_bg;
|
||||
setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->default_bg);
|
||||
|
||||
state->pen.uri = 0; setpenattr_int(state, VTERM_ATTR_URI, 0);
|
||||
}
|
||||
|
||||
void vterm_state_savepen(VTermState *state, int save)
|
||||
{
|
||||
if (save) {
|
||||
state->saved.pen = state->pen;
|
||||
} else {
|
||||
state->pen = state->saved.pen;
|
||||
|
||||
setpenattr_bool(state, VTERM_ATTR_BOLD, state->pen.bold);
|
||||
setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
|
||||
setpenattr_bool(state, VTERM_ATTR_ITALIC, state->pen.italic);
|
||||
setpenattr_bool(state, VTERM_ATTR_BLINK, state->pen.blink);
|
||||
setpenattr_bool(state, VTERM_ATTR_REVERSE, state->pen.reverse);
|
||||
setpenattr_bool(state, VTERM_ATTR_CONCEAL, state->pen.conceal);
|
||||
setpenattr_bool(state, VTERM_ATTR_STRIKE, state->pen.strike);
|
||||
setpenattr_int(state, VTERM_ATTR_FONT, state->pen.font);
|
||||
setpenattr_bool(state, VTERM_ATTR_SMALL, state->pen.small);
|
||||
setpenattr_int(state, VTERM_ATTR_BASELINE, state->pen.baseline);
|
||||
|
||||
setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
|
||||
setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
|
||||
|
||||
setpenattr_int(state, VTERM_ATTR_URI, state->pen.uri);
|
||||
}
|
||||
}
|
||||
|
||||
void vterm_state_set_default_colors(VTermState *state, const VTermColor *default_fg,
|
||||
const VTermColor *default_bg)
|
||||
{
|
||||
if (default_fg) {
|
||||
state->default_fg = *default_fg;
|
||||
state->default_fg.type = (state->default_fg.type & ~VTERM_COLOR_DEFAULT_MASK)
|
||||
| VTERM_COLOR_DEFAULT_FG;
|
||||
}
|
||||
|
||||
if (default_bg) {
|
||||
state->default_bg = *default_bg;
|
||||
state->default_bg.type = (state->default_bg.type & ~VTERM_COLOR_DEFAULT_MASK)
|
||||
| VTERM_COLOR_DEFAULT_BG;
|
||||
}
|
||||
}
|
||||
|
||||
void vterm_state_set_palette_color(VTermState *state, int index, const VTermColor *col)
|
||||
{
|
||||
if (index >= 0 && index < 16) {
|
||||
state->colors[index] = *col;
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)
|
||||
{
|
||||
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_setpen(VTermState *state, const long args[], int argcount)
|
||||
{
|
||||
// SGR - ECMA-48 8.3.117
|
||||
|
||||
int argi = 0;
|
||||
int value;
|
||||
|
||||
while (argi < argcount) {
|
||||
// This logic is easier to do 'done' backwards; set it true, and make it
|
||||
// false again in the 'default' case
|
||||
int done = 1;
|
||||
|
||||
long arg;
|
||||
switch (arg = CSI_ARG(args[argi])) {
|
||||
case CSI_ARG_MISSING:
|
||||
case 0: // Reset
|
||||
vterm_state_resetpen(state);
|
||||
break;
|
||||
|
||||
case 1: { // Bold on
|
||||
const VTermColor *fg = &state->pen.fg;
|
||||
state->pen.bold = 1;
|
||||
setpenattr_bool(state, VTERM_ATTR_BOLD, 1);
|
||||
if (!VTERM_COLOR_IS_DEFAULT_FG(fg) && VTERM_COLOR_IS_INDEXED(fg) && fg->indexed.idx < 8
|
||||
&& state->bold_is_highbright) {
|
||||
set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, fg->indexed.idx + (state->pen.bold ? 8 : 0));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 3: // Italic on
|
||||
state->pen.italic = 1;
|
||||
setpenattr_bool(state, VTERM_ATTR_ITALIC, 1);
|
||||
break;
|
||||
|
||||
case 4: // Underline
|
||||
state->pen.underline = VTERM_UNDERLINE_SINGLE;
|
||||
if (CSI_ARG_HAS_MORE(args[argi])) {
|
||||
argi++;
|
||||
switch (CSI_ARG(args[argi])) {
|
||||
case 0:
|
||||
state->pen.underline = 0;
|
||||
break;
|
||||
case 1:
|
||||
state->pen.underline = VTERM_UNDERLINE_SINGLE;
|
||||
break;
|
||||
case 2:
|
||||
state->pen.underline = VTERM_UNDERLINE_DOUBLE;
|
||||
break;
|
||||
case 3:
|
||||
state->pen.underline = VTERM_UNDERLINE_CURLY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
|
||||
break;
|
||||
|
||||
case 5: // Blink
|
||||
state->pen.blink = 1;
|
||||
setpenattr_bool(state, VTERM_ATTR_BLINK, 1);
|
||||
break;
|
||||
|
||||
case 7: // Reverse on
|
||||
state->pen.reverse = 1;
|
||||
setpenattr_bool(state, VTERM_ATTR_REVERSE, 1);
|
||||
break;
|
||||
|
||||
case 8: // Conceal on
|
||||
state->pen.conceal = 1;
|
||||
setpenattr_bool(state, VTERM_ATTR_CONCEAL, 1);
|
||||
break;
|
||||
|
||||
case 9: // Strikethrough on
|
||||
state->pen.strike = 1;
|
||||
setpenattr_bool(state, VTERM_ATTR_STRIKE, 1);
|
||||
break;
|
||||
|
||||
case 10:
|
||||
case 11:
|
||||
case 12:
|
||||
case 13:
|
||||
case 14:
|
||||
case 15:
|
||||
case 16:
|
||||
case 17:
|
||||
case 18:
|
||||
case 19: // Select font
|
||||
state->pen.font = CSI_ARG(args[argi]) - 10;
|
||||
setpenattr_int(state, VTERM_ATTR_FONT, state->pen.font);
|
||||
break;
|
||||
|
||||
case 21: // Underline double
|
||||
state->pen.underline = VTERM_UNDERLINE_DOUBLE;
|
||||
setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
|
||||
break;
|
||||
|
||||
case 22: // Bold off
|
||||
state->pen.bold = 0;
|
||||
setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
|
||||
break;
|
||||
|
||||
case 23: // Italic and Gothic (currently unsupported) off
|
||||
state->pen.italic = 0;
|
||||
setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
|
||||
break;
|
||||
|
||||
case 24: // Underline off
|
||||
state->pen.underline = 0;
|
||||
setpenattr_int(state, VTERM_ATTR_UNDERLINE, 0);
|
||||
break;
|
||||
|
||||
case 25: // Blink off
|
||||
state->pen.blink = 0;
|
||||
setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
|
||||
break;
|
||||
|
||||
case 27: // Reverse off
|
||||
state->pen.reverse = 0;
|
||||
setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
|
||||
break;
|
||||
|
||||
case 28: // Conceal off (Reveal)
|
||||
state->pen.conceal = 0;
|
||||
setpenattr_bool(state, VTERM_ATTR_CONCEAL, 0);
|
||||
break;
|
||||
|
||||
case 29: // Strikethrough off
|
||||
state->pen.strike = 0;
|
||||
setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
|
||||
break;
|
||||
|
||||
case 30:
|
||||
case 31:
|
||||
case 32:
|
||||
case 33:
|
||||
case 34:
|
||||
case 35:
|
||||
case 36:
|
||||
case 37: // Foreground colour palette
|
||||
value = CSI_ARG(args[argi]) - 30;
|
||||
if (state->pen.bold && state->bold_is_highbright) {
|
||||
value += 8;
|
||||
}
|
||||
set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
|
||||
break;
|
||||
|
||||
case 38: // Foreground colour alternative palette
|
||||
if (argcount - argi < 1) {
|
||||
return;
|
||||
}
|
||||
argi += 1 + lookup_colour(state, CSI_ARG(args[argi + 1]), args + argi + 2,
|
||||
argcount - argi - 2, &state->pen.fg);
|
||||
setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
|
||||
break;
|
||||
|
||||
case 39: // Foreground colour default
|
||||
state->pen.fg = state->default_fg;
|
||||
setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
|
||||
break;
|
||||
|
||||
case 40:
|
||||
case 41:
|
||||
case 42:
|
||||
case 43:
|
||||
case 44:
|
||||
case 45:
|
||||
case 46:
|
||||
case 47: // Background colour palette
|
||||
value = CSI_ARG(args[argi]) - 40;
|
||||
set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
|
||||
break;
|
||||
|
||||
case 48: // Background colour alternative palette
|
||||
if (argcount - argi < 1) {
|
||||
return;
|
||||
}
|
||||
argi += 1 + lookup_colour(state, CSI_ARG(args[argi + 1]), args + argi + 2,
|
||||
argcount - argi - 2, &state->pen.bg);
|
||||
setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
|
||||
break;
|
||||
|
||||
case 49: // Default background
|
||||
state->pen.bg = state->default_bg;
|
||||
setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
|
||||
break;
|
||||
|
||||
case 73: // Superscript
|
||||
case 74: // Subscript
|
||||
case 75: // Superscript/subscript off
|
||||
state->pen.small = (arg != 75);
|
||||
state->pen.baseline =
|
||||
(arg == 73) ? VTERM_BASELINE_RAISE
|
||||
: (arg == 74) ? VTERM_BASELINE_LOWER
|
||||
: VTERM_BASELINE_NORMAL;
|
||||
setpenattr_bool(state, VTERM_ATTR_SMALL, state->pen.small);
|
||||
setpenattr_int(state, VTERM_ATTR_BASELINE, state->pen.baseline);
|
||||
break;
|
||||
|
||||
case 90:
|
||||
case 91:
|
||||
case 92:
|
||||
case 93:
|
||||
case 94:
|
||||
case 95:
|
||||
case 96:
|
||||
case 97: // Foreground colour high-intensity palette
|
||||
value = CSI_ARG(args[argi]) - 90 + 8;
|
||||
set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
|
||||
break;
|
||||
|
||||
case 100:
|
||||
case 101:
|
||||
case 102:
|
||||
case 103:
|
||||
case 104:
|
||||
case 105:
|
||||
case 106:
|
||||
case 107: // Background colour high-intensity palette
|
||||
value = CSI_ARG(args[argi]) - 100 + 8;
|
||||
set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
|
||||
break;
|
||||
|
||||
default:
|
||||
done = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
DEBUG_LOG("libvterm: Unhandled CSI SGR %ld\n", arg);
|
||||
}
|
||||
|
||||
while (CSI_ARG_HAS_MORE(args[argi++])) {}
|
||||
}
|
||||
}
|
||||
|
||||
static int vterm_state_getpen_color(const VTermColor *col, int argi, long args[], int fg)
|
||||
{
|
||||
// Do nothing if the given color is the default color
|
||||
if ((fg && VTERM_COLOR_IS_DEFAULT_FG(col))
|
||||
|| (!fg && VTERM_COLOR_IS_DEFAULT_BG(col))) {
|
||||
return argi;
|
||||
}
|
||||
|
||||
// Decide whether to send an indexed color or an RGB color
|
||||
if (VTERM_COLOR_IS_INDEXED(col)) {
|
||||
const uint8_t idx = col->indexed.idx;
|
||||
if (idx < 8) {
|
||||
args[argi++] = (idx + (fg ? 30 : 40));
|
||||
} else if (idx < 16) {
|
||||
args[argi++] = (idx - 8 + (fg ? 90 : 100));
|
||||
} else {
|
||||
args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
|
||||
args[argi++] = CSI_ARG_FLAG_MORE | 5;
|
||||
args[argi++] = idx;
|
||||
}
|
||||
} else if (VTERM_COLOR_IS_RGB(col)) {
|
||||
args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
|
||||
args[argi++] = CSI_ARG_FLAG_MORE | 2;
|
||||
args[argi++] = CSI_ARG_FLAG_MORE | col->rgb.red;
|
||||
args[argi++] = CSI_ARG_FLAG_MORE | col->rgb.green;
|
||||
args[argi++] = col->rgb.blue;
|
||||
}
|
||||
return argi;
|
||||
}
|
||||
|
||||
int vterm_state_getpen(VTermState *state, long args[], int argcount)
|
||||
{
|
||||
int argi = 0;
|
||||
|
||||
if (state->pen.bold) {
|
||||
args[argi++] = 1;
|
||||
}
|
||||
|
||||
if (state->pen.italic) {
|
||||
args[argi++] = 3;
|
||||
}
|
||||
|
||||
if (state->pen.underline == VTERM_UNDERLINE_SINGLE) {
|
||||
args[argi++] = 4;
|
||||
}
|
||||
if (state->pen.underline == VTERM_UNDERLINE_CURLY) {
|
||||
args[argi++] = 4 | CSI_ARG_FLAG_MORE, args[argi++] = 3;
|
||||
}
|
||||
|
||||
if (state->pen.blink) {
|
||||
args[argi++] = 5;
|
||||
}
|
||||
|
||||
if (state->pen.reverse) {
|
||||
args[argi++] = 7;
|
||||
}
|
||||
|
||||
if (state->pen.conceal) {
|
||||
args[argi++] = 8;
|
||||
}
|
||||
|
||||
if (state->pen.strike) {
|
||||
args[argi++] = 9;
|
||||
}
|
||||
|
||||
if (state->pen.font) {
|
||||
args[argi++] = 10 + state->pen.font;
|
||||
}
|
||||
|
||||
if (state->pen.underline == VTERM_UNDERLINE_DOUBLE) {
|
||||
args[argi++] = 21;
|
||||
}
|
||||
|
||||
argi = vterm_state_getpen_color(&state->pen.fg, argi, args, true);
|
||||
|
||||
argi = vterm_state_getpen_color(&state->pen.bg, argi, args, false);
|
||||
|
||||
if (state->pen.small) {
|
||||
if (state->pen.baseline == VTERM_BASELINE_RAISE) {
|
||||
args[argi++] = 73;
|
||||
} else if (state->pen.baseline == VTERM_BASELINE_LOWER) {
|
||||
args[argi++] = 74;
|
||||
}
|
||||
}
|
||||
|
||||
return argi;
|
||||
}
|
||||
|
||||
int vterm_state_set_penattr(VTermState *state, VTermAttr attr, VTermValueType type, VTermValue *val)
|
||||
{
|
||||
if (!val) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 0;
|
||||
}
|
||||
|
||||
switch (attr) {
|
||||
case VTERM_ATTR_BOLD:
|
||||
state->pen.bold = (unsigned)val->boolean;
|
||||
break;
|
||||
case VTERM_ATTR_UNDERLINE:
|
||||
state->pen.underline = (unsigned)val->number;
|
||||
break;
|
||||
case VTERM_ATTR_ITALIC:
|
||||
state->pen.italic = (unsigned)val->boolean;
|
||||
break;
|
||||
case VTERM_ATTR_BLINK:
|
||||
state->pen.blink = (unsigned)val->boolean;
|
||||
break;
|
||||
case VTERM_ATTR_REVERSE:
|
||||
state->pen.reverse = (unsigned)val->boolean;
|
||||
break;
|
||||
case VTERM_ATTR_CONCEAL:
|
||||
state->pen.conceal = (unsigned)val->boolean;
|
||||
break;
|
||||
case VTERM_ATTR_STRIKE:
|
||||
state->pen.strike = (unsigned)val->boolean;
|
||||
break;
|
||||
case VTERM_ATTR_FONT:
|
||||
state->pen.font = (unsigned)val->number;
|
||||
break;
|
||||
case VTERM_ATTR_FOREGROUND:
|
||||
state->pen.fg = val->color;
|
||||
break;
|
||||
case VTERM_ATTR_BACKGROUND:
|
||||
state->pen.bg = val->color;
|
||||
break;
|
||||
case VTERM_ATTR_SMALL:
|
||||
state->pen.small = (unsigned)val->boolean;
|
||||
break;
|
||||
case VTERM_ATTR_BASELINE:
|
||||
state->pen.baseline = (unsigned)val->number;
|
||||
break;
|
||||
case VTERM_ATTR_URI:
|
||||
state->pen.uri = val->number;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (state->callbacks && state->callbacks->setpenattr) {
|
||||
(*state->callbacks->setpenattr)(attr, val, state->cbdata);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
9
src/nvim/vterm/pen.h
Normal file
9
src/nvim/vterm/pen.h
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "nvim/vterm/vterm_defs.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "vterm/pen.h.generated.h"
|
||||
#endif
|
1103
src/nvim/vterm/screen.c
Normal file
1103
src/nvim/vterm/screen.c
Normal file
File diff suppressed because it is too large
Load Diff
9
src/nvim/vterm/screen.h
Normal file
9
src/nvim/vterm/screen.h
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "nvim/vterm/vterm_defs.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "vterm/screen.h.generated.h"
|
||||
#endif
|
2310
src/nvim/vterm/state.c
Normal file
2310
src/nvim/vterm/state.c
Normal file
File diff suppressed because it is too large
Load Diff
7
src/nvim/vterm/state.h
Normal file
7
src/nvim/vterm/state.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "nvim/vterm/vterm_defs.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "vterm/state.h.generated.h"
|
||||
#endif
|
335
src/nvim/vterm/vterm.c
Normal file
335
src/nvim/vterm/vterm.c
Normal file
@ -0,0 +1,335 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "auto/config.h"
|
||||
#include "nvim/memory.h"
|
||||
#include "nvim/vterm/screen.h"
|
||||
#include "nvim/vterm/state.h"
|
||||
#include "nvim/vterm/vterm.h"
|
||||
#include "nvim/vterm/vterm_internal_defs.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "vterm/vterm.c.generated.h"
|
||||
#endif
|
||||
|
||||
// *****************
|
||||
// * API functions *
|
||||
// *****************
|
||||
|
||||
static void *default_malloc(size_t size, void *allocdata)
|
||||
{
|
||||
void *ptr = xmalloc(size);
|
||||
if (ptr) {
|
||||
memset(ptr, 0, size);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void default_free(void *ptr, void *allocdata)
|
||||
{
|
||||
xfree(ptr);
|
||||
}
|
||||
|
||||
static VTermAllocatorFunctions default_allocator = {
|
||||
.malloc = &default_malloc,
|
||||
.free = &default_free,
|
||||
};
|
||||
|
||||
/// Convenient shortcut for default cases
|
||||
VTerm *vterm_new(int rows, int cols)
|
||||
{
|
||||
return vterm_build(&(const struct VTermBuilder){
|
||||
.rows = rows,
|
||||
.cols = cols,
|
||||
});
|
||||
}
|
||||
|
||||
// A handy macro for defaulting values out of builder fields
|
||||
#define DEFAULT(v, def) ((v) ? (v) : (def))
|
||||
|
||||
VTerm *vterm_build(const struct VTermBuilder *builder)
|
||||
{
|
||||
const VTermAllocatorFunctions *allocator = DEFAULT(builder->allocator, &default_allocator);
|
||||
|
||||
// Need to bootstrap using the allocator function directly
|
||||
VTerm *vt = (*allocator->malloc)(sizeof(VTerm), builder->allocdata);
|
||||
|
||||
vt->allocator = allocator;
|
||||
vt->allocdata = builder->allocdata;
|
||||
|
||||
vt->rows = builder->rows;
|
||||
vt->cols = builder->cols;
|
||||
|
||||
vt->parser.state = NORMAL;
|
||||
|
||||
vt->parser.callbacks = NULL;
|
||||
vt->parser.cbdata = NULL;
|
||||
|
||||
vt->parser.emit_nul = false;
|
||||
|
||||
vt->outfunc = NULL;
|
||||
vt->outdata = NULL;
|
||||
|
||||
vt->outbuffer_len = DEFAULT(builder->outbuffer_len, 4096);
|
||||
vt->outbuffer_cur = 0;
|
||||
vt->outbuffer = vterm_allocator_malloc(vt, vt->outbuffer_len);
|
||||
|
||||
vt->tmpbuffer_len = DEFAULT(builder->tmpbuffer_len, 4096);
|
||||
vt->tmpbuffer = vterm_allocator_malloc(vt, vt->tmpbuffer_len);
|
||||
|
||||
return vt;
|
||||
}
|
||||
|
||||
void vterm_free(VTerm *vt)
|
||||
{
|
||||
if (vt->screen) {
|
||||
vterm_screen_free(vt->screen);
|
||||
}
|
||||
|
||||
if (vt->state) {
|
||||
vterm_state_free(vt->state);
|
||||
}
|
||||
|
||||
vterm_allocator_free(vt, vt->outbuffer);
|
||||
vterm_allocator_free(vt, vt->tmpbuffer);
|
||||
|
||||
vterm_allocator_free(vt, vt);
|
||||
}
|
||||
|
||||
void *vterm_allocator_malloc(VTerm *vt, size_t size)
|
||||
{
|
||||
return (*vt->allocator->malloc)(size, vt->allocdata);
|
||||
}
|
||||
|
||||
void vterm_allocator_free(VTerm *vt, void *ptr)
|
||||
{
|
||||
(*vt->allocator->free)(ptr, vt->allocdata);
|
||||
}
|
||||
|
||||
void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp)
|
||||
{
|
||||
if (rowsp) {
|
||||
*rowsp = vt->rows;
|
||||
}
|
||||
if (colsp) {
|
||||
*colsp = vt->cols;
|
||||
}
|
||||
}
|
||||
|
||||
void vterm_set_size(VTerm *vt, int rows, int cols)
|
||||
{
|
||||
if (rows < 1 || cols < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
vt->rows = rows;
|
||||
vt->cols = cols;
|
||||
|
||||
if (vt->parser.callbacks && vt->parser.callbacks->resize) {
|
||||
(*vt->parser.callbacks->resize)(rows, cols, vt->parser.cbdata);
|
||||
}
|
||||
}
|
||||
|
||||
void vterm_set_utf8(VTerm *vt, int is_utf8)
|
||||
{
|
||||
vt->mode.utf8 = (unsigned)is_utf8;
|
||||
}
|
||||
|
||||
void vterm_output_set_callback(VTerm *vt, VTermOutputCallback *func, void *user)
|
||||
{
|
||||
vt->outfunc = func;
|
||||
vt->outdata = user;
|
||||
}
|
||||
|
||||
void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len)
|
||||
{
|
||||
if (vt->outfunc) {
|
||||
(vt->outfunc)(bytes, len, vt->outdata);
|
||||
return;
|
||||
}
|
||||
|
||||
if (len > vt->outbuffer_len - vt->outbuffer_cur) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(vt->outbuffer + vt->outbuffer_cur, bytes, len);
|
||||
vt->outbuffer_cur += len;
|
||||
}
|
||||
|
||||
void vterm_push_output_sprintf(VTerm *vt, const char *format, ...)
|
||||
FUNC_ATTR_PRINTF(2, 3)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
size_t len = (size_t)vsnprintf(vt->tmpbuffer, vt->tmpbuffer_len, format, args);
|
||||
vterm_push_output_bytes(vt, vt->tmpbuffer, len);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void vterm_push_output_sprintf_ctrl(VTerm *vt, uint8_t ctrl, const char *fmt, ...)
|
||||
FUNC_ATTR_PRINTF(3, 4)
|
||||
{
|
||||
size_t cur;
|
||||
|
||||
if (ctrl >= 0x80 && !vt->mode.ctrl8bit) {
|
||||
cur = (size_t)snprintf(vt->tmpbuffer, vt->tmpbuffer_len, ESC_S "%c", ctrl - 0x40);
|
||||
} else {
|
||||
cur = (size_t)snprintf(vt->tmpbuffer, vt->tmpbuffer_len, "%c", ctrl);
|
||||
}
|
||||
|
||||
if (cur >= vt->tmpbuffer_len) {
|
||||
return;
|
||||
}
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
cur += (size_t)vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (cur >= vt->tmpbuffer_len) {
|
||||
return;
|
||||
}
|
||||
|
||||
vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
|
||||
}
|
||||
|
||||
void vterm_push_output_sprintf_str(VTerm *vt, uint8_t ctrl, bool term, const char *fmt, ...)
|
||||
FUNC_ATTR_PRINTF(4, 5)
|
||||
{
|
||||
size_t cur = 0;
|
||||
|
||||
if (ctrl) {
|
||||
if (ctrl >= 0x80 && !vt->mode.ctrl8bit) {
|
||||
cur = (size_t)snprintf(vt->tmpbuffer, vt->tmpbuffer_len, ESC_S "%c", ctrl - 0x40);
|
||||
} else {
|
||||
cur = (size_t)snprintf(vt->tmpbuffer, vt->tmpbuffer_len, "%c", ctrl);
|
||||
}
|
||||
|
||||
if (cur >= vt->tmpbuffer_len) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
cur += (size_t)vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (cur >= vt->tmpbuffer_len) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (term) {
|
||||
cur += (size_t)snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
|
||||
vt->mode.ctrl8bit ? "\x9C" : ESC_S "\\"); // ST
|
||||
|
||||
if (cur >= vt->tmpbuffer_len) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
|
||||
}
|
||||
|
||||
VTermValueType vterm_get_attr_type(VTermAttr attr)
|
||||
{
|
||||
switch (attr) {
|
||||
case VTERM_ATTR_BOLD:
|
||||
return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_ATTR_UNDERLINE:
|
||||
return VTERM_VALUETYPE_INT;
|
||||
case VTERM_ATTR_ITALIC:
|
||||
return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_ATTR_BLINK:
|
||||
return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_ATTR_REVERSE:
|
||||
return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_ATTR_CONCEAL:
|
||||
return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_ATTR_STRIKE:
|
||||
return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_ATTR_FONT:
|
||||
return VTERM_VALUETYPE_INT;
|
||||
case VTERM_ATTR_FOREGROUND:
|
||||
return VTERM_VALUETYPE_COLOR;
|
||||
case VTERM_ATTR_BACKGROUND:
|
||||
return VTERM_VALUETYPE_COLOR;
|
||||
case VTERM_ATTR_SMALL:
|
||||
return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_ATTR_BASELINE:
|
||||
return VTERM_VALUETYPE_INT;
|
||||
case VTERM_ATTR_URI:
|
||||
return VTERM_VALUETYPE_INT;
|
||||
|
||||
case VTERM_N_ATTRS:
|
||||
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);
|
||||
}
|
161
src/nvim/vterm/vterm.h
Normal file
161
src/nvim/vterm/vterm.h
Normal file
@ -0,0 +1,161 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "nvim/macros_defs.h"
|
||||
#include "nvim/types_defs.h"
|
||||
#include "nvim/vterm/vterm_defs.h"
|
||||
#include "nvim/vterm/vterm_keycodes_defs.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "vterm/vterm.h.generated.h"
|
||||
#endif
|
||||
|
||||
#define VTERM_VERSION_MAJOR 0
|
||||
#define VTERM_VERSION_MINOR 3
|
||||
|
||||
// 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))
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// ------------
|
||||
// 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))
|
||||
|
||||
enum {
|
||||
VTERM_UNDERLINE_OFF,
|
||||
VTERM_UNDERLINE_SINGLE,
|
||||
VTERM_UNDERLINE_DOUBLE,
|
||||
VTERM_UNDERLINE_CURLY,
|
||||
};
|
||||
|
||||
enum {
|
||||
VTERM_BASELINE_NORMAL,
|
||||
VTERM_BASELINE_RAISE,
|
||||
VTERM_BASELINE_LOWER,
|
||||
};
|
||||
|
||||
// Back-compat alias for the brief time it was in 0.3-RC1
|
||||
#define vterm_screen_set_reflow vterm_screen_enable_reflow
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
unsigned global_reverse : 1;
|
||||
unsigned reflow : 1;
|
||||
|
||||
// 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;
|
||||
};
|
319
src/nvim/vterm/vterm_defs.h
Normal file
319
src/nvim/vterm/vterm_defs.h
Normal file
@ -0,0 +1,319 @@
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "nvim/types_defs.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
|
||||
|
||||
typedef struct {
|
||||
int start_row;
|
||||
int end_row;
|
||||
int start_col;
|
||||
int end_col;
|
||||
} VTermRect;
|
||||
|
||||
// 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;
|
||||
|
||||
typedef struct {
|
||||
unsigned bold : 1;
|
||||
unsigned underline : 2;
|
||||
unsigned italic : 1;
|
||||
unsigned blink : 1;
|
||||
unsigned reverse : 1;
|
||||
unsigned conceal : 1;
|
||||
unsigned strike : 1;
|
||||
unsigned font : 4; // 0 to 9
|
||||
unsigned dwl : 1; // On a DECDWL or DECDHL line
|
||||
unsigned dhl : 2; // On a DECDHL line (1=top 2=bottom)
|
||||
unsigned small : 1;
|
||||
unsigned baseline : 2;
|
||||
} VTermScreenCellAttrs;
|
||||
|
||||
typedef struct {
|
||||
schar_T schar;
|
||||
char width;
|
||||
VTermScreenCellAttrs attrs;
|
||||
VTermColor fg, bg;
|
||||
int uri;
|
||||
} VTermScreenCell;
|
||||
|
||||
typedef enum {
|
||||
// VTERM_PROP_NONE = 0
|
||||
VTERM_PROP_CURSORVISIBLE = 1, // bool
|
||||
VTERM_PROP_CURSORBLINK, // bool
|
||||
VTERM_PROP_ALTSCREEN, // bool
|
||||
VTERM_PROP_TITLE, // string
|
||||
VTERM_PROP_ICONNAME, // string
|
||||
VTERM_PROP_REVERSE, // bool
|
||||
VTERM_PROP_CURSORSHAPE, // number
|
||||
VTERM_PROP_MOUSE, // number
|
||||
VTERM_PROP_FOCUSREPORT, // bool
|
||||
|
||||
VTERM_N_PROPS,
|
||||
} VTermProp;
|
||||
|
||||
typedef struct {
|
||||
const char *str;
|
||||
size_t len : 30;
|
||||
bool initial : 1;
|
||||
bool final : 1;
|
||||
} VTermStringFragment;
|
||||
|
||||
typedef union {
|
||||
int boolean;
|
||||
int number;
|
||||
VTermStringFragment string;
|
||||
VTermColor color;
|
||||
} VTermValue;
|
||||
|
||||
typedef struct {
|
||||
int (*damage)(VTermRect rect, void *user);
|
||||
int (*moverect)(VTermRect dest, VTermRect src, void *user);
|
||||
int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
|
||||
int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
|
||||
int (*bell)(void *user);
|
||||
int (*resize)(int rows, int cols, void *user);
|
||||
int (*sb_pushline)(int cols, const VTermScreenCell *cells, void *user);
|
||||
int (*sb_popline)(int cols, VTermScreenCell *cells, void *user);
|
||||
int (*sb_clear)(void *user);
|
||||
} VTermScreenCallbacks;
|
||||
|
||||
typedef struct {
|
||||
int (*control)(uint8_t control, void *user);
|
||||
int (*csi)(const char *leader, const long args[], int argcount, const char *intermed,
|
||||
char command, void *user);
|
||||
int (*osc)(int command, VTermStringFragment frag, void *user);
|
||||
int (*dcs)(const char *command, size_t commandlen, VTermStringFragment frag, void *user);
|
||||
int (*apc)(VTermStringFragment frag, void *user);
|
||||
int (*pm)(VTermStringFragment frag, void *user);
|
||||
int (*sos)(VTermStringFragment frag, void *user);
|
||||
} VTermStateFallbacks;
|
||||
|
||||
typedef 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;
|
||||
|
||||
typedef enum {
|
||||
VTERM_ATTR_BOLD_MASK = 1 << 0,
|
||||
VTERM_ATTR_UNDERLINE_MASK = 1 << 1,
|
||||
VTERM_ATTR_ITALIC_MASK = 1 << 2,
|
||||
VTERM_ATTR_BLINK_MASK = 1 << 3,
|
||||
VTERM_ATTR_REVERSE_MASK = 1 << 4,
|
||||
VTERM_ATTR_STRIKE_MASK = 1 << 5,
|
||||
VTERM_ATTR_FONT_MASK = 1 << 6,
|
||||
VTERM_ATTR_FOREGROUND_MASK = 1 << 7,
|
||||
VTERM_ATTR_BACKGROUND_MASK = 1 << 8,
|
||||
VTERM_ATTR_CONCEAL_MASK = 1 << 9,
|
||||
VTERM_ATTR_SMALL_MASK = 1 << 10,
|
||||
VTERM_ATTR_BASELINE_MASK = 1 << 11,
|
||||
VTERM_ATTR_URI_MASK = 1 << 12,
|
||||
|
||||
VTERM_ALL_ATTRS_MASK = (1 << 13) - 1,
|
||||
} VTermAttrMask;
|
||||
|
||||
typedef enum {
|
||||
// VTERM_VALUETYPE_NONE = 0
|
||||
VTERM_VALUETYPE_BOOL = 1,
|
||||
VTERM_VALUETYPE_INT,
|
||||
VTERM_VALUETYPE_STRING,
|
||||
VTERM_VALUETYPE_COLOR,
|
||||
|
||||
VTERM_N_VALUETYPES,
|
||||
} VTermValueType;
|
||||
|
||||
typedef enum {
|
||||
// VTERM_ATTR_NONE = 0
|
||||
VTERM_ATTR_BOLD = 1, // bool: 1, 22
|
||||
VTERM_ATTR_UNDERLINE, // number: 4, 21, 24
|
||||
VTERM_ATTR_ITALIC, // bool: 3, 23
|
||||
VTERM_ATTR_BLINK, // bool: 5, 25
|
||||
VTERM_ATTR_REVERSE, // bool: 7, 27
|
||||
VTERM_ATTR_CONCEAL, // bool: 8, 28
|
||||
VTERM_ATTR_STRIKE, // bool: 9, 29
|
||||
VTERM_ATTR_FONT, // number: 10-19
|
||||
VTERM_ATTR_FOREGROUND, // color: 30-39 90-97
|
||||
VTERM_ATTR_BACKGROUND, // color: 40-49 100-107
|
||||
VTERM_ATTR_SMALL, // bool: 73, 74, 75
|
||||
VTERM_ATTR_BASELINE, // number: 73, 74, 75
|
||||
VTERM_ATTR_URI, // number
|
||||
|
||||
VTERM_N_ATTRS,
|
||||
} VTermAttr;
|
||||
|
||||
enum {
|
||||
VTERM_PROP_CURSORSHAPE_BLOCK = 1,
|
||||
VTERM_PROP_CURSORSHAPE_UNDERLINE,
|
||||
VTERM_PROP_CURSORSHAPE_BAR_LEFT,
|
||||
|
||||
VTERM_N_PROP_CURSORSHAPES,
|
||||
};
|
||||
|
||||
enum {
|
||||
VTERM_PROP_MOUSE_NONE = 0,
|
||||
VTERM_PROP_MOUSE_CLICK,
|
||||
VTERM_PROP_MOUSE_DRAG,
|
||||
VTERM_PROP_MOUSE_MOVE,
|
||||
|
||||
VTERM_N_PROP_MOUSES,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
VTERM_SELECTION_CLIPBOARD = (1<<0),
|
||||
VTERM_SELECTION_PRIMARY = (1<<1),
|
||||
VTERM_SELECTION_SECONDARY = (1<<2),
|
||||
VTERM_SELECTION_SELECT = (1<<3),
|
||||
VTERM_SELECTION_CUT0 = (1<<4), // also CUT1 .. CUT7 by bitshifting
|
||||
} VTermSelectionMask;
|
||||
|
||||
typedef struct {
|
||||
schar_T schar;
|
||||
int width;
|
||||
unsigned protected_cell:1; // DECSCA-protected against DECSEL/DECSED
|
||||
unsigned dwl:1; // DECDWL or DECDHL double-width line
|
||||
unsigned dhl:2; // DECDHL double-height line (1=top 2=bottom)
|
||||
} VTermGlyphInfo;
|
||||
|
||||
typedef struct {
|
||||
unsigned doublewidth:1; // DECDWL or DECDHL line
|
||||
unsigned doubleheight:2; // DECDHL line (1=top 2=bottom)
|
||||
unsigned continuation:1; // Line is a flow continuation of the previous
|
||||
} VTermLineInfo;
|
||||
|
||||
// Copies of VTermState fields that the 'resize' callback might have reason to edit. 'resize'
|
||||
// callback gets total control of these fields and may free-and-reallocate them if required. They
|
||||
// will be copied back from the struct after the callback has returned.
|
||||
typedef struct {
|
||||
VTermPos pos; // current cursor position
|
||||
VTermLineInfo *lineinfos[2]; // [1] may be NULL
|
||||
} VTermStateFields;
|
||||
|
||||
typedef struct {
|
||||
// libvterm relies on this memory to be zeroed out before it is returned by the allocator.
|
||||
void *(*malloc)(size_t size, void *allocdata);
|
||||
void (*free)(void *ptr, void *allocdata);
|
||||
} VTermAllocatorFunctions;
|
||||
|
||||
// Setting output callback will override the buffer logic
|
||||
typedef void VTermOutputCallback(const char *s, size_t len, void *user);
|
||||
|
||||
struct VTermBuilder {
|
||||
int ver; // currently unused but reserved for some sort of ABI version flag
|
||||
|
||||
int rows, cols;
|
||||
|
||||
const VTermAllocatorFunctions *allocator;
|
||||
void *allocdata;
|
||||
|
||||
// Override default sizes for various structures
|
||||
size_t outbuffer_len; // default: 4096
|
||||
size_t tmpbuffer_len; // default: 4096
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int (*putglyph)(VTermGlyphInfo *info, VTermPos pos, void *user);
|
||||
int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
|
||||
int (*scrollrect)(VTermRect rect, int downward, int rightward, void *user);
|
||||
int (*moverect)(VTermRect dest, VTermRect src, void *user);
|
||||
int (*erase)(VTermRect rect, int selective, void *user);
|
||||
int (*initpen)(void *user);
|
||||
int (*setpenattr)(VTermAttr attr, VTermValue *val, void *user);
|
||||
int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
|
||||
int (*bell)(void *user);
|
||||
int (*resize)(int rows, int cols, VTermStateFields *fields, void *user);
|
||||
int (*setlineinfo)(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo,
|
||||
void *user);
|
||||
int (*sb_clear)(void *user);
|
||||
} VTermStateCallbacks;
|
||||
|
||||
typedef struct {
|
||||
int (*set)(VTermSelectionMask mask, VTermStringFragment frag, void *user);
|
||||
int (*query)(VTermSelectionMask mask, void *user);
|
||||
} VTermSelectionCallbacks;
|
||||
|
||||
typedef struct {
|
||||
int (*text)(const char *bytes, size_t len, void *user);
|
||||
int (*control)(uint8_t control, void *user);
|
||||
int (*escape)(const char *bytes, size_t len, void *user);
|
||||
int (*csi)(const char *leader, const long args[], int argcount, const char *intermed,
|
||||
char command, void *user);
|
||||
int (*osc)(int command, VTermStringFragment frag, void *user);
|
||||
int (*dcs)(const char *command, size_t commandlen, VTermStringFragment frag, void *user);
|
||||
int (*apc)(VTermStringFragment frag, void *user);
|
||||
int (*pm)(VTermStringFragment frag, void *user);
|
||||
int (*sos)(VTermStringFragment frag, void *user);
|
||||
int (*resize)(int rows, int cols, void *user);
|
||||
} VTermParserCallbacks;
|
||||
|
||||
// State of the pen at some moment in time, also used in a cell
|
||||
typedef struct {
|
||||
// After the bitfield
|
||||
VTermColor fg, bg;
|
||||
|
||||
// Opaque ID that maps to a URI in a set
|
||||
int uri;
|
||||
|
||||
unsigned bold : 1;
|
||||
unsigned underline : 2;
|
||||
unsigned italic : 1;
|
||||
unsigned blink : 1;
|
||||
unsigned reverse : 1;
|
||||
unsigned conceal : 1;
|
||||
unsigned strike : 1;
|
||||
unsigned font : 4; // 0 to 9
|
||||
unsigned small : 1;
|
||||
unsigned baseline : 2;
|
||||
|
||||
// Extra state storage that isn't strictly pen-related
|
||||
unsigned protected_cell : 1;
|
||||
unsigned dwl : 1; // on a DECDWL or DECDHL line
|
||||
unsigned dhl : 2; // on a DECDHL line (1=top 2=bottom)
|
||||
} ScreenPen;
|
||||
|
||||
// Internal representation of a screen cell
|
||||
typedef struct {
|
||||
schar_T schar;
|
||||
ScreenPen pen;
|
||||
} ScreenCell;
|
266
src/nvim/vterm/vterm_internal_defs.h
Normal file
266
src/nvim/vterm/vterm_internal_defs.h
Normal file
@ -0,0 +1,266 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "nvim/mbyte_defs.h"
|
||||
#include "nvim/vterm/vterm_defs.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
# define DEBUG_LOG(...) fprintf(stderr, __VA_ARGS__)
|
||||
#else
|
||||
# define DEBUG_LOG(...)
|
||||
#endif
|
||||
|
||||
#define ESC_S "\x1b"
|
||||
|
||||
#define INTERMED_MAX 16
|
||||
|
||||
#define CSI_ARGS_MAX 16
|
||||
#define CSI_LEADER_MAX 16
|
||||
|
||||
#define BUFIDX_PRIMARY 0
|
||||
#define BUFIDX_ALTSCREEN 1
|
||||
|
||||
typedef struct VTermEncoding VTermEncoding;
|
||||
|
||||
typedef struct {
|
||||
VTermEncoding *enc;
|
||||
|
||||
// This size should be increased if required by other stateful encodings
|
||||
char data[4 * sizeof(uint32_t)];
|
||||
} VTermEncodingInstance;
|
||||
|
||||
struct VTermPen {
|
||||
VTermColor fg;
|
||||
VTermColor bg;
|
||||
int uri;
|
||||
unsigned bold:1;
|
||||
unsigned underline:2;
|
||||
unsigned italic:1;
|
||||
unsigned blink:1;
|
||||
unsigned reverse:1;
|
||||
unsigned conceal:1;
|
||||
unsigned strike:1;
|
||||
unsigned font:4; // To store 0-9
|
||||
unsigned small:1;
|
||||
unsigned baseline:2;
|
||||
};
|
||||
|
||||
struct VTermState {
|
||||
VTerm *vt;
|
||||
|
||||
const VTermStateCallbacks *callbacks;
|
||||
void *cbdata;
|
||||
|
||||
const VTermStateFallbacks *fallbacks;
|
||||
void *fbdata;
|
||||
|
||||
int rows;
|
||||
int cols;
|
||||
|
||||
// Current cursor position
|
||||
VTermPos pos;
|
||||
|
||||
int at_phantom; // True if we're on the "81st" phantom column to defer a wraparound
|
||||
|
||||
int scrollregion_top;
|
||||
int scrollregion_bottom; // -1 means unbounded
|
||||
#define SCROLLREGION_BOTTOM(state) ((state)->scrollregion_bottom > \
|
||||
-1 ? (state)->scrollregion_bottom : (state)->rows)
|
||||
int scrollregion_left;
|
||||
#define SCROLLREGION_LEFT(state) ((state)->mode.leftrightmargin ? (state)->scrollregion_left : 0)
|
||||
int scrollregion_right; // -1 means unbounded
|
||||
#define SCROLLREGION_RIGHT(state) ((state)->mode.leftrightmargin \
|
||||
&& (state)->scrollregion_right > \
|
||||
-1 ? (state)->scrollregion_right : (state)->cols)
|
||||
|
||||
// Bitvector of tab stops
|
||||
uint8_t *tabstops;
|
||||
|
||||
// Primary and Altscreen; lineinfos[1] is lazily allocated as needed
|
||||
VTermLineInfo *lineinfos[2];
|
||||
|
||||
// lineinfo will == lineinfos[0] or lineinfos[1], depending on altscreen
|
||||
VTermLineInfo *lineinfo;
|
||||
#define ROWWIDTH(state, \
|
||||
row) ((state)->lineinfo[(row)].doublewidth ? ((state)->cols / 2) : (state)->cols)
|
||||
#define THISROWWIDTH(state) ROWWIDTH(state, (state)->pos.row)
|
||||
|
||||
// Mouse state
|
||||
int mouse_col, mouse_row;
|
||||
int mouse_buttons;
|
||||
int mouse_flags;
|
||||
#define MOUSE_WANT_CLICK 0x01
|
||||
#define MOUSE_WANT_DRAG 0x02
|
||||
#define MOUSE_WANT_MOVE 0x04
|
||||
|
||||
enum { MOUSE_X10, MOUSE_UTF8, MOUSE_SGR, MOUSE_RXVT, } mouse_protocol;
|
||||
|
||||
// Last glyph output, for Unicode recombining purposes
|
||||
char grapheme_buf[MAX_SCHAR_SIZE];
|
||||
size_t grapheme_len;
|
||||
uint32_t grapheme_last; // last added UTF-32 char
|
||||
GraphemeState grapheme_state;
|
||||
int combine_width; // The width of the glyph above
|
||||
VTermPos combine_pos; // Position before movement
|
||||
|
||||
struct {
|
||||
unsigned keypad:1;
|
||||
unsigned cursor:1;
|
||||
unsigned autowrap:1;
|
||||
unsigned insert:1;
|
||||
unsigned newline:1;
|
||||
unsigned cursor_visible:1;
|
||||
unsigned cursor_blink:1;
|
||||
unsigned cursor_shape:2;
|
||||
unsigned alt_screen:1;
|
||||
unsigned origin:1;
|
||||
unsigned screen:1;
|
||||
unsigned leftrightmargin:1;
|
||||
unsigned bracketpaste:1;
|
||||
unsigned 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 protected_cell : 1;
|
||||
|
||||
// Saved state under DEC mode 1048/1049
|
||||
struct {
|
||||
VTermPos pos;
|
||||
struct VTermPen pen;
|
||||
|
||||
struct {
|
||||
unsigned cursor_visible:1;
|
||||
unsigned cursor_blink:1;
|
||||
unsigned cursor_shape:2;
|
||||
} mode;
|
||||
} saved;
|
||||
|
||||
// Temporary state for DECRQSS parsing
|
||||
union {
|
||||
char decrqss[4];
|
||||
struct {
|
||||
uint16_t mask;
|
||||
enum {
|
||||
SELECTION_INITIAL,
|
||||
SELECTION_SELECTED,
|
||||
SELECTION_QUERY,
|
||||
SELECTION_SET_INITIAL,
|
||||
SELECTION_SET,
|
||||
SELECTION_INVALID,
|
||||
} state : 8;
|
||||
uint32_t recvpartial;
|
||||
uint32_t sendpartial;
|
||||
} selection;
|
||||
} tmp;
|
||||
|
||||
struct {
|
||||
const VTermSelectionCallbacks *callbacks;
|
||||
void *user;
|
||||
char *buffer;
|
||||
size_t buflen;
|
||||
} selection;
|
||||
};
|
||||
|
||||
struct VTerm {
|
||||
const VTermAllocatorFunctions *allocator;
|
||||
void *allocdata;
|
||||
|
||||
int rows;
|
||||
int cols;
|
||||
|
||||
struct {
|
||||
unsigned utf8:1;
|
||||
unsigned ctrl8bit:1;
|
||||
} mode;
|
||||
|
||||
struct {
|
||||
enum VTermParserState {
|
||||
NORMAL,
|
||||
CSI_LEADER,
|
||||
CSI_ARGS,
|
||||
CSI_INTERMED,
|
||||
DCS_COMMAND,
|
||||
// below here are the "string states"
|
||||
OSC_COMMAND,
|
||||
OSC,
|
||||
DCS_VTERM,
|
||||
APC,
|
||||
PM,
|
||||
SOS,
|
||||
} state;
|
||||
|
||||
bool in_esc : 1;
|
||||
|
||||
int intermedlen;
|
||||
char intermed[INTERMED_MAX];
|
||||
|
||||
union {
|
||||
struct {
|
||||
int leaderlen;
|
||||
char leader[CSI_LEADER_MAX];
|
||||
|
||||
int argi;
|
||||
long args[CSI_ARGS_MAX];
|
||||
} csi;
|
||||
struct {
|
||||
int command;
|
||||
} osc;
|
||||
struct {
|
||||
int commandlen;
|
||||
char command[CSI_LEADER_MAX];
|
||||
} dcs;
|
||||
} v;
|
||||
|
||||
const VTermParserCallbacks *callbacks;
|
||||
void *cbdata;
|
||||
|
||||
bool string_initial;
|
||||
|
||||
bool emit_nul;
|
||||
} parser;
|
||||
|
||||
// len == malloc()ed size; cur == number of valid bytes
|
||||
|
||||
VTermOutputCallback *outfunc;
|
||||
void *outdata;
|
||||
|
||||
char *outbuffer;
|
||||
size_t outbuffer_len;
|
||||
size_t outbuffer_cur;
|
||||
|
||||
char *tmpbuffer;
|
||||
size_t tmpbuffer_len;
|
||||
|
||||
VTermState *state;
|
||||
VTermScreen *screen;
|
||||
};
|
||||
|
||||
struct VTermEncoding {
|
||||
void (*init)(VTermEncoding *enc, void *data);
|
||||
void (*decode)(VTermEncoding *enc, void *data, uint32_t cp[], int *cpi, int cplen,
|
||||
const char bytes[], size_t *pos, size_t len);
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
ENC_UTF8,
|
||||
ENC_SINGLE_94,
|
||||
} VTermEncodingType;
|
||||
|
||||
enum {
|
||||
C1_SS3 = 0x8f,
|
||||
C1_DCS = 0x90,
|
||||
C1_CSI = 0x9b,
|
||||
C1_ST = 0x9c,
|
||||
C1_OSC = 0x9d,
|
||||
};
|
@ -1,5 +1,4 @@
|
||||
#ifndef __VTERM_INPUT_H__
|
||||
#define __VTERM_INPUT_H__
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
VTERM_MOD_NONE = 0x00,
|
||||
@ -7,7 +6,7 @@ typedef enum {
|
||||
VTERM_MOD_ALT = 0x02,
|
||||
VTERM_MOD_CTRL = 0x04,
|
||||
|
||||
VTERM_ALL_MODS_MASK = 0x07
|
||||
VTERM_ALL_MODS_MASK = 0x07,
|
||||
} VTermModifier;
|
||||
|
||||
typedef enum {
|
||||
@ -52,10 +51,8 @@ typedef enum {
|
||||
VTERM_KEY_KP_ENTER,
|
||||
VTERM_KEY_KP_EQUAL,
|
||||
|
||||
VTERM_KEY_MAX, // Must be last
|
||||
VTERM_N_KEYS = VTERM_KEY_MAX
|
||||
VTERM_KEY_MAX, // Must be last
|
||||
VTERM_N_KEYS = VTERM_KEY_MAX,
|
||||
} VTermKey;
|
||||
|
||||
#define VTERM_KEY_FUNCTION(n) (VTERM_KEY_FUNCTION_0+(n))
|
||||
|
||||
#endif
|
||||
#define VTERM_KEY_FUNCTION(n) (VTERM_KEY_FUNCTION_0 + (n))
|
@ -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;
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
static const struct StaticTableEncoding encoding_DECdrawing = {
|
||||
{ .decode = &decode_table },
|
||||
{
|
||||
[0x60] = 0x25C6, // BLACK DIAMOND
|
||||
[0x61] = 0x2592, // MEDIUM SHADE (checkerboard)
|
||||
[0x62] = 0x2409, // SYMBOL FOR HORIZONTAL TAB
|
||||
[0x63] = 0x240C, // SYMBOL FOR FORM FEED
|
||||
[0x64] = 0x240D, // SYMBOL FOR CARRIAGE RETURN
|
||||
[0x65] = 0x240A, // SYMBOL FOR LINE FEED
|
||||
[0x66] = 0x00B0, // DEGREE SIGN
|
||||
[0x67] = 0x00B1, // PLUS-MINUS SIGN (plus or minus)
|
||||
[0x68] = 0x2424, // SYMBOL FOR NEW LINE
|
||||
[0x69] = 0x240B, // SYMBOL FOR VERTICAL TAB
|
||||
[0x6a] = 0x2518, // BOX DRAWINGS LIGHT UP AND LEFT (bottom-right corner)
|
||||
[0x6b] = 0x2510, // BOX DRAWINGS LIGHT DOWN AND LEFT (top-right corner)
|
||||
[0x6c] = 0x250C, // BOX DRAWINGS LIGHT DOWN AND RIGHT (top-left corner)
|
||||
[0x6d] = 0x2514, // BOX DRAWINGS LIGHT UP AND RIGHT (bottom-left corner)
|
||||
[0x6e] = 0x253C, // BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL (crossing lines)
|
||||
[0x6f] = 0x23BA, // HORIZONTAL SCAN LINE-1
|
||||
[0x70] = 0x23BB, // HORIZONTAL SCAN LINE-3
|
||||
[0x71] = 0x2500, // BOX DRAWINGS LIGHT HORIZONTAL
|
||||
[0x72] = 0x23BC, // HORIZONTAL SCAN LINE-7
|
||||
[0x73] = 0x23BD, // HORIZONTAL SCAN LINE-9
|
||||
[0x74] = 0x251C, // BOX DRAWINGS LIGHT VERTICAL AND RIGHT
|
||||
[0x75] = 0x2524, // BOX DRAWINGS LIGHT VERTICAL AND LEFT
|
||||
[0x76] = 0x2534, // BOX DRAWINGS LIGHT UP AND HORIZONTAL
|
||||
[0x77] = 0x252C, // BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
|
||||
[0x78] = 0x2502, // BOX DRAWINGS LIGHT VERTICAL
|
||||
[0x79] = 0x2A7D, // LESS-THAN OR SLANTED EQUAL-TO
|
||||
[0x7a] = 0x2A7E, // GREATER-THAN OR SLANTED EQUAL-TO
|
||||
[0x7b] = 0x03C0, // GREEK SMALL LETTER PI
|
||||
[0x7c] = 0x2260, // NOT EQUAL TO
|
||||
[0x7d] = 0x00A3, // POUND SIGN
|
||||
[0x7e] = 0x00B7, // MIDDLE DOT
|
||||
}
|
||||
};
|
@ -1,6 +0,0 @@
|
||||
static const struct StaticTableEncoding encoding_uk = {
|
||||
{ .decode = &decode_table },
|
||||
{
|
||||
[0x23] = 0x00a3, // £
|
||||
}
|
||||
};
|
@ -1,225 +0,0 @@
|
||||
#include "vterm_internal.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nvim/tui/termkey/termkey.h"
|
||||
|
||||
void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod)
|
||||
{
|
||||
/* The shift modifier is never important for Unicode characters
|
||||
* apart from Space
|
||||
*/
|
||||
if(c != ' ')
|
||||
mod &= ~VTERM_MOD_SHIFT;
|
||||
|
||||
if(mod == 0) {
|
||||
// Normal text - ignore just shift
|
||||
char str[6];
|
||||
int seqlen = fill_utf8(c, str);
|
||||
vterm_push_output_bytes(vt, str, seqlen);
|
||||
return;
|
||||
}
|
||||
|
||||
int needs_CSIu;
|
||||
switch(c) {
|
||||
/* Special Ctrl- letters that can't be represented elsewise */
|
||||
case 'i': case 'j': case 'm': case '[':
|
||||
needs_CSIu = 1;
|
||||
break;
|
||||
/* Ctrl-\ ] ^ _ don't need CSUu */
|
||||
case '\\': case ']': case '^': case '_':
|
||||
needs_CSIu = 0;
|
||||
break;
|
||||
/* Shift-space needs CSIu */
|
||||
case ' ':
|
||||
needs_CSIu = !!(mod & VTERM_MOD_SHIFT);
|
||||
break;
|
||||
/* All other characters needs CSIu except for letters a-z */
|
||||
default:
|
||||
needs_CSIu = (c < 'a' || c > 'z');
|
||||
}
|
||||
|
||||
/* ALT we can just prefix with ESC; anything else requires CSI u */
|
||||
if(needs_CSIu && (mod & ~VTERM_MOD_ALT)) {
|
||||
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", c, mod+1);
|
||||
return;
|
||||
}
|
||||
|
||||
if(mod & VTERM_MOD_CTRL)
|
||||
c &= 0x1f;
|
||||
|
||||
vterm_push_output_sprintf(vt, "%s%c", mod & VTERM_MOD_ALT ? ESC_S : "", c);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
enum {
|
||||
KEYCODE_NONE,
|
||||
KEYCODE_LITERAL,
|
||||
KEYCODE_TAB,
|
||||
KEYCODE_ENTER,
|
||||
KEYCODE_SS3,
|
||||
KEYCODE_CSI,
|
||||
KEYCODE_CSI_CURSOR,
|
||||
KEYCODE_CSINUM,
|
||||
KEYCODE_KEYPAD,
|
||||
} type;
|
||||
char literal;
|
||||
int csinum;
|
||||
} keycodes_s;
|
||||
|
||||
static keycodes_s keycodes[] = {
|
||||
{ KEYCODE_NONE }, // NONE
|
||||
|
||||
{ KEYCODE_ENTER, '\r' }, // ENTER
|
||||
{ KEYCODE_TAB, '\t' }, // TAB
|
||||
{ KEYCODE_LITERAL, '\x7f' }, // BACKSPACE == ASCII DEL
|
||||
{ KEYCODE_LITERAL, '\x1b' }, // ESCAPE
|
||||
|
||||
{ KEYCODE_CSI_CURSOR, 'A' }, // UP
|
||||
{ KEYCODE_CSI_CURSOR, 'B' }, // DOWN
|
||||
{ KEYCODE_CSI_CURSOR, 'D' }, // LEFT
|
||||
{ KEYCODE_CSI_CURSOR, 'C' }, // RIGHT
|
||||
|
||||
{ KEYCODE_CSINUM, '~', 2 }, // INS
|
||||
{ KEYCODE_CSINUM, '~', 3 }, // DEL
|
||||
{ KEYCODE_CSI_CURSOR, 'H' }, // HOME
|
||||
{ KEYCODE_CSI_CURSOR, 'F' }, // END
|
||||
{ KEYCODE_CSINUM, '~', 5 }, // PAGEUP
|
||||
{ KEYCODE_CSINUM, '~', 6 }, // PAGEDOWN
|
||||
};
|
||||
|
||||
static keycodes_s keycodes_fn[] = {
|
||||
{ KEYCODE_NONE }, // F0 - shouldn't happen
|
||||
{ KEYCODE_SS3, 'P' }, // F1
|
||||
{ KEYCODE_SS3, 'Q' }, // F2
|
||||
{ KEYCODE_SS3, 'R' }, // F3
|
||||
{ KEYCODE_SS3, 'S' }, // F4
|
||||
{ KEYCODE_CSINUM, '~', 15 }, // F5
|
||||
{ KEYCODE_CSINUM, '~', 17 }, // F6
|
||||
{ KEYCODE_CSINUM, '~', 18 }, // F7
|
||||
{ KEYCODE_CSINUM, '~', 19 }, // F8
|
||||
{ KEYCODE_CSINUM, '~', 20 }, // F9
|
||||
{ KEYCODE_CSINUM, '~', 21 }, // F10
|
||||
{ KEYCODE_CSINUM, '~', 23 }, // F11
|
||||
{ KEYCODE_CSINUM, '~', 24 }, // F12
|
||||
};
|
||||
|
||||
static keycodes_s keycodes_kp[] = {
|
||||
{ KEYCODE_KEYPAD, '0', 'p' }, // KP_0
|
||||
{ KEYCODE_KEYPAD, '1', 'q' }, // KP_1
|
||||
{ KEYCODE_KEYPAD, '2', 'r' }, // KP_2
|
||||
{ KEYCODE_KEYPAD, '3', 's' }, // KP_3
|
||||
{ KEYCODE_KEYPAD, '4', 't' }, // KP_4
|
||||
{ KEYCODE_KEYPAD, '5', 'u' }, // KP_5
|
||||
{ KEYCODE_KEYPAD, '6', 'v' }, // KP_6
|
||||
{ KEYCODE_KEYPAD, '7', 'w' }, // KP_7
|
||||
{ KEYCODE_KEYPAD, '8', 'x' }, // KP_8
|
||||
{ KEYCODE_KEYPAD, '9', 'y' }, // KP_9
|
||||
{ KEYCODE_KEYPAD, '*', 'j' }, // KP_MULT
|
||||
{ KEYCODE_KEYPAD, '+', 'k' }, // KP_PLUS
|
||||
{ KEYCODE_KEYPAD, ',', 'l' }, // KP_COMMA
|
||||
{ KEYCODE_KEYPAD, '-', 'm' }, // KP_MINUS
|
||||
{ KEYCODE_KEYPAD, '.', 'n' }, // KP_PERIOD
|
||||
{ KEYCODE_KEYPAD, '/', 'o' }, // KP_DIVIDE
|
||||
{ KEYCODE_KEYPAD, '\n', 'M' }, // KP_ENTER
|
||||
{ KEYCODE_KEYPAD, '=', 'X' }, // KP_EQUAL
|
||||
};
|
||||
|
||||
void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod)
|
||||
{
|
||||
if(key == VTERM_KEY_NONE)
|
||||
return;
|
||||
|
||||
keycodes_s k;
|
||||
if(key < VTERM_KEY_FUNCTION_0) {
|
||||
if(key >= sizeof(keycodes)/sizeof(keycodes[0]))
|
||||
return;
|
||||
k = keycodes[key];
|
||||
}
|
||||
else if(key >= VTERM_KEY_FUNCTION_0 && key <= VTERM_KEY_FUNCTION_MAX) {
|
||||
if((key - VTERM_KEY_FUNCTION_0) >= sizeof(keycodes_fn)/sizeof(keycodes_fn[0]))
|
||||
return;
|
||||
k = keycodes_fn[key - VTERM_KEY_FUNCTION_0];
|
||||
}
|
||||
else if(key >= VTERM_KEY_KP_0) {
|
||||
if((key - VTERM_KEY_KP_0) >= sizeof(keycodes_kp)/sizeof(keycodes_kp[0]))
|
||||
return;
|
||||
k = keycodes_kp[key - VTERM_KEY_KP_0];
|
||||
}
|
||||
|
||||
switch(k.type) {
|
||||
case KEYCODE_NONE:
|
||||
break;
|
||||
|
||||
case KEYCODE_TAB:
|
||||
/* Shift-Tab is CSI Z but plain Tab is 0x09 */
|
||||
if(mod == VTERM_MOD_SHIFT)
|
||||
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "Z");
|
||||
else if(mod & VTERM_MOD_SHIFT)
|
||||
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%dZ", mod+1);
|
||||
else
|
||||
goto case_LITERAL;
|
||||
break;
|
||||
|
||||
case KEYCODE_ENTER:
|
||||
/* Enter is CRLF in newline mode, but just LF in linefeed */
|
||||
if(vt->state->mode.newline)
|
||||
vterm_push_output_sprintf(vt, "\r\n");
|
||||
else
|
||||
goto case_LITERAL;
|
||||
break;
|
||||
|
||||
case KEYCODE_LITERAL: case_LITERAL:
|
||||
if(mod & (VTERM_MOD_SHIFT|VTERM_MOD_CTRL))
|
||||
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", k.literal, mod+1);
|
||||
else
|
||||
vterm_push_output_sprintf(vt, mod & VTERM_MOD_ALT ? ESC_S "%c" : "%c", k.literal);
|
||||
break;
|
||||
|
||||
case KEYCODE_SS3: case_SS3:
|
||||
if(mod == 0)
|
||||
vterm_push_output_sprintf_ctrl(vt, C1_SS3, "%c", k.literal);
|
||||
else
|
||||
goto case_CSI;
|
||||
break;
|
||||
|
||||
case KEYCODE_CSI: case_CSI:
|
||||
if(mod == 0)
|
||||
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%c", k.literal);
|
||||
else
|
||||
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%d%c", mod + 1, k.literal);
|
||||
break;
|
||||
|
||||
case KEYCODE_CSINUM:
|
||||
if(mod == 0)
|
||||
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d%c", k.csinum, k.literal);
|
||||
else
|
||||
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%d%c", k.csinum, mod + 1, k.literal);
|
||||
break;
|
||||
|
||||
case KEYCODE_CSI_CURSOR:
|
||||
if(vt->state->mode.cursor)
|
||||
goto case_SS3;
|
||||
else
|
||||
goto case_CSI;
|
||||
|
||||
case KEYCODE_KEYPAD:
|
||||
if(vt->state->mode.keypad) {
|
||||
k.literal = k.csinum;
|
||||
goto case_SS3;
|
||||
}
|
||||
else
|
||||
goto case_LITERAL;
|
||||
}
|
||||
}
|
||||
|
||||
void vterm_keyboard_start_paste(VTerm *vt)
|
||||
{
|
||||
if(vt->state->mode.bracketpaste)
|
||||
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "200~");
|
||||
}
|
||||
|
||||
void vterm_keyboard_end_paste(VTerm *vt)
|
||||
{
|
||||
if(vt->state->mode.bracketpaste)
|
||||
vterm_push_output_sprintf_ctrl(vt, C1_CSI, "201~");
|
||||
}
|
@ -1,99 +0,0 @@
|
||||
#include "vterm_internal.h"
|
||||
|
||||
#include "nvim/tui/termkey/termkey.h"
|
||||
|
||||
static void output_mouse(VTermState *state, int code, int pressed, int modifiers, int col, int row)
|
||||
{
|
||||
modifiers <<= 2;
|
||||
|
||||
switch(state->mouse_protocol) {
|
||||
case MOUSE_X10:
|
||||
if(col + 0x21 > 0xff)
|
||||
col = 0xff - 0x21;
|
||||
if(row + 0x21 > 0xff)
|
||||
row = 0xff - 0x21;
|
||||
|
||||
if(!pressed)
|
||||
code = 3;
|
||||
|
||||
vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "M%c%c%c",
|
||||
(code | modifiers) + 0x20, col + 0x21, row + 0x21);
|
||||
break;
|
||||
|
||||
case MOUSE_UTF8:
|
||||
{
|
||||
char utf8[18]; size_t len = 0;
|
||||
|
||||
if(!pressed)
|
||||
code = 3;
|
||||
|
||||
len += fill_utf8((code | modifiers) + 0x20, utf8 + len);
|
||||
len += fill_utf8(col + 0x21, utf8 + len);
|
||||
len += fill_utf8(row + 0x21, utf8 + len);
|
||||
utf8[len] = 0;
|
||||
|
||||
vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "M%s", utf8);
|
||||
}
|
||||
break;
|
||||
|
||||
case MOUSE_SGR:
|
||||
vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "<%d;%d;%d%c",
|
||||
code | modifiers, col + 1, row + 1, pressed ? 'M' : 'm');
|
||||
break;
|
||||
|
||||
case MOUSE_RXVT:
|
||||
if(!pressed)
|
||||
code = 3;
|
||||
|
||||
vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%d;%d;%dM",
|
||||
code | modifiers, col + 1, row + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void vterm_mouse_move(VTerm *vt, int row, int col, VTermModifier mod)
|
||||
{
|
||||
VTermState *state = vt->state;
|
||||
|
||||
if(col == state->mouse_col && row == state->mouse_row)
|
||||
return;
|
||||
|
||||
state->mouse_col = col;
|
||||
state->mouse_row = row;
|
||||
|
||||
if((state->mouse_flags & MOUSE_WANT_DRAG && state->mouse_buttons) ||
|
||||
(state->mouse_flags & MOUSE_WANT_MOVE)) {
|
||||
int button = state->mouse_buttons & 0x01 ? 1 :
|
||||
state->mouse_buttons & 0x02 ? 2 :
|
||||
state->mouse_buttons & 0x04 ? 3 : 4;
|
||||
output_mouse(state, button-1 + 0x20, 1, mod, col, row);
|
||||
}
|
||||
}
|
||||
|
||||
void vterm_mouse_button(VTerm *vt, int button, bool pressed, VTermModifier mod)
|
||||
{
|
||||
VTermState *state = vt->state;
|
||||
|
||||
int old_buttons = state->mouse_buttons;
|
||||
|
||||
if(button > 0 && button <= 3) {
|
||||
if(pressed)
|
||||
state->mouse_buttons |= (1 << (button-1));
|
||||
else
|
||||
state->mouse_buttons &= ~(1 << (button-1));
|
||||
}
|
||||
|
||||
/* Most of the time we don't get button releases from 4/5 */
|
||||
if(state->mouse_buttons == old_buttons && button < 4)
|
||||
return;
|
||||
|
||||
if(!state->mouse_flags)
|
||||
return;
|
||||
|
||||
if(button < 4) {
|
||||
output_mouse(state, button-1, pressed, mod, state->mouse_col, state->mouse_row);
|
||||
}
|
||||
else if(button < 8) {
|
||||
output_mouse(state, button-4 + 0x40, pressed, mod, state->mouse_col, state->mouse_row);
|
||||
}
|
||||
}
|
@ -1,408 +0,0 @@
|
||||
#include "vterm_internal.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#undef DEBUG_PARSER
|
||||
|
||||
static bool is_intermed(unsigned char c)
|
||||
{
|
||||
return c >= 0x20 && c <= 0x2f;
|
||||
}
|
||||
|
||||
static void do_control(VTerm *vt, unsigned char control)
|
||||
{
|
||||
if(vt->parser.callbacks && vt->parser.callbacks->control)
|
||||
if((*vt->parser.callbacks->control)(control, vt->parser.cbdata))
|
||||
return;
|
||||
|
||||
DEBUG_LOG("libvterm: Unhandled control 0x%02x\n", control);
|
||||
}
|
||||
|
||||
static void do_csi(VTerm *vt, char command)
|
||||
{
|
||||
#ifdef DEBUG_PARSER
|
||||
printf("Parsed CSI args as:\n", arglen, args);
|
||||
printf(" leader: %s\n", vt->parser.v.csi.leader);
|
||||
for(int argi = 0; argi < vt->parser.v.csi.argi; argi++) {
|
||||
printf(" %lu", CSI_ARG(vt->parser.v.csi.args[argi]));
|
||||
if(!CSI_ARG_HAS_MORE(vt->parser.v.csi.args[argi]))
|
||||
printf("\n");
|
||||
printf(" intermed: %s\n", vt->parser.intermed);
|
||||
}
|
||||
#endif
|
||||
|
||||
if(vt->parser.callbacks && vt->parser.callbacks->csi)
|
||||
if((*vt->parser.callbacks->csi)(
|
||||
vt->parser.v.csi.leaderlen ? vt->parser.v.csi.leader : NULL,
|
||||
vt->parser.v.csi.args,
|
||||
vt->parser.v.csi.argi,
|
||||
vt->parser.intermedlen ? vt->parser.intermed : NULL,
|
||||
command,
|
||||
vt->parser.cbdata))
|
||||
return;
|
||||
|
||||
DEBUG_LOG("libvterm: Unhandled CSI %c\n", command);
|
||||
}
|
||||
|
||||
static void do_escape(VTerm *vt, char command)
|
||||
{
|
||||
char seq[INTERMED_MAX+1];
|
||||
|
||||
size_t len = vt->parser.intermedlen;
|
||||
strncpy(seq, vt->parser.intermed, len);
|
||||
seq[len++] = command;
|
||||
seq[len] = 0;
|
||||
|
||||
if(vt->parser.callbacks && vt->parser.callbacks->escape)
|
||||
if((*vt->parser.callbacks->escape)(seq, len, vt->parser.cbdata))
|
||||
return;
|
||||
|
||||
DEBUG_LOG("libvterm: Unhandled escape ESC 0x%02x\n", command);
|
||||
}
|
||||
|
||||
static void string_fragment(VTerm *vt, const char *str, size_t len, bool final)
|
||||
{
|
||||
VTermStringFragment frag = {
|
||||
.str = str,
|
||||
.len = len,
|
||||
.initial = vt->parser.string_initial,
|
||||
.final = final,
|
||||
};
|
||||
|
||||
switch(vt->parser.state) {
|
||||
case OSC:
|
||||
if(vt->parser.callbacks && vt->parser.callbacks->osc)
|
||||
(*vt->parser.callbacks->osc)(vt->parser.v.osc.command, frag, vt->parser.cbdata);
|
||||
break;
|
||||
|
||||
case DCS:
|
||||
if(vt->parser.callbacks && vt->parser.callbacks->dcs)
|
||||
(*vt->parser.callbacks->dcs)(vt->parser.v.dcs.command, vt->parser.v.dcs.commandlen, frag, vt->parser.cbdata);
|
||||
break;
|
||||
|
||||
case APC:
|
||||
if(vt->parser.callbacks && vt->parser.callbacks->apc)
|
||||
(*vt->parser.callbacks->apc)(frag, vt->parser.cbdata);
|
||||
break;
|
||||
|
||||
case PM:
|
||||
if(vt->parser.callbacks && vt->parser.callbacks->pm)
|
||||
(*vt->parser.callbacks->pm)(frag, vt->parser.cbdata);
|
||||
break;
|
||||
|
||||
case SOS:
|
||||
if(vt->parser.callbacks && vt->parser.callbacks->sos)
|
||||
(*vt->parser.callbacks->sos)(frag, vt->parser.cbdata);
|
||||
break;
|
||||
|
||||
case NORMAL:
|
||||
case CSI_LEADER:
|
||||
case CSI_ARGS:
|
||||
case CSI_INTERMED:
|
||||
case OSC_COMMAND:
|
||||
case DCS_COMMAND:
|
||||
break;
|
||||
}
|
||||
|
||||
vt->parser.string_initial = false;
|
||||
}
|
||||
|
||||
size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len)
|
||||
{
|
||||
size_t pos = 0;
|
||||
const char *string_start;
|
||||
|
||||
switch(vt->parser.state) {
|
||||
case NORMAL:
|
||||
case CSI_LEADER:
|
||||
case CSI_ARGS:
|
||||
case CSI_INTERMED:
|
||||
case OSC_COMMAND:
|
||||
case DCS_COMMAND:
|
||||
string_start = NULL;
|
||||
break;
|
||||
case OSC:
|
||||
case DCS:
|
||||
case APC:
|
||||
case PM:
|
||||
case SOS:
|
||||
string_start = bytes;
|
||||
break;
|
||||
}
|
||||
|
||||
#define ENTER_STATE(st) do { vt->parser.state = st; string_start = NULL; } while(0)
|
||||
#define ENTER_NORMAL_STATE() ENTER_STATE(NORMAL)
|
||||
|
||||
#define IS_STRING_STATE() (vt->parser.state >= OSC_COMMAND)
|
||||
|
||||
for( ; pos < len; pos++) {
|
||||
unsigned char c = bytes[pos];
|
||||
bool c1_allowed = !vt->mode.utf8;
|
||||
|
||||
if(c == 0x00 || c == 0x7f) { // NUL, DEL
|
||||
if(IS_STRING_STATE()) {
|
||||
string_fragment(vt, string_start, bytes + pos - string_start, false);
|
||||
string_start = bytes + pos + 1;
|
||||
}
|
||||
if(vt->parser.emit_nul)
|
||||
do_control(vt, c);
|
||||
continue;
|
||||
}
|
||||
if(c == 0x18 || c == 0x1a) { // CAN, SUB
|
||||
vt->parser.in_esc = false;
|
||||
ENTER_NORMAL_STATE();
|
||||
if(vt->parser.emit_nul)
|
||||
do_control(vt, c);
|
||||
continue;
|
||||
}
|
||||
else if(c == 0x1b) { // ESC
|
||||
vt->parser.intermedlen = 0;
|
||||
if(!IS_STRING_STATE())
|
||||
vt->parser.state = NORMAL;
|
||||
vt->parser.in_esc = true;
|
||||
continue;
|
||||
}
|
||||
else if(c == 0x07 && // BEL, can stand for ST in OSC or DCS state
|
||||
IS_STRING_STATE()) {
|
||||
// fallthrough
|
||||
}
|
||||
else if(c < 0x20) { // other C0
|
||||
if(vt->parser.state == SOS)
|
||||
continue; // All other C0s permitted in SOS
|
||||
|
||||
if(IS_STRING_STATE())
|
||||
string_fragment(vt, string_start, bytes + pos - string_start, false);
|
||||
do_control(vt, c);
|
||||
if(IS_STRING_STATE())
|
||||
string_start = bytes + pos + 1;
|
||||
continue;
|
||||
}
|
||||
// else fallthrough
|
||||
|
||||
size_t string_len = bytes + pos - string_start;
|
||||
|
||||
if(vt->parser.in_esc) {
|
||||
// Hoist an ESC letter into a C1 if we're not in a string mode
|
||||
// Always accept ESC \ == ST even in string mode
|
||||
if(!vt->parser.intermedlen &&
|
||||
c >= 0x40 && c < 0x60 &&
|
||||
((!IS_STRING_STATE() || c == 0x5c))) {
|
||||
c += 0x40;
|
||||
c1_allowed = true;
|
||||
if(string_len) {
|
||||
assert(string_len > 0);
|
||||
string_len -= 1;
|
||||
}
|
||||
vt->parser.in_esc = false;
|
||||
}
|
||||
else {
|
||||
string_start = NULL;
|
||||
vt->parser.state = NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
switch(vt->parser.state) {
|
||||
case CSI_LEADER:
|
||||
/* Extract leader bytes 0x3c to 0x3f */
|
||||
if(c >= 0x3c && c <= 0x3f) {
|
||||
if(vt->parser.v.csi.leaderlen < CSI_LEADER_MAX-1)
|
||||
vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen++] = c;
|
||||
break;
|
||||
}
|
||||
|
||||
/* else fallthrough */
|
||||
vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen] = 0;
|
||||
|
||||
vt->parser.v.csi.argi = 0;
|
||||
vt->parser.v.csi.args[0] = CSI_ARG_MISSING;
|
||||
vt->parser.state = CSI_ARGS;
|
||||
|
||||
/* fallthrough */
|
||||
case CSI_ARGS:
|
||||
/* Numerical value of argument */
|
||||
if(c >= '0' && c <= '9') {
|
||||
if(vt->parser.v.csi.args[vt->parser.v.csi.argi] == CSI_ARG_MISSING)
|
||||
vt->parser.v.csi.args[vt->parser.v.csi.argi] = 0;
|
||||
vt->parser.v.csi.args[vt->parser.v.csi.argi] *= 10;
|
||||
vt->parser.v.csi.args[vt->parser.v.csi.argi] += c - '0';
|
||||
break;
|
||||
}
|
||||
if(c == ':') {
|
||||
vt->parser.v.csi.args[vt->parser.v.csi.argi] |= CSI_ARG_FLAG_MORE;
|
||||
c = ';';
|
||||
}
|
||||
if(c == ';') {
|
||||
vt->parser.v.csi.argi++;
|
||||
vt->parser.v.csi.args[vt->parser.v.csi.argi] = CSI_ARG_MISSING;
|
||||
break;
|
||||
}
|
||||
|
||||
/* else fallthrough */
|
||||
vt->parser.v.csi.argi++;
|
||||
vt->parser.intermedlen = 0;
|
||||
vt->parser.state = CSI_INTERMED;
|
||||
case CSI_INTERMED:
|
||||
if(is_intermed(c)) {
|
||||
if(vt->parser.intermedlen < INTERMED_MAX-1)
|
||||
vt->parser.intermed[vt->parser.intermedlen++] = c;
|
||||
break;
|
||||
}
|
||||
else if(c == 0x1b) {
|
||||
/* ESC in CSI cancels */
|
||||
}
|
||||
else if(c >= 0x40 && c <= 0x7e) {
|
||||
vt->parser.intermed[vt->parser.intermedlen] = 0;
|
||||
do_csi(vt, c);
|
||||
}
|
||||
/* else was invalid CSI */
|
||||
|
||||
ENTER_NORMAL_STATE();
|
||||
break;
|
||||
|
||||
case OSC_COMMAND:
|
||||
/* Numerical value of command */
|
||||
if(c >= '0' && c <= '9') {
|
||||
if(vt->parser.v.osc.command == -1)
|
||||
vt->parser.v.osc.command = 0;
|
||||
else
|
||||
vt->parser.v.osc.command *= 10;
|
||||
vt->parser.v.osc.command += c - '0';
|
||||
break;
|
||||
}
|
||||
if(c == ';') {
|
||||
vt->parser.state = OSC;
|
||||
string_start = bytes + pos + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* else fallthrough */
|
||||
string_start = bytes + pos;
|
||||
string_len = 0;
|
||||
vt->parser.state = OSC;
|
||||
goto string_state;
|
||||
|
||||
case DCS_COMMAND:
|
||||
if(vt->parser.v.dcs.commandlen < CSI_LEADER_MAX)
|
||||
vt->parser.v.dcs.command[vt->parser.v.dcs.commandlen++] = c;
|
||||
|
||||
if(c >= 0x40 && c<= 0x7e) {
|
||||
string_start = bytes + pos + 1;
|
||||
vt->parser.state = DCS;
|
||||
}
|
||||
break;
|
||||
|
||||
string_state:
|
||||
case OSC:
|
||||
case DCS:
|
||||
case APC:
|
||||
case PM:
|
||||
case SOS:
|
||||
if(c == 0x07 || (c1_allowed && c == 0x9c)) {
|
||||
string_fragment(vt, string_start, string_len, true);
|
||||
ENTER_NORMAL_STATE();
|
||||
}
|
||||
break;
|
||||
|
||||
case NORMAL:
|
||||
if(vt->parser.in_esc) {
|
||||
if(is_intermed(c)) {
|
||||
if(vt->parser.intermedlen < INTERMED_MAX-1)
|
||||
vt->parser.intermed[vt->parser.intermedlen++] = c;
|
||||
}
|
||||
else if(c >= 0x30 && c < 0x7f) {
|
||||
do_escape(vt, c);
|
||||
vt->parser.in_esc = 0;
|
||||
ENTER_NORMAL_STATE();
|
||||
}
|
||||
else {
|
||||
DEBUG_LOG("TODO: Unhandled byte %02x in Escape\n", c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(c1_allowed && c >= 0x80 && c < 0xa0) {
|
||||
switch(c) {
|
||||
case 0x90: // DCS
|
||||
vt->parser.string_initial = true;
|
||||
vt->parser.v.dcs.commandlen = 0;
|
||||
ENTER_STATE(DCS_COMMAND);
|
||||
break;
|
||||
case 0x98: // SOS
|
||||
vt->parser.string_initial = true;
|
||||
ENTER_STATE(SOS);
|
||||
string_start = bytes + pos + 1;
|
||||
string_len = 0;
|
||||
break;
|
||||
case 0x9b: // CSI
|
||||
vt->parser.v.csi.leaderlen = 0;
|
||||
ENTER_STATE(CSI_LEADER);
|
||||
break;
|
||||
case 0x9d: // OSC
|
||||
vt->parser.v.osc.command = -1;
|
||||
vt->parser.string_initial = true;
|
||||
string_start = bytes + pos + 1;
|
||||
ENTER_STATE(OSC_COMMAND);
|
||||
break;
|
||||
case 0x9e: // PM
|
||||
vt->parser.string_initial = true;
|
||||
ENTER_STATE(PM);
|
||||
string_start = bytes + pos + 1;
|
||||
string_len = 0;
|
||||
break;
|
||||
case 0x9f: // APC
|
||||
vt->parser.string_initial = true;
|
||||
ENTER_STATE(APC);
|
||||
string_start = bytes + pos + 1;
|
||||
string_len = 0;
|
||||
break;
|
||||
default:
|
||||
do_control(vt, c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
size_t eaten = 0;
|
||||
if(vt->parser.callbacks && vt->parser.callbacks->text)
|
||||
eaten = (*vt->parser.callbacks->text)(bytes + pos, len - pos, vt->parser.cbdata);
|
||||
|
||||
if(!eaten) {
|
||||
DEBUG_LOG("libvterm: Text callback did not consume any input\n");
|
||||
/* force it to make progress */
|
||||
eaten = 1;
|
||||
}
|
||||
|
||||
pos += (eaten - 1); // we'll ++ it again in a moment
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(string_start) {
|
||||
size_t string_len = bytes + pos - string_start;
|
||||
if (string_len > 0) {
|
||||
if(vt->parser.in_esc) {
|
||||
string_len -= 1;
|
||||
}
|
||||
string_fragment(vt, string_start, string_len, false);
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user)
|
||||
{
|
||||
vt->parser.callbacks = callbacks;
|
||||
vt->parser.cbdata = user;
|
||||
}
|
||||
|
||||
void *vterm_parser_get_cbdata(VTerm *vt)
|
||||
{
|
||||
return vt->parser.cbdata;
|
||||
}
|
||||
|
||||
void vterm_parser_set_emit_nul(VTerm *vt, bool emit)
|
||||
{
|
||||
vt->parser.emit_nul = emit;
|
||||
}
|
678
src/vterm/pen.c
678
src/vterm/pen.c
@ -1,678 +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.conceal = 0; setpenattr_bool(state, VTERM_ATTR_CONCEAL, 0);
|
||||
state->pen.strike = 0; setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
|
||||
state->pen.font = 0; setpenattr_int (state, VTERM_ATTR_FONT, 0);
|
||||
state->pen.small = 0; setpenattr_bool(state, VTERM_ATTR_SMALL, 0);
|
||||
state->pen.baseline = 0; setpenattr_int (state, VTERM_ATTR_BASELINE, 0);
|
||||
|
||||
state->pen.fg = state->default_fg; setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->default_fg);
|
||||
state->pen.bg = state->default_bg; setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->default_bg);
|
||||
|
||||
state->pen.uri = 0; setpenattr_int(state, VTERM_ATTR_URI, 0);
|
||||
}
|
||||
|
||||
INTERNAL void vterm_state_savepen(VTermState *state, int save)
|
||||
{
|
||||
if(save) {
|
||||
state->saved.pen = state->pen;
|
||||
}
|
||||
else {
|
||||
state->pen = state->saved.pen;
|
||||
|
||||
setpenattr_bool(state, VTERM_ATTR_BOLD, state->pen.bold);
|
||||
setpenattr_int (state, VTERM_ATTR_UNDERLINE, state->pen.underline);
|
||||
setpenattr_bool(state, VTERM_ATTR_ITALIC, state->pen.italic);
|
||||
setpenattr_bool(state, VTERM_ATTR_BLINK, state->pen.blink);
|
||||
setpenattr_bool(state, VTERM_ATTR_REVERSE, state->pen.reverse);
|
||||
setpenattr_bool(state, VTERM_ATTR_CONCEAL, state->pen.conceal);
|
||||
setpenattr_bool(state, VTERM_ATTR_STRIKE, state->pen.strike);
|
||||
setpenattr_int (state, VTERM_ATTR_FONT, state->pen.font);
|
||||
setpenattr_bool(state, VTERM_ATTR_SMALL, state->pen.small);
|
||||
setpenattr_int (state, VTERM_ATTR_BASELINE, state->pen.baseline);
|
||||
|
||||
setpenattr_col( state, VTERM_ATTR_FOREGROUND, state->pen.fg);
|
||||
setpenattr_col( state, VTERM_ATTR_BACKGROUND, state->pen.bg);
|
||||
|
||||
setpenattr_int( state, VTERM_ATTR_URI, state->pen.uri);
|
||||
}
|
||||
}
|
||||
|
||||
int vterm_color_is_equal(const VTermColor *a, const VTermColor *b)
|
||||
{
|
||||
/* First make sure that the two colours are of the same type (RGB/Indexed) */
|
||||
if (a->type != b->type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Depending on the type inspect the corresponding members */
|
||||
if (VTERM_COLOR_IS_INDEXED(a)) {
|
||||
return a->indexed.idx == b->indexed.idx;
|
||||
}
|
||||
else if (VTERM_COLOR_IS_RGB(a)) {
|
||||
return (a->rgb.red == b->rgb.red)
|
||||
&& (a->rgb.green == b->rgb.green)
|
||||
&& (a->rgb.blue == b->rgb.blue);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vterm_state_get_default_colors(const VTermState *state, VTermColor *default_fg, VTermColor *default_bg)
|
||||
{
|
||||
*default_fg = state->default_fg;
|
||||
*default_bg = state->default_bg;
|
||||
}
|
||||
|
||||
void vterm_state_get_palette_color(const VTermState *state, int index, VTermColor *col)
|
||||
{
|
||||
lookup_colour_palette(state, index, col);
|
||||
}
|
||||
|
||||
void vterm_state_set_default_colors(VTermState *state, const VTermColor *default_fg, const VTermColor *default_bg)
|
||||
{
|
||||
if(default_fg) {
|
||||
state->default_fg = *default_fg;
|
||||
state->default_fg.type = (state->default_fg.type & ~VTERM_COLOR_DEFAULT_MASK)
|
||||
| VTERM_COLOR_DEFAULT_FG;
|
||||
}
|
||||
|
||||
if(default_bg) {
|
||||
state->default_bg = *default_bg;
|
||||
state->default_bg.type = (state->default_bg.type & ~VTERM_COLOR_DEFAULT_MASK)
|
||||
| VTERM_COLOR_DEFAULT_BG;
|
||||
}
|
||||
}
|
||||
|
||||
void vterm_state_set_palette_color(VTermState *state, int index, const VTermColor *col)
|
||||
{
|
||||
if(index >= 0 && index < 16)
|
||||
state->colors[index] = *col;
|
||||
}
|
||||
|
||||
void vterm_state_convert_color_to_rgb(const VTermState *state, VTermColor *col)
|
||||
{
|
||||
if (VTERM_COLOR_IS_INDEXED(col)) { /* Convert indexed colors to RGB */
|
||||
lookup_colour_palette(state, col->indexed.idx, col);
|
||||
}
|
||||
col->type &= VTERM_COLOR_TYPE_MASK; /* Reset any metadata but the type */
|
||||
}
|
||||
|
||||
void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright)
|
||||
{
|
||||
state->bold_is_highbright = bold_is_highbright;
|
||||
}
|
||||
|
||||
INTERNAL void vterm_state_setpen(VTermState *state, const long args[], int argcount)
|
||||
{
|
||||
// SGR - ECMA-48 8.3.117
|
||||
|
||||
int argi = 0;
|
||||
int value;
|
||||
|
||||
while(argi < argcount) {
|
||||
// This logic is easier to do 'done' backwards; set it true, and make it
|
||||
// false again in the 'default' case
|
||||
int done = 1;
|
||||
|
||||
long arg;
|
||||
switch(arg = CSI_ARG(args[argi])) {
|
||||
case CSI_ARG_MISSING:
|
||||
case 0: // Reset
|
||||
vterm_state_resetpen(state);
|
||||
break;
|
||||
|
||||
case 1: { // Bold on
|
||||
const VTermColor *fg = &state->pen.fg;
|
||||
state->pen.bold = 1;
|
||||
setpenattr_bool(state, VTERM_ATTR_BOLD, 1);
|
||||
if(!VTERM_COLOR_IS_DEFAULT_FG(fg) && VTERM_COLOR_IS_INDEXED(fg) && fg->indexed.idx < 8 && state->bold_is_highbright)
|
||||
set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, fg->indexed.idx + (state->pen.bold ? 8 : 0));
|
||||
break;
|
||||
}
|
||||
|
||||
case 3: // Italic on
|
||||
state->pen.italic = 1;
|
||||
setpenattr_bool(state, VTERM_ATTR_ITALIC, 1);
|
||||
break;
|
||||
|
||||
case 4: // Underline
|
||||
state->pen.underline = VTERM_UNDERLINE_SINGLE;
|
||||
if(CSI_ARG_HAS_MORE(args[argi])) {
|
||||
argi++;
|
||||
switch(CSI_ARG(args[argi])) {
|
||||
case 0:
|
||||
state->pen.underline = 0;
|
||||
break;
|
||||
case 1:
|
||||
state->pen.underline = VTERM_UNDERLINE_SINGLE;
|
||||
break;
|
||||
case 2:
|
||||
state->pen.underline = VTERM_UNDERLINE_DOUBLE;
|
||||
break;
|
||||
case 3:
|
||||
state->pen.underline = VTERM_UNDERLINE_CURLY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
|
||||
break;
|
||||
|
||||
case 5: // Blink
|
||||
state->pen.blink = 1;
|
||||
setpenattr_bool(state, VTERM_ATTR_BLINK, 1);
|
||||
break;
|
||||
|
||||
case 7: // Reverse on
|
||||
state->pen.reverse = 1;
|
||||
setpenattr_bool(state, VTERM_ATTR_REVERSE, 1);
|
||||
break;
|
||||
|
||||
case 8: // Conceal on
|
||||
state->pen.conceal = 1;
|
||||
setpenattr_bool(state, VTERM_ATTR_CONCEAL, 1);
|
||||
break;
|
||||
|
||||
case 9: // Strikethrough on
|
||||
state->pen.strike = 1;
|
||||
setpenattr_bool(state, VTERM_ATTR_STRIKE, 1);
|
||||
break;
|
||||
|
||||
case 10: case 11: case 12: case 13: case 14:
|
||||
case 15: case 16: case 17: case 18: case 19: // Select font
|
||||
state->pen.font = CSI_ARG(args[argi]) - 10;
|
||||
setpenattr_int(state, VTERM_ATTR_FONT, state->pen.font);
|
||||
break;
|
||||
|
||||
case 21: // Underline double
|
||||
state->pen.underline = VTERM_UNDERLINE_DOUBLE;
|
||||
setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
|
||||
break;
|
||||
|
||||
case 22: // Bold off
|
||||
state->pen.bold = 0;
|
||||
setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
|
||||
break;
|
||||
|
||||
case 23: // Italic and Gothic (currently unsupported) off
|
||||
state->pen.italic = 0;
|
||||
setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
|
||||
break;
|
||||
|
||||
case 24: // Underline off
|
||||
state->pen.underline = 0;
|
||||
setpenattr_int(state, VTERM_ATTR_UNDERLINE, 0);
|
||||
break;
|
||||
|
||||
case 25: // Blink off
|
||||
state->pen.blink = 0;
|
||||
setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
|
||||
break;
|
||||
|
||||
case 27: // Reverse off
|
||||
state->pen.reverse = 0;
|
||||
setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
|
||||
break;
|
||||
|
||||
case 28: // Conceal off (Reveal)
|
||||
state->pen.conceal = 0;
|
||||
setpenattr_bool(state, VTERM_ATTR_CONCEAL, 0);
|
||||
break;
|
||||
|
||||
case 29: // Strikethrough off
|
||||
state->pen.strike = 0;
|
||||
setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
|
||||
break;
|
||||
|
||||
case 30: case 31: case 32: case 33:
|
||||
case 34: case 35: case 36: case 37: // Foreground colour palette
|
||||
value = CSI_ARG(args[argi]) - 30;
|
||||
if(state->pen.bold && state->bold_is_highbright)
|
||||
value += 8;
|
||||
set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
|
||||
break;
|
||||
|
||||
case 38: // Foreground colour alternative palette
|
||||
if(argcount - argi < 1)
|
||||
return;
|
||||
argi += 1 + lookup_colour(state, CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.fg);
|
||||
setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
|
||||
break;
|
||||
|
||||
case 39: // Foreground colour default
|
||||
state->pen.fg = state->default_fg;
|
||||
setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
|
||||
break;
|
||||
|
||||
case 40: case 41: case 42: case 43:
|
||||
case 44: case 45: case 46: case 47: // Background colour palette
|
||||
value = CSI_ARG(args[argi]) - 40;
|
||||
set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
|
||||
break;
|
||||
|
||||
case 48: // Background colour alternative palette
|
||||
if(argcount - argi < 1)
|
||||
return;
|
||||
argi += 1 + lookup_colour(state, CSI_ARG(args[argi+1]), args+argi+2, argcount-argi-2, &state->pen.bg);
|
||||
setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
|
||||
break;
|
||||
|
||||
case 49: // Default background
|
||||
state->pen.bg = state->default_bg;
|
||||
setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
|
||||
break;
|
||||
|
||||
case 73: // Superscript
|
||||
case 74: // Subscript
|
||||
case 75: // Superscript/subscript off
|
||||
state->pen.small = (arg != 75);
|
||||
state->pen.baseline =
|
||||
(arg == 73) ? VTERM_BASELINE_RAISE :
|
||||
(arg == 74) ? VTERM_BASELINE_LOWER :
|
||||
VTERM_BASELINE_NORMAL;
|
||||
setpenattr_bool(state, VTERM_ATTR_SMALL, state->pen.small);
|
||||
setpenattr_int (state, VTERM_ATTR_BASELINE, state->pen.baseline);
|
||||
break;
|
||||
|
||||
case 90: case 91: case 92: case 93:
|
||||
case 94: case 95: case 96: case 97: // Foreground colour high-intensity palette
|
||||
value = CSI_ARG(args[argi]) - 90 + 8;
|
||||
set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
|
||||
break;
|
||||
|
||||
case 100: case 101: case 102: case 103:
|
||||
case 104: case 105: case 106: case 107: // Background colour high-intensity palette
|
||||
value = CSI_ARG(args[argi]) - 100 + 8;
|
||||
set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
|
||||
break;
|
||||
|
||||
default:
|
||||
done = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!done) {
|
||||
DEBUG_LOG("libvterm: Unhandled CSI SGR %ld\n", arg);
|
||||
}
|
||||
|
||||
while(CSI_ARG_HAS_MORE(args[argi++]));
|
||||
}
|
||||
}
|
||||
|
||||
static int vterm_state_getpen_color(const VTermColor *col, int argi, long args[], int fg)
|
||||
{
|
||||
/* Do nothing if the given color is the default color */
|
||||
if (( fg && VTERM_COLOR_IS_DEFAULT_FG(col)) ||
|
||||
(!fg && VTERM_COLOR_IS_DEFAULT_BG(col))) {
|
||||
return argi;
|
||||
}
|
||||
|
||||
/* Decide whether to send an indexed color or an RGB color */
|
||||
if (VTERM_COLOR_IS_INDEXED(col)) {
|
||||
const uint8_t idx = col->indexed.idx;
|
||||
if (idx < 8) {
|
||||
args[argi++] = (idx + (fg ? 30 : 40));
|
||||
}
|
||||
else if (idx < 16) {
|
||||
args[argi++] = (idx - 8 + (fg ? 90 : 100));
|
||||
}
|
||||
else {
|
||||
args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
|
||||
args[argi++] = CSI_ARG_FLAG_MORE | 5;
|
||||
args[argi++] = idx;
|
||||
}
|
||||
}
|
||||
else if (VTERM_COLOR_IS_RGB(col)) {
|
||||
args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
|
||||
args[argi++] = CSI_ARG_FLAG_MORE | 2;
|
||||
args[argi++] = CSI_ARG_FLAG_MORE | col->rgb.red;
|
||||
args[argi++] = CSI_ARG_FLAG_MORE | col->rgb.green;
|
||||
args[argi++] = col->rgb.blue;
|
||||
}
|
||||
return argi;
|
||||
}
|
||||
|
||||
INTERNAL int vterm_state_getpen(VTermState *state, long args[], int argcount)
|
||||
{
|
||||
int argi = 0;
|
||||
|
||||
if(state->pen.bold)
|
||||
args[argi++] = 1;
|
||||
|
||||
if(state->pen.italic)
|
||||
args[argi++] = 3;
|
||||
|
||||
if(state->pen.underline == VTERM_UNDERLINE_SINGLE)
|
||||
args[argi++] = 4;
|
||||
if(state->pen.underline == VTERM_UNDERLINE_CURLY)
|
||||
args[argi++] = 4 | CSI_ARG_FLAG_MORE, args[argi++] = 3;
|
||||
|
||||
if(state->pen.blink)
|
||||
args[argi++] = 5;
|
||||
|
||||
if(state->pen.reverse)
|
||||
args[argi++] = 7;
|
||||
|
||||
if(state->pen.conceal)
|
||||
args[argi++] = 8;
|
||||
|
||||
if(state->pen.strike)
|
||||
args[argi++] = 9;
|
||||
|
||||
if(state->pen.font)
|
||||
args[argi++] = 10 + state->pen.font;
|
||||
|
||||
if(state->pen.underline == VTERM_UNDERLINE_DOUBLE)
|
||||
args[argi++] = 21;
|
||||
|
||||
argi = vterm_state_getpen_color(&state->pen.fg, argi, args, true);
|
||||
|
||||
argi = vterm_state_getpen_color(&state->pen.bg, argi, args, false);
|
||||
|
||||
if(state->pen.small) {
|
||||
if(state->pen.baseline == VTERM_BASELINE_RAISE)
|
||||
args[argi++] = 73;
|
||||
else if(state->pen.baseline == VTERM_BASELINE_LOWER)
|
||||
args[argi++] = 74;
|
||||
}
|
||||
|
||||
return argi;
|
||||
}
|
||||
|
||||
int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val)
|
||||
{
|
||||
switch(attr) {
|
||||
case VTERM_ATTR_BOLD:
|
||||
val->boolean = state->pen.bold;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_UNDERLINE:
|
||||
val->number = state->pen.underline;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_ITALIC:
|
||||
val->boolean = state->pen.italic;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_BLINK:
|
||||
val->boolean = state->pen.blink;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_REVERSE:
|
||||
val->boolean = state->pen.reverse;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_CONCEAL:
|
||||
val->boolean = state->pen.conceal;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_STRIKE:
|
||||
val->boolean = state->pen.strike;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_FONT:
|
||||
val->number = state->pen.font;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_FOREGROUND:
|
||||
val->color = state->pen.fg;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_BACKGROUND:
|
||||
val->color = state->pen.bg;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_SMALL:
|
||||
val->boolean = state->pen.small;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_BASELINE:
|
||||
val->number = state->pen.baseline;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_URI:
|
||||
val->number = state->pen.uri;
|
||||
return 1;
|
||||
|
||||
case VTERM_N_ATTRS:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vterm_state_set_penattr(VTermState *state, VTermAttr attr, VTermValueType type, VTermValue *val)
|
||||
{
|
||||
if (!val) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 0;
|
||||
}
|
||||
|
||||
switch (attr) {
|
||||
case VTERM_ATTR_BOLD:
|
||||
state->pen.bold = val->boolean;
|
||||
break;
|
||||
case VTERM_ATTR_UNDERLINE:
|
||||
state->pen.underline = val->number;
|
||||
break;
|
||||
case VTERM_ATTR_ITALIC:
|
||||
state->pen.italic = val->boolean;
|
||||
break;
|
||||
case VTERM_ATTR_BLINK:
|
||||
state->pen.blink = val->boolean;
|
||||
break;
|
||||
case VTERM_ATTR_REVERSE:
|
||||
state->pen.reverse = val->boolean;
|
||||
break;
|
||||
case VTERM_ATTR_CONCEAL:
|
||||
state->pen.conceal = val->boolean;
|
||||
break;
|
||||
case VTERM_ATTR_STRIKE:
|
||||
state->pen.strike = val->boolean;
|
||||
break;
|
||||
case VTERM_ATTR_FONT:
|
||||
state->pen.font = val->number;
|
||||
break;
|
||||
case VTERM_ATTR_FOREGROUND:
|
||||
state->pen.fg = val->color;
|
||||
break;
|
||||
case VTERM_ATTR_BACKGROUND:
|
||||
state->pen.bg = val->color;
|
||||
break;
|
||||
case VTERM_ATTR_SMALL:
|
||||
state->pen.small = val->boolean;
|
||||
break;
|
||||
case VTERM_ATTR_BASELINE:
|
||||
state->pen.baseline = val->number;
|
||||
break;
|
||||
case VTERM_ATTR_URI:
|
||||
state->pen.uri = val->number;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(state->callbacks && state->callbacks->setpenattr)
|
||||
(*state->callbacks->setpenattr)(attr, val, state->cbdata);
|
||||
|
||||
return 1;
|
||||
}
|
@ -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;
|
||||
}
|
1174
src/vterm/screen.c
1174
src/vterm/screen.c
File diff suppressed because it is too large
Load Diff
2281
src/vterm/state.c
2281
src/vterm/state.c
File diff suppressed because it is too large
Load Diff
@ -1,432 +0,0 @@
|
||||
#include "vterm_internal.h"
|
||||
|
||||
#include "auto/config.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/*****************
|
||||
* API functions *
|
||||
*****************/
|
||||
|
||||
static void *default_malloc(size_t size, void *allocdata)
|
||||
{
|
||||
void *ptr = malloc(size);
|
||||
if(ptr)
|
||||
memset(ptr, 0, size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void default_free(void *ptr, void *allocdata)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static VTermAllocatorFunctions default_allocator = {
|
||||
.malloc = &default_malloc,
|
||||
.free = &default_free,
|
||||
};
|
||||
|
||||
VTerm *vterm_new(int rows, int cols)
|
||||
{
|
||||
return vterm_build(&(const struct VTermBuilder){
|
||||
.rows = rows,
|
||||
.cols = cols,
|
||||
});
|
||||
}
|
||||
|
||||
VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *funcs, void *allocdata)
|
||||
{
|
||||
return vterm_build(&(const struct VTermBuilder){
|
||||
.rows = rows,
|
||||
.cols = cols,
|
||||
.allocator = funcs,
|
||||
.allocdata = allocdata,
|
||||
});
|
||||
}
|
||||
|
||||
/* A handy macro for defaulting values out of builder fields */
|
||||
#define DEFAULT(v, def) ((v) ? (v) : (def))
|
||||
|
||||
VTerm *vterm_build(const struct VTermBuilder *builder)
|
||||
{
|
||||
const VTermAllocatorFunctions *allocator = DEFAULT(builder->allocator, &default_allocator);
|
||||
|
||||
/* Need to bootstrap using the allocator function directly */
|
||||
VTerm *vt = (*allocator->malloc)(sizeof(VTerm), builder->allocdata);
|
||||
|
||||
vt->allocator = allocator;
|
||||
vt->allocdata = builder->allocdata;
|
||||
|
||||
vt->rows = builder->rows;
|
||||
vt->cols = builder->cols;
|
||||
|
||||
vt->parser.state = NORMAL;
|
||||
|
||||
vt->parser.callbacks = NULL;
|
||||
vt->parser.cbdata = NULL;
|
||||
|
||||
vt->parser.emit_nul = false;
|
||||
|
||||
vt->outfunc = NULL;
|
||||
vt->outdata = NULL;
|
||||
|
||||
vt->outbuffer_len = DEFAULT(builder->outbuffer_len, 4096);
|
||||
vt->outbuffer_cur = 0;
|
||||
vt->outbuffer = vterm_allocator_malloc(vt, vt->outbuffer_len);
|
||||
|
||||
vt->tmpbuffer_len = DEFAULT(builder->tmpbuffer_len, 4096);
|
||||
vt->tmpbuffer = vterm_allocator_malloc(vt, vt->tmpbuffer_len);
|
||||
|
||||
return vt;
|
||||
}
|
||||
|
||||
void vterm_free(VTerm *vt)
|
||||
{
|
||||
if(vt->screen)
|
||||
vterm_screen_free(vt->screen);
|
||||
|
||||
if(vt->state)
|
||||
vterm_state_free(vt->state);
|
||||
|
||||
vterm_allocator_free(vt, vt->outbuffer);
|
||||
vterm_allocator_free(vt, vt->tmpbuffer);
|
||||
|
||||
vterm_allocator_free(vt, vt);
|
||||
}
|
||||
|
||||
INTERNAL void *vterm_allocator_malloc(VTerm *vt, size_t size)
|
||||
{
|
||||
return (*vt->allocator->malloc)(size, vt->allocdata);
|
||||
}
|
||||
|
||||
INTERNAL void vterm_allocator_free(VTerm *vt, void *ptr)
|
||||
{
|
||||
(*vt->allocator->free)(ptr, vt->allocdata);
|
||||
}
|
||||
|
||||
void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp)
|
||||
{
|
||||
if(rowsp)
|
||||
*rowsp = vt->rows;
|
||||
if(colsp)
|
||||
*colsp = vt->cols;
|
||||
}
|
||||
|
||||
void vterm_set_size(VTerm *vt, int rows, int cols)
|
||||
{
|
||||
if(rows < 1 || cols < 1)
|
||||
return;
|
||||
|
||||
vt->rows = rows;
|
||||
vt->cols = cols;
|
||||
|
||||
if(vt->parser.callbacks && vt->parser.callbacks->resize)
|
||||
(*vt->parser.callbacks->resize)(rows, cols, vt->parser.cbdata);
|
||||
}
|
||||
|
||||
int vterm_get_utf8(const VTerm *vt)
|
||||
{
|
||||
return vt->mode.utf8;
|
||||
}
|
||||
|
||||
void vterm_set_utf8(VTerm *vt, int is_utf8)
|
||||
{
|
||||
vt->mode.utf8 = is_utf8;
|
||||
}
|
||||
|
||||
void vterm_output_set_callback(VTerm *vt, VTermOutputCallback *func, void *user)
|
||||
{
|
||||
vt->outfunc = func;
|
||||
vt->outdata = user;
|
||||
}
|
||||
|
||||
INTERNAL void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len)
|
||||
{
|
||||
if(vt->outfunc) {
|
||||
(vt->outfunc)(bytes, len, vt->outdata);
|
||||
return;
|
||||
}
|
||||
|
||||
if(len > vt->outbuffer_len - vt->outbuffer_cur)
|
||||
return;
|
||||
|
||||
memcpy(vt->outbuffer + vt->outbuffer_cur, bytes, len);
|
||||
vt->outbuffer_cur += len;
|
||||
}
|
||||
|
||||
INTERNAL void vterm_push_output_vsprintf(VTerm *vt, const char *format, va_list args)
|
||||
{
|
||||
size_t len = vsnprintf(vt->tmpbuffer, vt->tmpbuffer_len,
|
||||
format, args);
|
||||
|
||||
vterm_push_output_bytes(vt, vt->tmpbuffer, len);
|
||||
}
|
||||
|
||||
INTERNAL void vterm_push_output_sprintf(VTerm *vt, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vterm_push_output_vsprintf(vt, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
INTERNAL void vterm_push_output_sprintf_ctrl(VTerm *vt, unsigned char ctrl, const char *fmt, ...)
|
||||
{
|
||||
size_t cur;
|
||||
|
||||
if(ctrl >= 0x80 && !vt->mode.ctrl8bit)
|
||||
cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len,
|
||||
ESC_S "%c", ctrl - 0x40);
|
||||
else
|
||||
cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len,
|
||||
"%c", ctrl);
|
||||
|
||||
if(cur >= vt->tmpbuffer_len)
|
||||
return;
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
cur += vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
|
||||
fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if(cur >= vt->tmpbuffer_len)
|
||||
return;
|
||||
|
||||
vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
|
||||
}
|
||||
|
||||
INTERNAL void vterm_push_output_sprintf_str(VTerm *vt, unsigned char ctrl, bool term, const char *fmt, ...)
|
||||
{
|
||||
size_t cur = 0;
|
||||
|
||||
if(ctrl) {
|
||||
if(ctrl >= 0x80 && !vt->mode.ctrl8bit)
|
||||
cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len,
|
||||
ESC_S "%c", ctrl - 0x40);
|
||||
else
|
||||
cur = snprintf(vt->tmpbuffer, vt->tmpbuffer_len,
|
||||
"%c", ctrl);
|
||||
|
||||
if(cur >= vt->tmpbuffer_len)
|
||||
return;
|
||||
}
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
cur += vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
|
||||
fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if(cur >= vt->tmpbuffer_len)
|
||||
return;
|
||||
|
||||
if(term) {
|
||||
cur += snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
|
||||
vt->mode.ctrl8bit ? "\x9C" : ESC_S "\\"); // ST
|
||||
|
||||
if(cur >= vt->tmpbuffer_len)
|
||||
return;
|
||||
}
|
||||
|
||||
vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
|
||||
}
|
||||
|
||||
size_t vterm_output_get_buffer_size(const VTerm *vt)
|
||||
{
|
||||
return vt->outbuffer_len;
|
||||
}
|
||||
|
||||
size_t vterm_output_get_buffer_current(const VTerm *vt)
|
||||
{
|
||||
return vt->outbuffer_cur;
|
||||
}
|
||||
|
||||
size_t vterm_output_get_buffer_remaining(const VTerm *vt)
|
||||
{
|
||||
return vt->outbuffer_len - vt->outbuffer_cur;
|
||||
}
|
||||
|
||||
size_t vterm_output_read(VTerm *vt, char *buffer, size_t len)
|
||||
{
|
||||
if(len > vt->outbuffer_cur)
|
||||
len = vt->outbuffer_cur;
|
||||
|
||||
memcpy(buffer, vt->outbuffer, len);
|
||||
|
||||
if(len < vt->outbuffer_cur)
|
||||
memmove(vt->outbuffer, vt->outbuffer + len, vt->outbuffer_cur - len);
|
||||
|
||||
vt->outbuffer_cur -= len;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
VTermValueType vterm_get_attr_type(VTermAttr attr)
|
||||
{
|
||||
switch(attr) {
|
||||
case VTERM_ATTR_BOLD: return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_ATTR_UNDERLINE: return VTERM_VALUETYPE_INT;
|
||||
case VTERM_ATTR_ITALIC: return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_ATTR_BLINK: return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_ATTR_REVERSE: return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_ATTR_CONCEAL: return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_ATTR_STRIKE: return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_ATTR_FONT: return VTERM_VALUETYPE_INT;
|
||||
case VTERM_ATTR_FOREGROUND: return VTERM_VALUETYPE_COLOR;
|
||||
case VTERM_ATTR_BACKGROUND: return VTERM_VALUETYPE_COLOR;
|
||||
case VTERM_ATTR_SMALL: return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_ATTR_BASELINE: return VTERM_VALUETYPE_INT;
|
||||
case VTERM_ATTR_URI: return VTERM_VALUETYPE_INT;
|
||||
|
||||
case VTERM_N_ATTRS: return 0;
|
||||
}
|
||||
return 0; /* UNREACHABLE */
|
||||
}
|
||||
|
||||
VTermValueType vterm_get_prop_type(VTermProp prop)
|
||||
{
|
||||
switch(prop) {
|
||||
case VTERM_PROP_CURSORVISIBLE: return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_PROP_CURSORBLINK: return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_PROP_ALTSCREEN: return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_PROP_TITLE: return VTERM_VALUETYPE_STRING;
|
||||
case VTERM_PROP_ICONNAME: return VTERM_VALUETYPE_STRING;
|
||||
case VTERM_PROP_REVERSE: return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_PROP_CURSORSHAPE: return VTERM_VALUETYPE_INT;
|
||||
case VTERM_PROP_MOUSE: return VTERM_VALUETYPE_INT;
|
||||
case VTERM_PROP_FOCUSREPORT: return VTERM_VALUETYPE_BOOL;
|
||||
|
||||
case VTERM_N_PROPS: return 0;
|
||||
}
|
||||
return 0; /* UNREACHABLE */
|
||||
}
|
||||
|
||||
void vterm_scroll_rect(VTermRect rect,
|
||||
int downward,
|
||||
int rightward,
|
||||
int (*moverect)(VTermRect src, VTermRect dest, void *user),
|
||||
int (*eraserect)(VTermRect rect, int selective, void *user),
|
||||
void *user)
|
||||
{
|
||||
VTermRect src;
|
||||
VTermRect dest;
|
||||
|
||||
if(abs(downward) >= rect.end_row - rect.start_row ||
|
||||
abs(rightward) >= rect.end_col - rect.start_col) {
|
||||
/* Scroll more than area; just erase the lot */
|
||||
(*eraserect)(rect, 0, user);
|
||||
return;
|
||||
}
|
||||
|
||||
if(rightward >= 0) {
|
||||
/* rect: [XXX................]
|
||||
* src: [----------------]
|
||||
* dest: [----------------]
|
||||
*/
|
||||
dest.start_col = rect.start_col;
|
||||
dest.end_col = rect.end_col - rightward;
|
||||
src.start_col = rect.start_col + rightward;
|
||||
src.end_col = rect.end_col;
|
||||
}
|
||||
else {
|
||||
/* rect: [................XXX]
|
||||
* src: [----------------]
|
||||
* dest: [----------------]
|
||||
*/
|
||||
int leftward = -rightward;
|
||||
dest.start_col = rect.start_col + leftward;
|
||||
dest.end_col = rect.end_col;
|
||||
src.start_col = rect.start_col;
|
||||
src.end_col = rect.end_col - leftward;
|
||||
}
|
||||
|
||||
if(downward >= 0) {
|
||||
dest.start_row = rect.start_row;
|
||||
dest.end_row = rect.end_row - downward;
|
||||
src.start_row = rect.start_row + downward;
|
||||
src.end_row = rect.end_row;
|
||||
}
|
||||
else {
|
||||
int upward = -downward;
|
||||
dest.start_row = rect.start_row + upward;
|
||||
dest.end_row = rect.end_row;
|
||||
src.start_row = rect.start_row;
|
||||
src.end_row = rect.end_row - upward;
|
||||
}
|
||||
|
||||
if(moverect)
|
||||
(*moverect)(dest, src, user);
|
||||
|
||||
if(downward > 0)
|
||||
rect.start_row = rect.end_row - downward;
|
||||
else if(downward < 0)
|
||||
rect.end_row = rect.start_row - downward;
|
||||
|
||||
if(rightward > 0)
|
||||
rect.start_col = rect.end_col - rightward;
|
||||
else if(rightward < 0)
|
||||
rect.end_col = rect.start_col - rightward;
|
||||
|
||||
(*eraserect)(rect, 0, user);
|
||||
}
|
||||
|
||||
void vterm_copy_cells(VTermRect dest,
|
||||
VTermRect src,
|
||||
void (*copycell)(VTermPos dest, VTermPos src, void *user),
|
||||
void *user)
|
||||
{
|
||||
int downward = src.start_row - dest.start_row;
|
||||
int rightward = src.start_col - dest.start_col;
|
||||
|
||||
int init_row, test_row, init_col, test_col;
|
||||
int inc_row, inc_col;
|
||||
|
||||
if(downward < 0) {
|
||||
init_row = dest.end_row - 1;
|
||||
test_row = dest.start_row - 1;
|
||||
inc_row = -1;
|
||||
}
|
||||
else /* downward >= 0 */ {
|
||||
init_row = dest.start_row;
|
||||
test_row = dest.end_row;
|
||||
inc_row = +1;
|
||||
}
|
||||
|
||||
if(rightward < 0) {
|
||||
init_col = dest.end_col - 1;
|
||||
test_col = dest.start_col - 1;
|
||||
inc_col = -1;
|
||||
}
|
||||
else /* rightward >= 0 */ {
|
||||
init_col = dest.start_col;
|
||||
test_col = dest.end_col;
|
||||
inc_col = +1;
|
||||
}
|
||||
|
||||
VTermPos pos;
|
||||
for(pos.row = init_row; pos.row != test_row; pos.row += inc_row)
|
||||
for(pos.col = init_col; pos.col != test_col; pos.col += inc_col) {
|
||||
VTermPos srcpos = { pos.row + downward, pos.col + rightward };
|
||||
(*copycell)(pos, srcpos, user);
|
||||
}
|
||||
}
|
||||
|
||||
void vterm_check_version(int major, int minor)
|
||||
{
|
||||
if(major != VTERM_VERSION_MAJOR) {
|
||||
fprintf(stderr, "libvterm major version mismatch; %d (wants) != %d (library)\n",
|
||||
major, VTERM_VERSION_MAJOR);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if(minor > VTERM_VERSION_MINOR) {
|
||||
fprintf(stderr, "libvterm minor version mismatch; %d (wants) > %d (library)\n",
|
||||
minor, VTERM_VERSION_MINOR);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Happy
|
||||
}
|
@ -1,638 +0,0 @@
|
||||
#ifndef __VTERM_H__
|
||||
#define __VTERM_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "nvim/macros_defs.h"
|
||||
#include "nvim/types_defs.h"
|
||||
#include "vterm_keycodes.h"
|
||||
|
||||
#define VTERM_VERSION_MAJOR 0
|
||||
#define VTERM_VERSION_MINOR 3
|
||||
#define VTERM_VERSION_PATCH 3
|
||||
|
||||
#define VTERM_CHECK_VERSION \
|
||||
vterm_check_version(VTERM_VERSION_MAJOR, VTERM_VERSION_MINOR)
|
||||
|
||||
typedef struct VTerm VTerm;
|
||||
typedef struct VTermState VTermState;
|
||||
typedef struct VTermScreen VTermScreen;
|
||||
|
||||
typedef struct {
|
||||
int row;
|
||||
int col;
|
||||
} VTermPos;
|
||||
|
||||
/* some small utility functions; we can just keep these static here */
|
||||
|
||||
/* order points by on-screen flow order */
|
||||
static inline int vterm_pos_cmp(VTermPos a, VTermPos b)
|
||||
{
|
||||
return (a.row == b.row) ? a.col - b.col : a.row - b.row;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int start_row;
|
||||
int end_row;
|
||||
int start_col;
|
||||
int end_col;
|
||||
} VTermRect;
|
||||
|
||||
/* true if the rect contains the point */
|
||||
static inline int vterm_rect_contains(VTermRect r, VTermPos p)
|
||||
{
|
||||
return p.row >= r.start_row && p.row < r.end_row &&
|
||||
p.col >= r.start_col && p.col < r.end_col;
|
||||
}
|
||||
|
||||
/* move a rect */
|
||||
static inline void vterm_rect_move(VTermRect *rect, int row_delta, int col_delta)
|
||||
{
|
||||
rect->start_row += row_delta; rect->end_row += row_delta;
|
||||
rect->start_col += col_delta; rect->end_col += col_delta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bit-field describing the content of the tagged union `VTermColor`.
|
||||
*/
|
||||
typedef enum {
|
||||
/**
|
||||
* If the lower bit of `type` is not set, the colour is 24-bit RGB.
|
||||
*/
|
||||
VTERM_COLOR_RGB = 0x00,
|
||||
|
||||
/**
|
||||
* The colour is an index into a palette of 256 colours.
|
||||
*/
|
||||
VTERM_COLOR_INDEXED = 0x01,
|
||||
|
||||
/**
|
||||
* Mask that can be used to extract the RGB/Indexed bit.
|
||||
*/
|
||||
VTERM_COLOR_TYPE_MASK = 0x01,
|
||||
|
||||
/**
|
||||
* If set, indicates that this colour should be the default foreground
|
||||
* color, i.e. there was no SGR request for another colour. When
|
||||
* rendering this colour it is possible to ignore "idx" and just use a
|
||||
* colour that is not in the palette.
|
||||
*/
|
||||
VTERM_COLOR_DEFAULT_FG = 0x02,
|
||||
|
||||
/**
|
||||
* If set, indicates that this colour should be the default background
|
||||
* color, i.e. there was no SGR request for another colour. A common
|
||||
* option when rendering this colour is to not render a background at
|
||||
* all, for example by rendering the window transparently at this spot.
|
||||
*/
|
||||
VTERM_COLOR_DEFAULT_BG = 0x04,
|
||||
|
||||
/**
|
||||
* Mask that can be used to extract the default foreground/background bit.
|
||||
*/
|
||||
VTERM_COLOR_DEFAULT_MASK = 0x06
|
||||
} VTermColorType;
|
||||
|
||||
/**
|
||||
* Returns true if the VTERM_COLOR_RGB `type` flag is set, indicating that the
|
||||
* given VTermColor instance is an indexed colour.
|
||||
*/
|
||||
#define VTERM_COLOR_IS_INDEXED(col) \
|
||||
(((col)->type & VTERM_COLOR_TYPE_MASK) == VTERM_COLOR_INDEXED)
|
||||
|
||||
/**
|
||||
* Returns true if the VTERM_COLOR_INDEXED `type` flag is set, indicating that
|
||||
* the given VTermColor instance is an rgb colour.
|
||||
*/
|
||||
#define VTERM_COLOR_IS_RGB(col) \
|
||||
(((col)->type & VTERM_COLOR_TYPE_MASK) == VTERM_COLOR_RGB)
|
||||
|
||||
/**
|
||||
* Returns true if the VTERM_COLOR_DEFAULT_FG `type` flag is set, indicating
|
||||
* that the given VTermColor instance corresponds to the default foreground
|
||||
* color.
|
||||
*/
|
||||
#define VTERM_COLOR_IS_DEFAULT_FG(col) \
|
||||
(!!((col)->type & VTERM_COLOR_DEFAULT_FG))
|
||||
|
||||
/**
|
||||
* Returns true if the VTERM_COLOR_DEFAULT_BG `type` flag is set, indicating
|
||||
* that the given VTermColor instance corresponds to the default background
|
||||
* color.
|
||||
*/
|
||||
#define VTERM_COLOR_IS_DEFAULT_BG(col) \
|
||||
(!!((col)->type & VTERM_COLOR_DEFAULT_BG))
|
||||
|
||||
/**
|
||||
* Tagged union storing either an RGB color or an index into a colour palette.
|
||||
* In order to convert indexed colours to RGB, you may use the
|
||||
* vterm_state_convert_color_to_rgb() or vterm_screen_convert_color_to_rgb()
|
||||
* functions which lookup the RGB colour from the palette maintained by a
|
||||
* VTermState or VTermScreen instance.
|
||||
*/
|
||||
typedef union {
|
||||
/**
|
||||
* Tag indicating which union member is actually valid. This variable
|
||||
* coincides with the `type` member of the `rgb` and the `indexed` struct
|
||||
* in memory. Please use the `VTERM_COLOR_IS_*` test macros to check whether
|
||||
* a particular type flag is set.
|
||||
*/
|
||||
uint8_t type;
|
||||
|
||||
/**
|
||||
* Valid if `VTERM_COLOR_IS_RGB(type)` is true. Holds the RGB colour values.
|
||||
*/
|
||||
struct {
|
||||
/**
|
||||
* Same as the top-level `type` member stored in VTermColor.
|
||||
*/
|
||||
uint8_t type;
|
||||
|
||||
/**
|
||||
* The actual 8-bit red, green, blue colour values.
|
||||
*/
|
||||
uint8_t red, green, blue;
|
||||
} rgb;
|
||||
|
||||
/**
|
||||
* If `VTERM_COLOR_IS_INDEXED(type)` is true, this member holds the index into
|
||||
* the colour palette.
|
||||
*/
|
||||
struct {
|
||||
/**
|
||||
* Same as the top-level `type` member stored in VTermColor.
|
||||
*/
|
||||
uint8_t type;
|
||||
|
||||
/**
|
||||
* Index into the colour map.
|
||||
*/
|
||||
uint8_t idx;
|
||||
} indexed;
|
||||
} VTermColor;
|
||||
|
||||
/**
|
||||
* Constructs a new VTermColor instance representing the given RGB values.
|
||||
*/
|
||||
static inline void vterm_color_rgb(VTermColor *col, uint8_t red, uint8_t green,
|
||||
uint8_t blue)
|
||||
{
|
||||
col->type = VTERM_COLOR_RGB;
|
||||
col->rgb.red = red;
|
||||
col->rgb.green = green;
|
||||
col->rgb.blue = blue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new VTermColor instance representing an indexed color with the
|
||||
* given index.
|
||||
*/
|
||||
static inline void vterm_color_indexed(VTermColor *col, uint8_t idx)
|
||||
{
|
||||
col->type = VTERM_COLOR_INDEXED;
|
||||
col->indexed.idx = idx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two colours. Returns true if the colors are equal, false otherwise.
|
||||
*/
|
||||
int vterm_color_is_equal(const VTermColor *a, const VTermColor *b);
|
||||
|
||||
typedef enum {
|
||||
/* VTERM_VALUETYPE_NONE = 0 */
|
||||
VTERM_VALUETYPE_BOOL = 1,
|
||||
VTERM_VALUETYPE_INT,
|
||||
VTERM_VALUETYPE_STRING,
|
||||
VTERM_VALUETYPE_COLOR,
|
||||
|
||||
VTERM_N_VALUETYPES
|
||||
} VTermValueType;
|
||||
|
||||
typedef struct {
|
||||
const char *str;
|
||||
size_t len : 30;
|
||||
bool initial : 1;
|
||||
bool final : 1;
|
||||
} VTermStringFragment;
|
||||
|
||||
typedef union {
|
||||
int boolean;
|
||||
int number;
|
||||
VTermStringFragment string;
|
||||
VTermColor color;
|
||||
} VTermValue;
|
||||
|
||||
typedef enum {
|
||||
/* VTERM_ATTR_NONE = 0 */
|
||||
VTERM_ATTR_BOLD = 1, // bool: 1, 22
|
||||
VTERM_ATTR_UNDERLINE, // number: 4, 21, 24
|
||||
VTERM_ATTR_ITALIC, // bool: 3, 23
|
||||
VTERM_ATTR_BLINK, // bool: 5, 25
|
||||
VTERM_ATTR_REVERSE, // bool: 7, 27
|
||||
VTERM_ATTR_CONCEAL, // bool: 8, 28
|
||||
VTERM_ATTR_STRIKE, // bool: 9, 29
|
||||
VTERM_ATTR_FONT, // number: 10-19
|
||||
VTERM_ATTR_FOREGROUND, // color: 30-39 90-97
|
||||
VTERM_ATTR_BACKGROUND, // color: 40-49 100-107
|
||||
VTERM_ATTR_SMALL, // bool: 73, 74, 75
|
||||
VTERM_ATTR_BASELINE, // number: 73, 74, 75
|
||||
VTERM_ATTR_URI, // number
|
||||
|
||||
VTERM_N_ATTRS
|
||||
} VTermAttr;
|
||||
|
||||
typedef enum {
|
||||
/* VTERM_PROP_NONE = 0 */
|
||||
VTERM_PROP_CURSORVISIBLE = 1, // bool
|
||||
VTERM_PROP_CURSORBLINK, // bool
|
||||
VTERM_PROP_ALTSCREEN, // bool
|
||||
VTERM_PROP_TITLE, // string
|
||||
VTERM_PROP_ICONNAME, // string
|
||||
VTERM_PROP_REVERSE, // bool
|
||||
VTERM_PROP_CURSORSHAPE, // number
|
||||
VTERM_PROP_MOUSE, // number
|
||||
VTERM_PROP_FOCUSREPORT, // bool
|
||||
|
||||
VTERM_N_PROPS
|
||||
} VTermProp;
|
||||
|
||||
enum {
|
||||
VTERM_PROP_CURSORSHAPE_BLOCK = 1,
|
||||
VTERM_PROP_CURSORSHAPE_UNDERLINE,
|
||||
VTERM_PROP_CURSORSHAPE_BAR_LEFT,
|
||||
|
||||
VTERM_N_PROP_CURSORSHAPES
|
||||
};
|
||||
|
||||
enum {
|
||||
VTERM_PROP_MOUSE_NONE = 0,
|
||||
VTERM_PROP_MOUSE_CLICK,
|
||||
VTERM_PROP_MOUSE_DRAG,
|
||||
VTERM_PROP_MOUSE_MOVE,
|
||||
|
||||
VTERM_N_PROP_MOUSES
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
VTERM_SELECTION_CLIPBOARD = (1<<0),
|
||||
VTERM_SELECTION_PRIMARY = (1<<1),
|
||||
VTERM_SELECTION_SECONDARY = (1<<2),
|
||||
VTERM_SELECTION_SELECT = (1<<3),
|
||||
VTERM_SELECTION_CUT0 = (1<<4), /* also CUT1 .. CUT7 by bitshifting */
|
||||
} VTermSelectionMask;
|
||||
|
||||
typedef struct {
|
||||
schar_T schar;
|
||||
int width;
|
||||
unsigned int protected_cell:1; /* DECSCA-protected against DECSEL/DECSED */
|
||||
unsigned int dwl:1; /* DECDWL or DECDHL double-width line */
|
||||
unsigned int dhl:2; /* DECDHL double-height line (1=top 2=bottom) */
|
||||
} VTermGlyphInfo;
|
||||
|
||||
typedef struct {
|
||||
unsigned int doublewidth:1; /* DECDWL or DECDHL line */
|
||||
unsigned int doubleheight:2; /* DECDHL line (1=top 2=bottom) */
|
||||
unsigned int continuation:1; /* Line is a flow continuation of the previous */
|
||||
} VTermLineInfo;
|
||||
|
||||
/* Copies of VTermState fields that the 'resize' callback might have reason to
|
||||
* edit. 'resize' callback gets total control of these fields and may
|
||||
* free-and-reallocate them if required. They will be copied back from the
|
||||
* struct after the callback has returned.
|
||||
*/
|
||||
typedef struct {
|
||||
VTermPos pos; /* current cursor position */
|
||||
VTermLineInfo *lineinfos[2]; /* [1] may be NULL */
|
||||
} VTermStateFields;
|
||||
|
||||
typedef struct {
|
||||
/* libvterm relies on this memory to be zeroed out before it is returned
|
||||
* by the allocator. */
|
||||
void *(*malloc)(size_t size, void *allocdata);
|
||||
void (*free)(void *ptr, void *allocdata);
|
||||
} VTermAllocatorFunctions;
|
||||
|
||||
void vterm_check_version(int major, int minor);
|
||||
|
||||
struct VTermBuilder {
|
||||
int ver; /* currently unused but reserved for some sort of ABI version flag */
|
||||
|
||||
int rows, cols;
|
||||
|
||||
const VTermAllocatorFunctions *allocator;
|
||||
void *allocdata;
|
||||
|
||||
/* Override default sizes for various structures */
|
||||
size_t outbuffer_len; /* default: 4096 */
|
||||
size_t tmpbuffer_len; /* default: 4096 */
|
||||
};
|
||||
|
||||
VTerm *vterm_build(const struct VTermBuilder *builder);
|
||||
|
||||
/* A convenient shortcut for default cases */
|
||||
VTerm *vterm_new(int rows, int cols);
|
||||
/* This shortcuts are generally discouraged in favour of just using vterm_build() */
|
||||
VTerm *vterm_new_with_allocator(int rows, int cols, VTermAllocatorFunctions *funcs, void *allocdata);
|
||||
|
||||
void vterm_free(VTerm* vt);
|
||||
|
||||
void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp);
|
||||
void vterm_set_size(VTerm *vt, int rows, int cols);
|
||||
|
||||
int vterm_get_utf8(const VTerm *vt);
|
||||
void vterm_set_utf8(VTerm *vt, int is_utf8);
|
||||
|
||||
size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len);
|
||||
|
||||
/* Setting output callback will override the buffer logic */
|
||||
typedef void VTermOutputCallback(const char *s, size_t len, void *user);
|
||||
void vterm_output_set_callback(VTerm *vt, VTermOutputCallback *func, void *user);
|
||||
|
||||
/* These buffer functions only work if output callback is NOT set
|
||||
* These are deprecated and will be removed in a later version */
|
||||
size_t vterm_output_get_buffer_size(const VTerm *vt);
|
||||
size_t vterm_output_get_buffer_current(const VTerm *vt);
|
||||
size_t vterm_output_get_buffer_remaining(const VTerm *vt);
|
||||
|
||||
/* This too */
|
||||
size_t vterm_output_read(VTerm *vt, char *buffer, size_t len);
|
||||
|
||||
void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod);
|
||||
void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod);
|
||||
|
||||
void vterm_keyboard_start_paste(VTerm *vt);
|
||||
void vterm_keyboard_end_paste(VTerm *vt);
|
||||
|
||||
void vterm_mouse_move(VTerm *vt, int row, int col, VTermModifier mod);
|
||||
void vterm_mouse_button(VTerm *vt, int button, bool pressed, VTermModifier mod);
|
||||
|
||||
// ------------
|
||||
// Parser layer
|
||||
// ------------
|
||||
|
||||
/* Flag to indicate non-final subparameters in a single CSI parameter.
|
||||
* Consider
|
||||
* CSI 1;2:3:4;5a
|
||||
* 1 4 and 5 are final.
|
||||
* 2 and 3 are non-final and will have this bit set
|
||||
*
|
||||
* Don't confuse this with the final byte of the CSI escape; 'a' in this case.
|
||||
*/
|
||||
#define CSI_ARG_FLAG_MORE (1U<<31)
|
||||
#define CSI_ARG_MASK (~(1U<<31))
|
||||
|
||||
#define CSI_ARG_HAS_MORE(a) ((a) & CSI_ARG_FLAG_MORE)
|
||||
#define CSI_ARG(a) ((a) & CSI_ARG_MASK)
|
||||
|
||||
/* Can't use -1 to indicate a missing argument; use this instead */
|
||||
#define CSI_ARG_MISSING ((1UL<<31)-1)
|
||||
|
||||
#define CSI_ARG_IS_MISSING(a) (CSI_ARG(a) == CSI_ARG_MISSING)
|
||||
#define CSI_ARG_OR(a,def) (CSI_ARG(a) == CSI_ARG_MISSING ? (def) : CSI_ARG(a))
|
||||
#define CSI_ARG_COUNT(a) (CSI_ARG(a) == CSI_ARG_MISSING || CSI_ARG(a) == 0 ? 1 : CSI_ARG(a))
|
||||
|
||||
typedef struct {
|
||||
int (*text)(const char *bytes, size_t len, void *user);
|
||||
int (*control)(unsigned char control, void *user);
|
||||
int (*escape)(const char *bytes, size_t len, void *user);
|
||||
int (*csi)(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user);
|
||||
int (*osc)(int command, VTermStringFragment frag, void *user);
|
||||
int (*dcs)(const char *command, size_t commandlen, VTermStringFragment frag, void *user);
|
||||
int (*apc)(VTermStringFragment frag, void *user);
|
||||
int (*pm)(VTermStringFragment frag, void *user);
|
||||
int (*sos)(VTermStringFragment frag, void *user);
|
||||
int (*resize)(int rows, int cols, void *user);
|
||||
} VTermParserCallbacks;
|
||||
|
||||
void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user);
|
||||
void *vterm_parser_get_cbdata(VTerm *vt);
|
||||
|
||||
/* Normally NUL, CAN, SUB and DEL are ignored. Setting this true causes them
|
||||
* to be emitted by the 'control' callback
|
||||
*/
|
||||
void vterm_parser_set_emit_nul(VTerm *vt, bool emit);
|
||||
|
||||
// -----------
|
||||
// State layer
|
||||
// -----------
|
||||
|
||||
typedef struct {
|
||||
int (*putglyph)(VTermGlyphInfo *info, VTermPos pos, void *user);
|
||||
int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
|
||||
int (*scrollrect)(VTermRect rect, int downward, int rightward, void *user);
|
||||
int (*moverect)(VTermRect dest, VTermRect src, void *user);
|
||||
int (*erase)(VTermRect rect, int selective, void *user);
|
||||
int (*initpen)(void *user);
|
||||
int (*setpenattr)(VTermAttr attr, VTermValue *val, void *user);
|
||||
int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
|
||||
int (*bell)(void *user);
|
||||
int (*resize)(int rows, int cols, VTermStateFields *fields, void *user);
|
||||
int (*setlineinfo)(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user);
|
||||
int (*sb_clear)(void *user);
|
||||
} VTermStateCallbacks;
|
||||
|
||||
typedef struct {
|
||||
int (*control)(unsigned char control, void *user);
|
||||
int (*csi)(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user);
|
||||
int (*osc)(int command, VTermStringFragment frag, void *user);
|
||||
int (*dcs)(const char *command, size_t commandlen, VTermStringFragment frag, void *user);
|
||||
int (*apc)(VTermStringFragment frag, void *user);
|
||||
int (*pm)(VTermStringFragment frag, void *user);
|
||||
int (*sos)(VTermStringFragment frag, void *user);
|
||||
} VTermStateFallbacks;
|
||||
|
||||
typedef struct {
|
||||
int (*set)(VTermSelectionMask mask, VTermStringFragment frag, void *user);
|
||||
int (*query)(VTermSelectionMask mask, void *user);
|
||||
} VTermSelectionCallbacks;
|
||||
|
||||
VTermState *vterm_obtain_state(VTerm *vt);
|
||||
|
||||
void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user);
|
||||
void *vterm_state_get_cbdata(VTermState *state);
|
||||
|
||||
void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermStateFallbacks *fallbacks, void *user);
|
||||
void *vterm_state_get_unrecognised_fbdata(VTermState *state);
|
||||
|
||||
void vterm_state_reset(VTermState *state, int hard);
|
||||
void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos);
|
||||
void vterm_state_get_default_colors(const VTermState *state, VTermColor *default_fg, VTermColor *default_bg);
|
||||
void vterm_state_get_palette_color(const VTermState *state, int index, VTermColor *col);
|
||||
void vterm_state_set_default_colors(VTermState *state, const VTermColor *default_fg, const VTermColor *default_bg);
|
||||
void vterm_state_set_palette_color(VTermState *state, int index, const VTermColor *col);
|
||||
void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright);
|
||||
int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val);
|
||||
int vterm_state_set_penattr(VTermState *state, VTermAttr attr, VTermValueType type, VTermValue *val);
|
||||
int vterm_state_set_termprop(VTermState *state, VTermProp prop, VTermValue *val);
|
||||
void vterm_state_focus_in(VTermState *state);
|
||||
void vterm_state_focus_out(VTermState *state);
|
||||
const VTermLineInfo *vterm_state_get_lineinfo(const VTermState *state, int row);
|
||||
|
||||
/**
|
||||
* Makes sure that the given color `col` is indeed an RGB colour. After this
|
||||
* function returns, VTERM_COLOR_IS_RGB(col) will return true, while all other
|
||||
* flags stored in `col->type` will have been reset.
|
||||
*
|
||||
* @param state is the VTermState instance from which the colour palette should
|
||||
* be extracted.
|
||||
* @param col is a pointer at the VTermColor instance that should be converted
|
||||
* to an RGB colour.
|
||||
*/
|
||||
void vterm_state_convert_color_to_rgb(const VTermState *state, VTermColor *col);
|
||||
|
||||
void vterm_state_set_selection_callbacks(VTermState *state, const VTermSelectionCallbacks *callbacks, void *user,
|
||||
char *buffer, size_t buflen);
|
||||
|
||||
void vterm_state_send_selection(VTermState *state, VTermSelectionMask mask, VTermStringFragment frag);
|
||||
|
||||
// ------------
|
||||
// Screen layer
|
||||
// ------------
|
||||
|
||||
typedef struct {
|
||||
unsigned int bold : 1;
|
||||
unsigned int underline : 2;
|
||||
unsigned int italic : 1;
|
||||
unsigned int blink : 1;
|
||||
unsigned int reverse : 1;
|
||||
unsigned int conceal : 1;
|
||||
unsigned int strike : 1;
|
||||
unsigned int font : 4; /* 0 to 9 */
|
||||
unsigned int dwl : 1; /* On a DECDWL or DECDHL line */
|
||||
unsigned int dhl : 2; /* On a DECDHL line (1=top 2=bottom) */
|
||||
unsigned int small : 1;
|
||||
unsigned int baseline : 2;
|
||||
} VTermScreenCellAttrs;
|
||||
|
||||
enum {
|
||||
VTERM_UNDERLINE_OFF,
|
||||
VTERM_UNDERLINE_SINGLE,
|
||||
VTERM_UNDERLINE_DOUBLE,
|
||||
VTERM_UNDERLINE_CURLY,
|
||||
};
|
||||
|
||||
enum {
|
||||
VTERM_BASELINE_NORMAL,
|
||||
VTERM_BASELINE_RAISE,
|
||||
VTERM_BASELINE_LOWER,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
schar_T schar;
|
||||
char width;
|
||||
VTermScreenCellAttrs attrs;
|
||||
VTermColor fg, bg;
|
||||
int uri;
|
||||
} VTermScreenCell;
|
||||
|
||||
typedef struct {
|
||||
int (*damage)(VTermRect rect, void *user);
|
||||
int (*moverect)(VTermRect dest, VTermRect src, void *user);
|
||||
int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
|
||||
int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
|
||||
int (*bell)(void *user);
|
||||
int (*resize)(int rows, int cols, void *user);
|
||||
int (*sb_pushline)(int cols, const VTermScreenCell *cells, void *user);
|
||||
int (*sb_popline)(int cols, VTermScreenCell *cells, void *user);
|
||||
int (*sb_clear)(void* user);
|
||||
} VTermScreenCallbacks;
|
||||
|
||||
VTermScreen *vterm_obtain_screen(VTerm *vt);
|
||||
|
||||
void vterm_screen_set_callbacks(VTermScreen *screen, const VTermScreenCallbacks *callbacks, void *user);
|
||||
void *vterm_screen_get_cbdata(VTermScreen *screen);
|
||||
|
||||
void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermStateFallbacks *fallbacks, void *user);
|
||||
void *vterm_screen_get_unrecognised_fbdata(VTermScreen *screen);
|
||||
|
||||
void vterm_screen_enable_reflow(VTermScreen *screen, bool reflow);
|
||||
|
||||
// Back-compat alias for the brief time it was in 0.3-RC1
|
||||
#define vterm_screen_set_reflow vterm_screen_enable_reflow
|
||||
|
||||
void vterm_screen_enable_altscreen(VTermScreen *screen, int altscreen);
|
||||
|
||||
typedef enum {
|
||||
VTERM_DAMAGE_CELL, /* every cell */
|
||||
VTERM_DAMAGE_ROW, /* entire rows */
|
||||
VTERM_DAMAGE_SCREEN, /* entire screen */
|
||||
VTERM_DAMAGE_SCROLL, /* entire screen + scrollrect */
|
||||
|
||||
VTERM_N_DAMAGES
|
||||
} VTermDamageSize;
|
||||
|
||||
void vterm_screen_flush_damage(VTermScreen *screen);
|
||||
void vterm_screen_set_damage_merge(VTermScreen *screen, VTermDamageSize size);
|
||||
|
||||
void vterm_screen_reset(VTermScreen *screen, int hard);
|
||||
|
||||
/* Neither of these functions NUL-terminate the buffer */
|
||||
size_t vterm_screen_get_chars(const VTermScreen *screen, uint32_t *chars, size_t len, const VTermRect rect);
|
||||
size_t vterm_screen_get_text(const VTermScreen *screen, char *str, size_t len, const VTermRect rect);
|
||||
|
||||
typedef enum {
|
||||
VTERM_ATTR_BOLD_MASK = 1 << 0,
|
||||
VTERM_ATTR_UNDERLINE_MASK = 1 << 1,
|
||||
VTERM_ATTR_ITALIC_MASK = 1 << 2,
|
||||
VTERM_ATTR_BLINK_MASK = 1 << 3,
|
||||
VTERM_ATTR_REVERSE_MASK = 1 << 4,
|
||||
VTERM_ATTR_STRIKE_MASK = 1 << 5,
|
||||
VTERM_ATTR_FONT_MASK = 1 << 6,
|
||||
VTERM_ATTR_FOREGROUND_MASK = 1 << 7,
|
||||
VTERM_ATTR_BACKGROUND_MASK = 1 << 8,
|
||||
VTERM_ATTR_CONCEAL_MASK = 1 << 9,
|
||||
VTERM_ATTR_SMALL_MASK = 1 << 10,
|
||||
VTERM_ATTR_BASELINE_MASK = 1 << 11,
|
||||
VTERM_ATTR_URI_MASK = 1 << 12,
|
||||
|
||||
VTERM_ALL_ATTRS_MASK = (1 << 13) - 1
|
||||
} VTermAttrMask;
|
||||
|
||||
int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs);
|
||||
|
||||
int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCell *cell);
|
||||
|
||||
int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos);
|
||||
|
||||
/**
|
||||
* Same as vterm_state_convert_color_to_rgb(), but takes a `screen` instead of a `state`
|
||||
* instance.
|
||||
*/
|
||||
void vterm_screen_convert_color_to_rgb(const VTermScreen *screen, VTermColor *col);
|
||||
|
||||
/**
|
||||
* Similar to vterm_state_set_default_colors(), but also resets colours in the
|
||||
* screen buffer(s)
|
||||
*/
|
||||
void vterm_screen_set_default_colors(VTermScreen *screen, const VTermColor *default_fg, const VTermColor *default_bg);
|
||||
|
||||
// ---------
|
||||
// Utilities
|
||||
// ---------
|
||||
|
||||
VTermValueType vterm_get_attr_type(VTermAttr attr);
|
||||
VTermValueType vterm_get_prop_type(VTermProp prop);
|
||||
|
||||
void vterm_scroll_rect(VTermRect rect,
|
||||
int downward,
|
||||
int rightward,
|
||||
int (*moverect)(VTermRect src, VTermRect dest, void *user),
|
||||
int (*eraserect)(VTermRect rect, int selective, void *user),
|
||||
void *user);
|
||||
|
||||
void vterm_copy_cells(VTermRect dest,
|
||||
VTermRect src,
|
||||
void (*copycell)(VTermPos dest, VTermPos src, void *user),
|
||||
void *user);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,298 +0,0 @@
|
||||
#ifndef __VTERM_INTERNAL_H__
|
||||
#define __VTERM_INTERNAL_H__
|
||||
|
||||
#include "vterm.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include "nvim/mbyte.h"
|
||||
|
||||
#if defined(__GNUC__)
|
||||
# define INTERNAL __attribute__((visibility("internal")))
|
||||
#else
|
||||
# define INTERNAL
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
# define DEBUG_LOG(...) fprintf(stderr, __VA_ARGS__)
|
||||
#else
|
||||
# define DEBUG_LOG(...)
|
||||
#endif
|
||||
|
||||
#define ESC_S "\x1b"
|
||||
|
||||
#define INTERMED_MAX 16
|
||||
|
||||
#define CSI_ARGS_MAX 16
|
||||
#define CSI_LEADER_MAX 16
|
||||
|
||||
#define BUFIDX_PRIMARY 0
|
||||
#define BUFIDX_ALTSCREEN 1
|
||||
|
||||
typedef struct VTermEncoding VTermEncoding;
|
||||
|
||||
typedef struct {
|
||||
VTermEncoding *enc;
|
||||
|
||||
// This size should be increased if required by other stateful encodings
|
||||
char data[4*sizeof(uint32_t)];
|
||||
} VTermEncodingInstance;
|
||||
|
||||
struct VTermPen
|
||||
{
|
||||
VTermColor fg;
|
||||
VTermColor bg;
|
||||
int uri;
|
||||
unsigned int bold:1;
|
||||
unsigned int underline:2;
|
||||
unsigned int italic:1;
|
||||
unsigned int blink:1;
|
||||
unsigned int reverse:1;
|
||||
unsigned int conceal:1;
|
||||
unsigned int strike:1;
|
||||
unsigned int font:4; /* To store 0-9 */
|
||||
unsigned int small:1;
|
||||
unsigned int baseline:2;
|
||||
};
|
||||
|
||||
struct VTermState
|
||||
{
|
||||
VTerm *vt;
|
||||
|
||||
const VTermStateCallbacks *callbacks;
|
||||
void *cbdata;
|
||||
|
||||
const VTermStateFallbacks *fallbacks;
|
||||
void *fbdata;
|
||||
|
||||
int rows;
|
||||
int cols;
|
||||
|
||||
/* Current cursor position */
|
||||
VTermPos pos;
|
||||
|
||||
int at_phantom; /* True if we're on the "81st" phantom column to defer a wraparound */
|
||||
|
||||
int scrollregion_top;
|
||||
int scrollregion_bottom; /* -1 means unbounded */
|
||||
#define SCROLLREGION_BOTTOM(state) ((state)->scrollregion_bottom > -1 ? (state)->scrollregion_bottom : (state)->rows)
|
||||
int scrollregion_left;
|
||||
#define SCROLLREGION_LEFT(state) ((state)->mode.leftrightmargin ? (state)->scrollregion_left : 0)
|
||||
int scrollregion_right; /* -1 means unbounded */
|
||||
#define SCROLLREGION_RIGHT(state) ((state)->mode.leftrightmargin && (state)->scrollregion_right > -1 ? (state)->scrollregion_right : (state)->cols)
|
||||
|
||||
/* Bitvector of tab stops */
|
||||
unsigned char *tabstops;
|
||||
|
||||
/* Primary and Altscreen; lineinfos[1] is lazily allocated as needed */
|
||||
VTermLineInfo *lineinfos[2];
|
||||
|
||||
/* lineinfo will == lineinfos[0] or lineinfos[1], depending on altscreen */
|
||||
VTermLineInfo *lineinfo;
|
||||
#define ROWWIDTH(state,row) ((state)->lineinfo[(row)].doublewidth ? ((state)->cols / 2) : (state)->cols)
|
||||
#define THISROWWIDTH(state) ROWWIDTH(state, (state)->pos.row)
|
||||
|
||||
/* Mouse state */
|
||||
int mouse_col, mouse_row;
|
||||
int mouse_buttons;
|
||||
int mouse_flags;
|
||||
#define MOUSE_WANT_CLICK 0x01
|
||||
#define MOUSE_WANT_DRAG 0x02
|
||||
#define MOUSE_WANT_MOVE 0x04
|
||||
|
||||
enum { MOUSE_X10, MOUSE_UTF8, MOUSE_SGR, MOUSE_RXVT } mouse_protocol;
|
||||
|
||||
/* Last glyph output, for Unicode recombining purposes */
|
||||
char grapheme_buf[MAX_SCHAR_SIZE];
|
||||
size_t grapheme_len;
|
||||
uint32_t grapheme_last; // last added UTF-32 char
|
||||
GraphemeState grapheme_state;
|
||||
int combine_width; // The width of the glyph above
|
||||
VTermPos combine_pos; // Position before movement
|
||||
|
||||
struct {
|
||||
unsigned int keypad:1;
|
||||
unsigned int cursor:1;
|
||||
unsigned int autowrap:1;
|
||||
unsigned int insert:1;
|
||||
unsigned int newline:1;
|
||||
unsigned int cursor_visible:1;
|
||||
unsigned int cursor_blink:1;
|
||||
unsigned int cursor_shape:2;
|
||||
unsigned int alt_screen:1;
|
||||
unsigned int origin:1;
|
||||
unsigned int screen:1;
|
||||
unsigned int leftrightmargin:1;
|
||||
unsigned int bracketpaste:1;
|
||||
unsigned int report_focus:1;
|
||||
} mode;
|
||||
|
||||
VTermEncodingInstance encoding[4], encoding_utf8;
|
||||
int gl_set, gr_set, gsingle_set;
|
||||
|
||||
struct VTermPen pen;
|
||||
|
||||
VTermColor default_fg;
|
||||
VTermColor default_bg;
|
||||
VTermColor colors[16]; // Store the 8 ANSI and the 8 ANSI high-brights only
|
||||
|
||||
int bold_is_highbright;
|
||||
|
||||
unsigned int protected_cell : 1;
|
||||
|
||||
/* Saved state under DEC mode 1048/1049 */
|
||||
struct {
|
||||
VTermPos pos;
|
||||
struct VTermPen pen;
|
||||
|
||||
struct {
|
||||
unsigned int cursor_visible:1;
|
||||
unsigned int cursor_blink:1;
|
||||
unsigned int cursor_shape:2;
|
||||
} mode;
|
||||
} saved;
|
||||
|
||||
/* Temporary state for DECRQSS parsing */
|
||||
union {
|
||||
char decrqss[4];
|
||||
struct {
|
||||
uint16_t mask;
|
||||
enum {
|
||||
SELECTION_INITIAL,
|
||||
SELECTION_SELECTED,
|
||||
SELECTION_QUERY,
|
||||
SELECTION_SET_INITIAL,
|
||||
SELECTION_SET,
|
||||
SELECTION_INVALID,
|
||||
} state : 8;
|
||||
uint32_t recvpartial;
|
||||
uint32_t sendpartial;
|
||||
} selection;
|
||||
} tmp;
|
||||
|
||||
struct {
|
||||
const VTermSelectionCallbacks *callbacks;
|
||||
void *user;
|
||||
char *buffer;
|
||||
size_t buflen;
|
||||
} selection;
|
||||
};
|
||||
|
||||
struct VTerm
|
||||
{
|
||||
const VTermAllocatorFunctions *allocator;
|
||||
void *allocdata;
|
||||
|
||||
int rows;
|
||||
int cols;
|
||||
|
||||
struct {
|
||||
unsigned int utf8:1;
|
||||
unsigned int ctrl8bit:1;
|
||||
} mode;
|
||||
|
||||
struct {
|
||||
enum VTermParserState {
|
||||
NORMAL,
|
||||
CSI_LEADER,
|
||||
CSI_ARGS,
|
||||
CSI_INTERMED,
|
||||
DCS_COMMAND,
|
||||
/* below here are the "string states" */
|
||||
OSC_COMMAND,
|
||||
OSC,
|
||||
DCS,
|
||||
APC,
|
||||
PM,
|
||||
SOS,
|
||||
} state;
|
||||
|
||||
bool in_esc : 1;
|
||||
|
||||
int intermedlen;
|
||||
char intermed[INTERMED_MAX];
|
||||
|
||||
union {
|
||||
struct {
|
||||
int leaderlen;
|
||||
char leader[CSI_LEADER_MAX];
|
||||
|
||||
int argi;
|
||||
long args[CSI_ARGS_MAX];
|
||||
} csi;
|
||||
struct {
|
||||
int command;
|
||||
} osc;
|
||||
struct {
|
||||
int commandlen;
|
||||
char command[CSI_LEADER_MAX];
|
||||
} dcs;
|
||||
} v;
|
||||
|
||||
const VTermParserCallbacks *callbacks;
|
||||
void *cbdata;
|
||||
|
||||
bool string_initial;
|
||||
|
||||
bool emit_nul;
|
||||
} parser;
|
||||
|
||||
/* len == malloc()ed size; cur == number of valid bytes */
|
||||
|
||||
VTermOutputCallback *outfunc;
|
||||
void *outdata;
|
||||
|
||||
char *outbuffer;
|
||||
size_t outbuffer_len;
|
||||
size_t outbuffer_cur;
|
||||
|
||||
char *tmpbuffer;
|
||||
size_t tmpbuffer_len;
|
||||
|
||||
VTermState *state;
|
||||
VTermScreen *screen;
|
||||
};
|
||||
|
||||
struct VTermEncoding {
|
||||
void (*init) (VTermEncoding *enc, void *data);
|
||||
void (*decode)(VTermEncoding *enc, void *data,
|
||||
uint32_t cp[], int *cpi, int cplen,
|
||||
const char bytes[], size_t *pos, size_t len);
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
ENC_UTF8,
|
||||
ENC_SINGLE_94
|
||||
} VTermEncodingType;
|
||||
|
||||
void *vterm_allocator_malloc(VTerm *vt, size_t size);
|
||||
void vterm_allocator_free(VTerm *vt, void *ptr);
|
||||
|
||||
void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len);
|
||||
void vterm_push_output_vsprintf(VTerm *vt, const char *format, va_list args);
|
||||
void vterm_push_output_sprintf(VTerm *vt, const char *format, ...);
|
||||
void vterm_push_output_sprintf_ctrl(VTerm *vt, unsigned char ctrl, const char *fmt, ...);
|
||||
void vterm_push_output_sprintf_str(VTerm *vt, unsigned char ctrl, bool term, const char *fmt, ...);
|
||||
|
||||
void vterm_state_free(VTermState *state);
|
||||
|
||||
void vterm_state_newpen(VTermState *state);
|
||||
void vterm_state_resetpen(VTermState *state);
|
||||
void vterm_state_setpen(VTermState *state, const long args[], int argcount);
|
||||
int vterm_state_getpen(VTermState *state, long args[], int argcount);
|
||||
void vterm_state_savepen(VTermState *state, int save);
|
||||
|
||||
enum {
|
||||
C1_SS3 = 0x8f,
|
||||
C1_DCS = 0x90,
|
||||
C1_CSI = 0x9b,
|
||||
C1_ST = 0x9c,
|
||||
C1_OSC = 0x9d,
|
||||
};
|
||||
|
||||
void vterm_state_push_output_sprintf_CSI(VTermState *vts, const char *format, ...);
|
||||
|
||||
void vterm_screen_free(VTermScreen *screen);
|
||||
|
||||
VTermEncoding *vterm_lookup_encoding(VTermEncodingType type, char designation);
|
||||
|
||||
#endif
|
@ -1,7 +1,11 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "nvim/grid.h"
|
||||
#include "nvim/mbyte.h"
|
||||
|
||||
#include "nvim/vterm/pen.h"
|
||||
#include "nvim/vterm/screen.h"
|
||||
#include "nvim/vterm/vterm_internal_defs.h"
|
||||
#include "vterm_test.h"
|
||||
|
||||
int parser_text(const char bytes[], size_t len, void *user)
|
||||
@ -204,7 +208,8 @@ int selection_query(VTermSelectionMask mask, void *user)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void print_schar(FILE *f, schar_T schar) {
|
||||
static void print_schar(FILE *f, schar_T schar)
|
||||
{
|
||||
char buf[MAX_SCHAR_SIZE];
|
||||
schar_get(buf, schar);
|
||||
StrCharInfo ci = utf_ptr2StrCharInfo(buf);
|
||||
@ -319,6 +324,34 @@ void print_color(const VTermColor *col)
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
static VTermValueType vterm_get_prop_type(VTermProp prop)
|
||||
{
|
||||
switch (prop) {
|
||||
case VTERM_PROP_CURSORVISIBLE:
|
||||
return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_PROP_CURSORBLINK:
|
||||
return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_PROP_ALTSCREEN:
|
||||
return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_PROP_TITLE:
|
||||
return VTERM_VALUETYPE_STRING;
|
||||
case VTERM_PROP_ICONNAME:
|
||||
return VTERM_VALUETYPE_STRING;
|
||||
case VTERM_PROP_REVERSE:
|
||||
return VTERM_VALUETYPE_BOOL;
|
||||
case VTERM_PROP_CURSORSHAPE:
|
||||
return VTERM_VALUETYPE_INT;
|
||||
case VTERM_PROP_MOUSE:
|
||||
return VTERM_VALUETYPE_INT;
|
||||
case VTERM_PROP_FOCUSREPORT:
|
||||
return VTERM_VALUETYPE_BOOL;
|
||||
|
||||
case VTERM_N_PROPS:
|
||||
return 0;
|
||||
}
|
||||
return 0; // UNREACHABLE
|
||||
}
|
||||
|
||||
bool want_state_settermprop;
|
||||
int state_settermprop(VTermProp prop, VTermValue *val, void *user)
|
||||
{
|
||||
@ -463,14 +496,14 @@ int screen_sb_pushline(int cols, const VTermScreenCell *cells, void *user)
|
||||
}
|
||||
|
||||
int eol = cols;
|
||||
while (eol && !cells[eol-1].schar) {
|
||||
while (eol && !cells[eol - 1].schar) {
|
||||
eol--;
|
||||
}
|
||||
|
||||
FILE *f = fopen(VTERM_TEST_FILE, "a");
|
||||
fprintf(f, "sb_pushline %d =", cols);
|
||||
for (int c = 0; c < eol; c++) {
|
||||
fprintf(f, " ");
|
||||
fprintf(f, " ");
|
||||
print_schar(f, cells[c].schar);
|
||||
}
|
||||
fprintf(f, "\n");
|
||||
@ -488,7 +521,7 @@ int screen_sb_popline(int cols, VTermScreenCell *cells, void *user)
|
||||
|
||||
// All lines of scrollback contain "ABCDE"
|
||||
for (int col = 0; col < cols; col++) {
|
||||
if(col < 5) {
|
||||
if (col < 5) {
|
||||
cells[col].schar = schar_from_ascii((uint32_t)('A' + col));
|
||||
} else {
|
||||
cells[col].schar = 0;
|
||||
@ -524,3 +557,231 @@ void term_output(const char *s, size_t len, void *user)
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val)
|
||||
{
|
||||
switch (attr) {
|
||||
case VTERM_ATTR_BOLD:
|
||||
val->boolean = state->pen.bold;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_UNDERLINE:
|
||||
val->number = state->pen.underline;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_ITALIC:
|
||||
val->boolean = state->pen.italic;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_BLINK:
|
||||
val->boolean = state->pen.blink;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_REVERSE:
|
||||
val->boolean = state->pen.reverse;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_CONCEAL:
|
||||
val->boolean = state->pen.conceal;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_STRIKE:
|
||||
val->boolean = state->pen.strike;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_FONT:
|
||||
val->number = state->pen.font;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_FOREGROUND:
|
||||
val->color = state->pen.fg;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_BACKGROUND:
|
||||
val->color = state->pen.bg;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_SMALL:
|
||||
val->boolean = state->pen.small;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_BASELINE:
|
||||
val->number = state->pen.baseline;
|
||||
return 1;
|
||||
|
||||
case VTERM_ATTR_URI:
|
||||
val->number = state->pen.uri;
|
||||
return 1;
|
||||
|
||||
case VTERM_N_ATTRS:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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_CONCEAL_MASK) && (a->pen.conceal != b->pen.conceal)) {
|
||||
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;
|
||||
}
|
||||
if ((attrs & VTERM_ATTR_SMALL_MASK) && (a->pen.small != b->pen.small)) {
|
||||
return 1;
|
||||
}
|
||||
if ((attrs & VTERM_ATTR_BASELINE_MASK) && (a->pen.baseline != b->pen.baseline)) {
|
||||
return 1;
|
||||
}
|
||||
if ((attrs & VTERM_ATTR_URI_MASK) && (a->pen.uri != b->pen.uri)) {
|
||||
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(vterm): 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;
|
||||
}
|
||||
|
||||
/// Does not NUL-terminate the buffer
|
||||
size_t vterm_screen_get_text(const VTermScreen *screen, char *buffer, size_t len,
|
||||
const VTermRect rect)
|
||||
{
|
||||
size_t outpos = 0;
|
||||
int padding = 0;
|
||||
|
||||
#define PUT(bytes, thislen) \
|
||||
if (true) { \
|
||||
if (buffer && outpos + thislen <= len) \
|
||||
memcpy((char *)buffer + outpos, bytes, thislen); \
|
||||
outpos += thislen; \
|
||||
} \
|
||||
|
||||
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->schar == 0) {
|
||||
// Erased cell, might need a space
|
||||
padding++;
|
||||
} else if (cell->schar == (uint32_t)-1) {
|
||||
// Gap behind a double-width char, do nothing
|
||||
} else {
|
||||
while (padding) {
|
||||
PUT(" ", 1);
|
||||
padding--;
|
||||
}
|
||||
char buf[MAX_SCHAR_SIZE + 1];
|
||||
size_t thislen = schar_get(buf, cell->schar);
|
||||
PUT(buf, thislen);
|
||||
}
|
||||
}
|
||||
|
||||
if (row < rect.end_row - 1) {
|
||||
PUT("\n", 1);
|
||||
padding = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return outpos;
|
||||
}
|
||||
|
||||
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->schar != 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos)
|
||||
{
|
||||
*cursorpos = state->pos;
|
||||
}
|
||||
|
||||
void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright)
|
||||
{
|
||||
state->bold_is_highbright = bold_is_highbright;
|
||||
}
|
||||
|
||||
/// Compares two colours. Returns true if the colors are equal, false otherwise.
|
||||
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;
|
||||
}
|
||||
|
@ -2,8 +2,17 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "nvim/macros_defs.h"
|
||||
#include "vterm/vterm.h"
|
||||
#include "nvim/vterm/vterm.h"
|
||||
|
||||
EXTERN VTermPos state_pos;
|
||||
EXTERN bool want_state_putglyph INIT (=false);
|
||||
EXTERN bool want_state_movecursor INIT(= false);
|
||||
EXTERN bool want_state_erase INIT(= false);
|
||||
EXTERN bool want_state_scrollrect INIT(= false);
|
||||
EXTERN bool want_state_moverect INIT(= false);
|
||||
EXTERN bool want_state_settermprop INIT(= false);
|
||||
EXTERN bool want_state_scrollback INIT(= false);
|
||||
EXTERN bool want_screen_scrollback INIT(= false);
|
||||
int parser_text(const char bytes[], size_t len, void *user);
|
||||
int parser_csi(const char *leader, const long args[], int argcount, const char *intermed,
|
||||
char command, void *user);
|
||||
@ -27,12 +36,10 @@ int screen_sb_pushline(int cols, const VTermScreenCell *cells, void *user);
|
||||
int screen_sb_popline(int cols, VTermScreenCell *cells, void *user);
|
||||
int screen_sb_clear(void *user);
|
||||
void term_output(const char *s, size_t len, void *user);
|
||||
EXTERN VTermPos state_pos;
|
||||
EXTERN bool want_state_putglyph INIT (=false);
|
||||
EXTERN bool want_state_movecursor INIT(= false);
|
||||
EXTERN bool want_state_erase INIT(= false);
|
||||
EXTERN bool want_state_scrollrect INIT(= false);
|
||||
EXTERN bool want_state_moverect INIT(= false);
|
||||
EXTERN bool want_state_settermprop INIT(= false);
|
||||
EXTERN bool want_state_scrollback INIT(= false);
|
||||
EXTERN bool want_screen_scrollback INIT(= false);
|
||||
int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val);
|
||||
int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs);
|
||||
size_t vterm_screen_get_text(const VTermScreen *screen, char *buffer, size_t len, VTermRect rect);
|
||||
int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos);
|
||||
void vterm_state_get_cursorpos(const VTermState *state, VTermPos *cursorpos);
|
||||
void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright);
|
||||
int vterm_color_is_equal(const VTermColor *a, const VTermColor *b);
|
||||
|
@ -61,7 +61,6 @@ local bit = require('bit')
|
||||
--- @field vterm_screen_enable_reflow function
|
||||
--- @field vterm_screen_get_attrs_extent function
|
||||
--- @field vterm_screen_get_cell function
|
||||
--- @field vterm_screen_get_chars fun(any, any, any, any):any
|
||||
--- @field vterm_screen_get_text fun(any, any, any, any):any
|
||||
--- @field vterm_screen_is_eol fun(any, any):any
|
||||
--- @field vterm_screen_reset function
|
||||
@ -79,10 +78,17 @@ local bit = require('bit')
|
||||
--- @field vterm_state_set_selection_callbacks function
|
||||
--- @field vterm_state_set_unrecognised_fallbacks function
|
||||
local vterm = t.cimport(
|
||||
'./src/nvim/mbyte.h',
|
||||
'./src/nvim/grid.h',
|
||||
'./src/vterm/vterm.h',
|
||||
'./src/vterm/vterm_internal.h',
|
||||
'./src/nvim/mbyte.h',
|
||||
'./src/nvim/vterm/encoding.h',
|
||||
'./src/nvim/vterm/keyboard.h',
|
||||
'./src/nvim/vterm/mouse.h',
|
||||
'./src/nvim/vterm/parser.h',
|
||||
'./src/nvim/vterm/pen.h',
|
||||
'./src/nvim/vterm/screen.h',
|
||||
'./src/nvim/vterm/state.h',
|
||||
'./src/nvim/vterm/vterm.h',
|
||||
'./src/nvim/vterm/vterm_internal.h',
|
||||
'./test/unit/fixtures/vterm_test.h'
|
||||
)
|
||||
|
||||
|
Reference in New Issue
Block a user