# HG changeset patch # User Christian Brabandt # Date 1521988206 -7200 # Node ID 9f857e6310b655e370708b0ca3440dd4975c54cc # Parent 28031e9f0da4a3b0b58644eee51e6f8b4f700085 patch 8.0.1639: libvterm code lags behind master commit https://github.com/vim/vim/commit/b5b49a3b430ea0aaf8cce6b7bc0e444f0211ddfb Author: Bram Moolenaar Date: Sun Mar 25 16:20:37 2018 +0200 patch 8.0.1639: libvterm code lags behind master Problem: Libvterm code lags behind master. Solution: Sync to head, solve merge problems. diff --git a/src/libvterm/README b/src/libvterm/README --- a/src/libvterm/README +++ b/src/libvterm/README @@ -10,3 +10,21 @@ Modifications: - Add a .gitignore file. - Convert from C99 to C90. - Other changes to support embedding in Vim. + + +To merge in changes from Github, do this: +- Commit any pending changes. +- Setup the merge tool: + git config merge.tool vimdiff + git config merge.conflictstyle diff3 + git config mergetool.prompt false +- Run the merge tool: + git mergetool + This will open a four-way diff between: + LOCAL - your current version + BASE - version as it was at your last sync + REMOTE - version at head on Github + MERGED - best-effort merge of LOCAL and REMOTE + Now find places where automatic merge didn't work, they are marked with + <<<<<<<<, ======= and >>>>>>> + Fix those places in MERGED, remove the markers, and save the file :wqall. diff --git a/src/libvterm/bin/unterm.c b/src/libvterm/bin/unterm.c --- a/src/libvterm/bin/unterm.c +++ b/src/libvterm/bin/unterm.c @@ -95,8 +95,8 @@ static void dump_cell(const VTermScreenC sgr[sgri++] = 90 + (index - 8); else { sgr[sgri++] = 38; - sgr[sgri++] = 5 | (1<<31); - sgr[sgri++] = index | (1<<31); + sgr[sgri++] = 5 | CSI_ARG_FLAG_MORE; + sgr[sgri++] = index | CSI_ARG_FLAG_MORE; } } @@ -112,8 +112,8 @@ static void dump_cell(const VTermScreenC sgr[sgri++] = 100 + (index - 8); else { sgr[sgri++] = 48; - sgr[sgri++] = 5 | (1<<31); - sgr[sgri++] = index | (1<<31); + sgr[sgri++] = 5 | CSI_ARG_FLAG_MORE; + sgr[sgri++] = index | CSI_ARG_FLAG_MORE; } } @@ -125,9 +125,9 @@ static void dump_cell(const VTermScreenC int i; for(i = 0; i < sgri; i++) printf(!i ? "%d" : - sgr[i] & (1<<31) ? ":%d" : + CSI_ARG_HAS_MORE(sgr[i]) ? ":%d" : ";%d", - sgr[i] & ~(1<<31)); + CSI_ARG(sgr[i])); } printf("m"); } @@ -283,5 +283,6 @@ int main(int argc, char *argv[]) close(fd); vterm_free(vt); + return 0; } diff --git a/src/libvterm/bin/vterm-ctrl.c b/src/libvterm/bin/vterm-ctrl.c --- a/src/libvterm/bin/vterm-ctrl.c +++ b/src/libvterm/bin/vterm-ctrl.c @@ -53,6 +53,7 @@ static char *helptext[] = { "curblink [off|on|query]", "curshape [block|under|bar|query]", "mouse [off|click|clickdrag|motion]", + "reportfocus [off|on|query]", "altscreen [off|on|query]", "bracketpaste [off|on|query]", "icontitle [STR]", @@ -81,9 +82,9 @@ static int seticanon(int icanon, int ech return ret; } -static void await_c1(int c1) +static void await_c1(unsigned char c1) { - int c; + unsigned char c; /* await CSI - 8bit or 2byte 7bit form */ int in_esc = FALSE; @@ -340,6 +341,9 @@ int main(int argc, char *argv[]) printf("\x1b[?1003h"); break; } } + else if(streq(arg, "reportfocus")) { + do_dec_mode(1004, getboolq(&argi, argc, argv), "reportfocus"); + } else if(streq(arg, "altscreen")) { do_dec_mode(1049, getboolq(&argi, argc, argv), "altscreen"); } diff --git a/src/libvterm/bin/vterm-dump.c b/src/libvterm/bin/vterm-dump.c --- a/src/libvterm/bin/vterm-dump.c +++ b/src/libvterm/bin/vterm-dump.c @@ -227,5 +227,6 @@ int main(int argc, char *argv[]) close(fd); vterm_free(vt); + return 0; } diff --git a/src/libvterm/doc/URLs b/src/libvterm/doc/URLs --- a/src/libvterm/doc/URLs +++ b/src/libvterm/doc/URLs @@ -9,3 +9,6 @@ Digital VT100 User Guide: Digital VT220 Programmer Reference Manual http://vt100.net/docs/vt220-rm/ + +Summary of ANSI standards for ASCII terminals + http://www.inwap.com/pdp10/ansicode.txt diff --git a/src/libvterm/doc/seqs.txt b/src/libvterm/doc/seqs.txt --- a/src/libvterm/doc/seqs.txt +++ b/src/libvterm/doc/seqs.txt @@ -151,6 +151,7 @@ 123 DECSM 7 = DECAWM, autowra DECSM 1000 = Mouse click/release tracking DECSM 1002 = Mouse click/release/drag tracking DECSM 1003 = Mouse all movements tracking + DECSM 1004 = Focus in/out reporting DECSM 1005 = Mouse protocol extended (UTF-8) - not recommended DECSM 1006 = Mouse protocol SGR DECSM 1015 = Mouse protocol rxvt diff --git a/src/libvterm/include/vterm.h b/src/libvterm/include/vterm.h --- a/src/libvterm/include/vterm.h +++ b/src/libvterm/include/vterm.h @@ -96,7 +96,9 @@ typedef enum { VTERM_VALUETYPE_BOOL = 1, VTERM_VALUETYPE_INT, VTERM_VALUETYPE_STRING, - VTERM_VALUETYPE_COLOR + VTERM_VALUETYPE_COLOR, + + VTERM_N_VALUETYPES } VTermValueType; typedef union { @@ -116,7 +118,9 @@ typedef enum { 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_BACKGROUND, /* color: 40-49 100-107 */ + + VTERM_N_ATTRS } VTermAttr; typedef enum { @@ -129,20 +133,26 @@ typedef enum { VTERM_PROP_REVERSE, /* bool */ VTERM_PROP_CURSORSHAPE, /* number */ VTERM_PROP_MOUSE, /* number */ - VTERM_PROP_CURSORCOLOR /* string */ + VTERM_PROP_CURSORCOLOR, /* string */ + + VTERM_N_PROPS } VTermProp; enum { VTERM_PROP_CURSORSHAPE_BLOCK = 1, VTERM_PROP_CURSORSHAPE_UNDERLINE, - VTERM_PROP_CURSORSHAPE_BAR_LEFT + 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_PROP_MOUSE_MOVE, + + VTERM_N_PROP_MOUSES }; typedef struct { @@ -213,8 +223,8 @@ void vterm_mouse_button(VTerm *vt, int b * * Don't confuse this with the final byte of the CSI escape; 'a' in this case. */ -#define CSI_ARG_FLAG_MORE (1<<30) -#define CSI_ARG_MASK (~(1<<30)) +#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) @@ -293,6 +303,8 @@ void vterm_state_set_palette_color(VTerm void vterm_state_set_bold_highbright(VTermState *state, int bold_is_highbright); int vterm_state_get_penattr(const VTermState *state, VTermAttr attr, VTermValue *val); int vterm_state_set_termprop(VTermState *state, VTermProp prop, VTermValue *val); +void vterm_state_focus_in(VTermState *state); +void vterm_state_focus_out(VTermState *state); const VTermLineInfo *vterm_state_get_lineinfo(const VTermState *state, int row); /* ------------ @@ -357,7 +369,9 @@ typedef enum { VTERM_DAMAGE_CELL, /* every cell */ VTERM_DAMAGE_ROW, /* entire rows */ VTERM_DAMAGE_SCREEN, /* entire screen */ - VTERM_DAMAGE_SCROLL /* entire screen + scrollrect */ + VTERM_DAMAGE_SCROLL, /* entire screen + scrollrect */ + + VTERM_N_DAMAGES } VTermDamageSize; /* Invoke the relevant callbacks to update the screen. */ @@ -384,7 +398,9 @@ typedef enum { 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_BACKGROUND_MASK = 1 << 8, + + VTERM_ALL_ATTRS_MASK = (1 << 9) - 1 } VTermAttrMask; int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs); diff --git a/src/libvterm/include/vterm_keycodes.h b/src/libvterm/include/vterm_keycodes.h --- a/src/libvterm/include/vterm_keycodes.h +++ b/src/libvterm/include/vterm_keycodes.h @@ -5,7 +5,9 @@ typedef enum { VTERM_MOD_NONE = 0x00, VTERM_MOD_SHIFT = 0x01, VTERM_MOD_ALT = 0x02, - VTERM_MOD_CTRL = 0x04 + VTERM_MOD_CTRL = 0x04, + + VTERM_ALL_MODS_MASK = 0x07 } VTermModifier; /* The order here must match keycodes[] in src/keyboard.c! */ @@ -53,7 +55,8 @@ typedef enum { VTERM_KEY_KP_ENTER, VTERM_KEY_KP_EQUAL, - VTERM_KEY_MAX /* Must be last */ + VTERM_KEY_MAX, /* Must be last */ + VTERM_N_KEYS = VTERM_KEY_MAX } VTermKey; #define VTERM_KEY_FUNCTION(n) (VTERM_KEY_FUNCTION_0+(n)) diff --git a/src/libvterm/src/mouse.c b/src/libvterm/src/mouse.c --- a/src/libvterm/src/mouse.c +++ b/src/libvterm/src/mouse.c @@ -63,9 +63,9 @@ void vterm_mouse_move(VTerm *vt, int 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; + int button = state->mouse_buttons & MOUSE_BUTTON_LEFT ? 1 : + state->mouse_buttons & MOUSE_BUTTON_MIDDLE ? 2 : + state->mouse_buttons & MOUSE_BUTTON_RIGHT ? 3 : 4; output_mouse(state, button-1 + 0x20, 1, mod, col, row); } } diff --git a/src/libvterm/src/parser.c b/src/libvterm/src/parser.c --- a/src/libvterm/src/parser.c +++ b/src/libvterm/src/parser.c @@ -3,188 +3,123 @@ #include #include -#define CSI_ARGS_MAX 16 -#define CSI_LEADER_MAX 16 -#define CSI_INTERMED_MAX 16 +#undef DEBUG_PARSER + +static int 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->cbdata)) + if(vt->parser.callbacks && vt->parser.callbacks->control) + if((*vt->parser.callbacks->control)(control, vt->parser.cbdata)) return; DEBUG_LOG1("libvterm: Unhandled control 0x%02x\n", control); } -static void do_string_csi(VTerm *vt, const char *args, size_t arglen, char command) +static void do_csi(VTerm *vt, char command) { - int i = 0; - - int leaderlen = 0; - char leader[CSI_LEADER_MAX]; - int argcount = 1; /* Always at least 1 arg */ - long csi_args[CSI_ARGS_MAX]; - int argi; - int intermedlen = 0; - char intermed[CSI_INTERMED_MAX]; - - /* Extract leader bytes 0x3c to 0x3f */ - for( ; i < (int)arglen; i++) { - if(args[i] < 0x3c || args[i] > 0x3f) - break; - if(leaderlen < CSI_LEADER_MAX-1) - leader[leaderlen++] = args[i]; - } - - leader[leaderlen] = 0; - - for( ; i < (int)arglen; i++) - if(args[i] == 0x3b || args[i] == 0x3a) /* ; or : */ - argcount++; - - /* TODO: Consider if these buffers should live in the VTerm struct itself */ - if(argcount > CSI_ARGS_MAX) - argcount = CSI_ARGS_MAX; - - for(argi = 0; argi < argcount; argi++) - csi_args[argi] = CSI_ARG_MISSING; - - argi = 0; - for(i = leaderlen; i < (int)arglen && argi < argcount; i++) { - switch(args[i]) { - case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: - case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: - if(csi_args[argi] == CSI_ARG_MISSING) - csi_args[argi] = 0; - csi_args[argi] *= 10; - csi_args[argi] += args[i] - '0'; - break; - case 0x3a: - csi_args[argi] |= CSI_ARG_FLAG_MORE; - /* FALLTHROUGH */ - case 0x3b: - argi++; - break; - default: - goto done_leader; - } - } -done_leader: ; - - for( ; i < (int)arglen; i++) { - if((args[i] & 0xf0) != 0x20) - break; - - if(intermedlen < CSI_INTERMED_MAX-1) - intermed[intermedlen++] = args[i]; - } - - intermed[intermedlen] = 0; - - if(i < (int)arglen) { - DEBUG_LOG2("libvterm: TODO unhandled CSI bytes \"%.*s\"\n", (int)(arglen - i), args + i); - } - -#if 0 - printf("Parsed CSI args %.*s as:\n", arglen, args); - printf(" leader: %s\n", leader); - for(argi = 0; argi < argcount; argi++) { - printf(" %lu", CSI_ARG(csi_args[argi])); - if(!CSI_ARG_HAS_MORE(csi_args[argi])) +#ifdef DEBUG_PARSER + printf("Parsed CSI args as:\n", arglen, args); + printf(" leader: %s\n", vt->parser.csi_leader); + for(int argi = 0; argi < vt->parser.csi_argi; argi++) { + printf(" %lu", CSI_ARG(vt->parser.csi_args[argi])); + if(!CSI_ARG_HAS_MORE(vt->parser.csi_args[argi])) printf("\n"); - printf(" intermed: %s\n", intermed); + printf(" intermed: %s\n", vt->parser.intermed); } #endif - if(vt->parser_callbacks && vt->parser_callbacks->csi) - if((*vt->parser_callbacks->csi)(leaderlen ? leader : NULL, csi_args, argcount, intermedlen ? intermed : NULL, command, vt->cbdata)) + if(vt->parser.callbacks && vt->parser.callbacks->csi) + if((*vt->parser.callbacks->csi)( + vt->parser.csi_leaderlen ? vt->parser.csi_leader : NULL, + vt->parser.csi_args, + vt->parser.csi_argi, + vt->parser.intermedlen ? vt->parser.intermed : NULL, + command, + vt->parser.cbdata)) return; - DEBUG_LOG3("libvterm: Unhandled CSI %.*s %c\n", (int)arglen, args, command); + DEBUG_LOG1("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_LOG1("libvterm: Unhandled escape ESC 0x%02x\n", command); } static void append_strbuffer(VTerm *vt, const char *str, size_t len) { - if(len > vt->strbuffer_len - vt->strbuffer_cur) { - len = vt->strbuffer_len - vt->strbuffer_cur; + if(len > vt->parser.strbuffer_len - vt->parser.strbuffer_cur) { + len = vt->parser.strbuffer_len - vt->parser.strbuffer_cur; DEBUG_LOG1("Truncating strbuffer preserve to %zd bytes\n", len); } if(len > 0) { - strncpy(vt->strbuffer + vt->strbuffer_cur, str, len); - vt->strbuffer_cur += len; + strncpy(vt->parser.strbuffer + vt->parser.strbuffer_cur, str, len); + vt->parser.strbuffer_cur += len; } } -static size_t do_string(VTerm *vt, const char *str_frag, size_t len) +static void start_string(VTerm *vt, VTermParserStringType type) { - size_t eaten; + vt->parser.stringtype = type; + + vt->parser.strbuffer_cur = 0; +} - if(vt->strbuffer_cur) { - if(str_frag) - append_strbuffer(vt, str_frag, len); +static void more_string(VTerm *vt, const char *str, size_t len) +{ + append_strbuffer(vt, str, len); +} - str_frag = vt->strbuffer; - len = vt->strbuffer_cur; +static void done_string(VTerm *vt, const char *str, size_t len) +{ + if(vt->parser.strbuffer_cur) { + if(str) + append_strbuffer(vt, str, len); + + str = vt->parser.strbuffer; + len = vt->parser.strbuffer_cur; } - else if(!str_frag) { + else if(!str) { DEBUG_LOG("parser.c: TODO: No strbuffer _and_ no final fragment???\n"); len = 0; } - vt->strbuffer_cur = 0; - - switch(vt->parser_state) { - case NORMAL: - if(vt->parser_callbacks && vt->parser_callbacks->text) - if((eaten = (*vt->parser_callbacks->text)(str_frag, len, vt->cbdata))) - return eaten; - - DEBUG_LOG1("libvterm: Unhandled text (%zu chars)\n", len); - return 0; + switch(vt->parser.stringtype) { + case VTERM_PARSER_OSC: + if(vt->parser.callbacks && vt->parser.callbacks->osc) + if((*vt->parser.callbacks->osc)(str, len, vt->parser.cbdata)) + return; - case ESC: - if(len == 1 && str_frag[0] >= 0x40 && str_frag[0] < 0x60) { - /* C1 emulations using 7bit clean */ - /* ESC 0x40 == 0x80 */ - do_control(vt, str_frag[0] + 0x40); - return 0; - } - - if(vt->parser_callbacks && vt->parser_callbacks->escape) - if((*vt->parser_callbacks->escape)(str_frag, len, vt->cbdata)) - return 0; - - DEBUG_LOG1("libvterm: Unhandled escape ESC 0x%02x\n", str_frag[len-1]); - return 0; + DEBUG_LOG2("libvterm: Unhandled OSC %.*s\n", (int)len, str); + return; - case CSI: - do_string_csi(vt, str_frag, len - 1, str_frag[len - 1]); - return 0; - - case OSC: - if(vt->parser_callbacks && vt->parser_callbacks->osc) - if((*vt->parser_callbacks->osc)(str_frag, len, vt->cbdata)) - return 0; - - DEBUG_LOG2("libvterm: Unhandled OSC %.*s\n", (int)len, str_frag); - return 0; + case VTERM_PARSER_DCS: + if(vt->parser.callbacks && vt->parser.callbacks->dcs) + if((*vt->parser.callbacks->dcs)(str, len, vt->parser.cbdata)) + return; - case DCS: - if(vt->parser_callbacks && vt->parser_callbacks->dcs) - if((*vt->parser_callbacks->dcs)(str_frag, len, vt->cbdata)) - return 0; - - DEBUG_LOG2("libvterm: Unhandled DCS %.*s\n", (int)len, str_frag); - return 0; + DEBUG_LOG2("libvterm: Unhandled DCS %.*s\n", (int)len, str); + return; - case ESC_IN_OSC: - case ESC_IN_DCS: - DEBUG_LOG("libvterm: ARGH! Should never do_string() in ESC_IN_{OSC,DCS}\n"); - return 0; + case VTERM_N_PARSER_TYPES: + return; } - - return 0; } size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len) @@ -192,29 +127,30 @@ size_t vterm_input_write(VTerm *vt, cons size_t pos = 0; const char *string_start = NULL; /* init to avoid gcc warning */ - switch(vt->parser_state) { + switch(vt->parser.state) { case NORMAL: + case CSI_LEADER: + case CSI_ARGS: + case CSI_INTERMED: + case ESC: string_start = NULL; break; - case ESC: - case ESC_IN_OSC: - case ESC_IN_DCS: - case CSI: - case OSC: - case DCS: + case STRING: + case ESC_IN_STRING: string_start = bytes; break; } -#define ENTER_STRING_STATE(st) do { vt->parser_state = st; string_start = bytes + pos + 1; } while(0) -#define ENTER_NORMAL_STATE() do { vt->parser_state = NORMAL; string_start = NULL; } while(0) +#define ENTER_STRING_STATE(st) do { vt->parser.state = STRING; string_start = bytes + pos + 1; } while(0) +#define ENTER_STATE(st) do { vt->parser.state = st; string_start = NULL; } while(0) +#define ENTER_NORMAL_STATE() ENTER_STATE(NORMAL) for( ; pos < len; pos++) { unsigned char c = bytes[pos]; if(c == 0x00 || c == 0x7f) { /* NUL, DEL */ - if(vt->parser_state != NORMAL) { - append_strbuffer(vt, string_start, bytes + pos - string_start); + if(vt->parser.state >= STRING) { + more_string(vt, string_start, bytes + pos - string_start); string_start = bytes + pos + 1; } continue; @@ -224,64 +160,64 @@ size_t vterm_input_write(VTerm *vt, cons continue; } else if(c == 0x1b) { /* ESC */ - if(vt->parser_state == OSC) - vt->parser_state = ESC_IN_OSC; - else if(vt->parser_state == DCS) - vt->parser_state = ESC_IN_DCS; + vt->parser.intermedlen = 0; + if(vt->parser.state == STRING) + vt->parser.state = ESC_IN_STRING; else - ENTER_STRING_STATE(ESC); + ENTER_STATE(ESC); continue; } else if(c == 0x07 && /* BEL, can stand for ST in OSC or DCS state */ - (vt->parser_state == OSC || vt->parser_state == DCS)) { + vt->parser.state == STRING) { /* fallthrough */ } else if(c < 0x20) { /* other C0 */ - if(vt->parser_state != NORMAL) - append_strbuffer(vt, string_start, bytes + pos - string_start); + if(vt->parser.state >= STRING) + more_string(vt, string_start, bytes + pos - string_start); do_control(vt, c); - if(vt->parser_state != NORMAL) + if(vt->parser.state >= STRING) string_start = bytes + pos + 1; continue; } /* else fallthrough */ - switch(vt->parser_state) { - case ESC_IN_OSC: - case ESC_IN_DCS: + switch(vt->parser.state) { + case ESC_IN_STRING: if(c == 0x5c) { /* ST */ - switch(vt->parser_state) { - case ESC_IN_OSC: vt->parser_state = OSC; break; - case ESC_IN_DCS: vt->parser_state = DCS; break; - default: break; - } - do_string(vt, string_start, bytes + pos - string_start - 1); + vt->parser.state = STRING; + done_string(vt, string_start, bytes + pos - string_start - 1); ENTER_NORMAL_STATE(); break; } - vt->parser_state = ESC; - string_start = bytes + pos; + vt->parser.state = ESC; /* else fallthrough */ case ESC: switch(c) { case 0x50: /* DCS */ - ENTER_STRING_STATE(DCS); + start_string(vt, VTERM_PARSER_DCS); + ENTER_STRING_STATE(); break; case 0x5b: /* CSI */ - ENTER_STRING_STATE(CSI); + vt->parser.csi_leaderlen = 0; + ENTER_STATE(CSI_LEADER); break; case 0x5d: /* OSC */ - ENTER_STRING_STATE(OSC); + start_string(vt, VTERM_PARSER_OSC); + ENTER_STRING_STATE(); break; default: - if(c >= 0x30 && c < 0x7f) { - /* +1 to pos because we want to include this command byte as well */ - do_string(vt, string_start, bytes + pos - string_start + 1); + if(is_intermed(c)) { + if(vt->parser.intermedlen < INTERMED_MAX-1) + vt->parser.intermed[vt->parser.intermedlen++] = c; + } + else if(!vt->parser.intermedlen && c >= 0x40 && c < 0x60) { + do_control(vt, c + 0x40); ENTER_NORMAL_STATE(); } - else if(c >= 0x20 && c < 0x30) { - /* intermediate byte */ + else if(c >= 0x30 && c < 0x7f) { + do_escape(vt, c); + ENTER_NORMAL_STATE(); } else { DEBUG_LOG1("TODO: Unhandled byte %02x in Escape\n", c); @@ -289,18 +225,67 @@ size_t vterm_input_write(VTerm *vt, cons } break; - case CSI: - if(c >= 0x40 && c <= 0x7f) { - /* +1 to pos because we want to include this command byte as well */ - do_string(vt, string_start, bytes + pos - string_start + 1); - ENTER_NORMAL_STATE(); + case CSI_LEADER: + /* Extract leader bytes 0x3c to 0x3f */ + if(c >= 0x3c && c <= 0x3f) { + if(vt->parser.csi_leaderlen < CSI_LEADER_MAX-1) + vt->parser.csi_leader[vt->parser.csi_leaderlen++] = c; + break; + } + + /* else fallthrough */ + vt->parser.csi_leader[vt->parser.csi_leaderlen] = 0; + + vt->parser.csi_argi = 0; + vt->parser.csi_args[0] = CSI_ARG_MISSING; + vt->parser.state = CSI_ARGS; + + /* fallthrough */ + case CSI_ARGS: + /* Numerical value of argument */ + if(c >= '0' && c <= '9') { + if(vt->parser.csi_args[vt->parser.csi_argi] == CSI_ARG_MISSING) + vt->parser.csi_args[vt->parser.csi_argi] = 0; + vt->parser.csi_args[vt->parser.csi_argi] *= 10; + vt->parser.csi_args[vt->parser.csi_argi] += c - '0'; + break; } + if(c == ':') { + vt->parser.csi_args[vt->parser.csi_argi] |= CSI_ARG_FLAG_MORE; + c = ';'; + } + if(c == ';') { + vt->parser.csi_argi++; + vt->parser.csi_args[vt->parser.csi_argi] = CSI_ARG_MISSING; + break; + } + + /* else fallthrough */ + vt->parser.csi_argi++; + vt->parser.intermedlen = 0; + vt->parser.state = CSI_INTERMED; + /* fallthrough */ + 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: - case DCS: + case STRING: if(c == 0x07 || (c == 0x9c && !vt->mode.utf8)) { - do_string(vt, string_start, bytes + pos - string_start); + done_string(vt, string_start, bytes + pos - string_start); ENTER_NORMAL_STATE(); } break; @@ -309,13 +294,15 @@ size_t vterm_input_write(VTerm *vt, cons if(c >= 0x80 && c < 0xa0 && !vt->mode.utf8) { switch(c) { case 0x90: /* DCS */ - ENTER_STRING_STATE(DCS); + start_string(vt, VTERM_PARSER_DCS); + ENTER_STRING_STATE(); break; case 0x9b: /* CSI */ - ENTER_STRING_STATE(CSI); + ENTER_STATE(CSI_LEADER); break; case 0x9d: /* OSC */ - ENTER_STRING_STATE(OSC); + start_string(vt, VTERM_PARSER_OSC); + ENTER_STRING_STATE(); break; default: do_control(vt, c); @@ -323,24 +310,32 @@ size_t vterm_input_write(VTerm *vt, cons } } else { - size_t text_eaten = do_string(vt, bytes + pos, len - pos); + 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(text_eaten == 0) { - string_start = bytes + pos; - goto pause; + if(!eaten) { + DEBUG_LOG("libvterm: Text callback did not consume any input\n"); + /* force it to make progress */ + eaten = 1; } - pos += (text_eaten - 1); /* we'll ++ it again in a moment */ + pos += (eaten - 1); /* we'll ++ it again in a moment */ } break; } } -pause: - if(string_start && string_start < len + bytes) { - size_t remaining = len - (string_start - bytes); - append_strbuffer(vt, string_start, remaining); - } - return len; } + +void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user) +{ + vt->parser.callbacks = callbacks; + vt->parser.cbdata = user; +} + +void *vterm_parser_get_cbdata(VTerm *vt) +{ + return vt->parser.cbdata; +} diff --git a/src/libvterm/src/pen.c b/src/libvterm/src/pen.c --- a/src/libvterm/src/pen.c +++ b/src/libvterm/src/pen.c @@ -507,6 +507,9 @@ int vterm_state_get_penattr(const VTermS case VTERM_ATTR_BACKGROUND: val->color = state->pen.bg; return 1; + + case VTERM_N_ATTRS: + return 0; } return 0; diff --git a/src/libvterm/src/screen.c b/src/libvterm/src/screen.c --- a/src/libvterm/src/screen.c +++ b/src/libvterm/src/screen.c @@ -429,6 +429,9 @@ static int setpenattr(VTermAttr attr, VT case VTERM_ATTR_BACKGROUND: screen->pen.bg = val->color; return 1; + + case VTERM_N_ATTRS: + return 0; } return 0; diff --git a/src/libvterm/src/state.c b/src/libvterm/src/state.c --- a/src/libvterm/src/state.c +++ b/src/libvterm/src/state.c @@ -268,7 +268,7 @@ static int on_text(const char bytes[], s if(!npoints) { vterm_allocator_free(state->vt, codepoints); - return 0; + return eaten; } if(state->gsingle_set && npoints) @@ -781,6 +781,10 @@ static void set_dec_mode(VTermState *sta VTERM_PROP_MOUSE_MOVE); break; + case 1004: + state->mode.report_focus = val; + break; + case 1005: state->mouse_protocol = val ? MOUSE_UTF8 : MOUSE_X10; break; @@ -861,6 +865,10 @@ static void request_dec_mode(VTermState reply = state->mouse_flags == (MOUSE_WANT_CLICK|MOUSE_WANT_MOVE); break; + case 1004: + reply = state->mode.report_focus; + break; + case 1005: reply = state->mouse_protocol == MOUSE_UTF8; break; @@ -1728,6 +1736,7 @@ void vterm_state_reset(VTermState *state state->mode.origin = 0; state->mode.leftrightmargin = 0; state->mode.bracketpaste = 0; + state->mode.report_focus = 0; state->vt->mode.ctrl8bit = 0; @@ -1882,11 +1891,26 @@ int vterm_state_set_termprop(VTermState if(val->number == VTERM_PROP_MOUSE_MOVE) state->mouse_flags |= MOUSE_WANT_MOVE; return 1; + + case VTERM_N_PROPS: + return 0; } return 0; } +void vterm_state_focus_in(VTermState *state) +{ + if(state->mode.report_focus) + vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "I"); +} + +void vterm_state_focus_out(VTermState *state) +{ + if(state->mode.report_focus) + vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "O"); +} + const VTermLineInfo *vterm_state_get_lineinfo(const VTermState *state, int row) { return state->lineinfo + row; diff --git a/src/libvterm/src/vterm.c b/src/libvterm/src/vterm.c --- a/src/libvterm/src/vterm.c +++ b/src/libvterm/src/vterm.c @@ -47,14 +47,14 @@ VTerm *vterm_new_with_allocator(int rows vt->rows = rows; vt->cols = cols; - vt->parser_state = NORMAL; + vt->parser.state = NORMAL; - vt->parser_callbacks = NULL; - vt->cbdata = NULL; + vt->parser.callbacks = NULL; + vt->parser.cbdata = NULL; - vt->strbuffer_len = 64; - vt->strbuffer_cur = 0; - vt->strbuffer = vterm_allocator_malloc(vt, vt->strbuffer_len); + vt->parser.strbuffer_len = 64; + vt->parser.strbuffer_cur = 0; + vt->parser.strbuffer = vterm_allocator_malloc(vt, vt->parser.strbuffer_len); vt->outbuffer_len = 200; vt->outbuffer_cur = 0; @@ -71,7 +71,7 @@ void vterm_free(VTerm *vt) if(vt->state) vterm_state_free(vt->state); - vterm_allocator_free(vt, vt->strbuffer); + vterm_allocator_free(vt, vt->parser.strbuffer); vterm_allocator_free(vt, vt->outbuffer); vterm_allocator_free(vt, vt); @@ -100,8 +100,8 @@ void vterm_set_size(VTerm *vt, int rows, vt->rows = rows; vt->cols = cols; - if(vt->parser_callbacks && vt->parser_callbacks->resize) - (*vt->parser_callbacks->resize)(rows, cols, vt->cbdata); + if(vt->parser.callbacks && vt->parser.callbacks->resize) + (*vt->parser.callbacks->resize)(rows, cols, vt->parser.cbdata); } int vterm_get_utf8(const VTerm *vt) @@ -257,17 +257,6 @@ size_t vterm_output_read(VTerm *vt, char return len; } -void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user) -{ - vt->parser_callbacks = callbacks; - vt->cbdata = user; -} - -void *vterm_parser_get_cbdata(VTerm *vt) -{ - return vt->cbdata; -} - VTermValueType vterm_get_attr_type(VTermAttr attr) { switch(attr) { @@ -280,6 +269,8 @@ VTermValueType vterm_get_attr_type(VTerm case VTERM_ATTR_FONT: return VTERM_VALUETYPE_INT; case VTERM_ATTR_FOREGROUND: return VTERM_VALUETYPE_COLOR; case VTERM_ATTR_BACKGROUND: return VTERM_VALUETYPE_COLOR; + + case VTERM_N_ATTRS: return 0; } return 0; /* UNREACHABLE */ } @@ -296,6 +287,8 @@ VTermValueType vterm_get_prop_type(VTerm case VTERM_PROP_CURSORSHAPE: return VTERM_VALUETYPE_INT; case VTERM_PROP_MOUSE: return VTERM_VALUETYPE_INT; case VTERM_PROP_CURSORCOLOR: return VTERM_VALUETYPE_STRING; + + case VTERM_N_PROPS: return 0; } return 0; /* UNREACHABLE */ } diff --git a/src/libvterm/src/vterm_internal.h b/src/libvterm/src/vterm_internal.h --- a/src/libvterm/src/vterm_internal.h +++ b/src/libvterm/src/vterm_internal.h @@ -27,6 +27,11 @@ #define ESC_S "\x1b" +#define INTERMED_MAX 16 + +#define CSI_ARGS_MAX 16 +#define CSI_LEADER_MAX 16 + typedef struct VTermEncoding VTermEncoding; typedef struct { @@ -118,6 +123,7 @@ struct VTermState unsigned int screen:1; unsigned int leftrightmargin:1; unsigned int bracketpaste:1; + unsigned int report_focus:1; } mode; VTermEncodingInstance encoding[4], encoding_utf8; @@ -148,6 +154,13 @@ struct VTermState } saved; }; +typedef enum { + VTERM_PARSER_OSC, + VTERM_PARSER_DCS, + + VTERM_N_PARSER_TYPES +} VTermParserStringType; + struct VTerm { VTermAllocatorFunctions *allocator; @@ -161,22 +174,37 @@ struct VTerm unsigned int ctrl8bit:1; } mode; - enum VTermParserState { - NORMAL, - CSI, - OSC, - DCS, - ESC, - ESC_IN_OSC, - ESC_IN_DCS - } parser_state; - const VTermParserCallbacks *parser_callbacks; - void *cbdata; + struct { + enum VTermParserState { + NORMAL, + CSI_LEADER, + CSI_ARGS, + CSI_INTERMED, + ESC, + /* below here are the "string states" */ + STRING, + ESC_IN_STRING + } state; + + int intermedlen; + char intermed[INTERMED_MAX]; + + int csi_leaderlen; + char csi_leader[CSI_LEADER_MAX]; + + int csi_argi; + long csi_args[CSI_ARGS_MAX]; + + const VTermParserCallbacks *callbacks; + void *cbdata; + + VTermParserStringType stringtype; + char *strbuffer; + size_t strbuffer_len; + size_t strbuffer_cur; + } parser; /* len == malloc()ed size; cur == number of valid bytes */ - char *strbuffer; - size_t strbuffer_len; - size_t strbuffer_cur; char *outbuffer; size_t outbuffer_len; diff --git a/src/libvterm/t/10state_putglyph.test b/src/libvterm/t/10state_putglyph.test --- a/src/libvterm/t/10state_putglyph.test +++ b/src/libvterm/t/10state_putglyph.test @@ -17,6 +17,12 @@ PUSH "\xC3\x81\xC3\xA9" putglyph 0xc1 1 0,0 putglyph 0xe9 1 0,1 +!UTF-8 split writes +RESET +PUSH "\xC3" +PUSH "\x81" + putglyph 0xc1 1 0,0 + !UTF-8 wide char # U+FF10 = 0xEF 0xBC 0x90 name: FULLWIDTH DIGIT ZERO RESET diff --git a/src/libvterm/t/25state_input.test b/src/libvterm/t/25state_input.test --- a/src/libvterm/t/25state_input.test +++ b/src/libvterm/t/25state_input.test @@ -130,3 +130,14 @@ PASTE START output "\e[200~" PASTE END output "\e[201~" + +!Focus reporting disabled +FOCUS IN +FOCUS OUT + +!Focus reporting enabled +PUSH "\e[?1004h" +FOCUS IN + output "\e[I" +FOCUS OUT + output "\e[O" diff --git a/src/libvterm/t/26state_query.test b/src/libvterm/t/26state_query.test --- a/src/libvterm/t/26state_query.test +++ b/src/libvterm/t/26state_query.test @@ -58,5 +58,5 @@ PUSH "\e[5n" PUSH "\e F" !Truncation on attempted buffer overflow -PUSH "\e[6n" x 20 - output "\e[10;10R" x 7 +PUSH "\e[6n" x 30 + output "\e[10;10R" x 24 diff --git a/src/libvterm/t/harness.c b/src/libvterm/t/harness.c --- a/src/libvterm/t/harness.c +++ b/src/libvterm/t/harness.c @@ -233,6 +233,9 @@ static int settermprop(VTermProp prop, V case VTERM_VALUETYPE_COLOR: printf("settermprop %d rgb(%d,%d,%d)\n", prop, val->color.red, val->color.green, val->color.blue); return 1; + + case VTERM_N_VALUETYPES: + return 0; } return 0; @@ -316,6 +319,9 @@ static int state_setpenattr(VTermAttr at case VTERM_ATTR_BACKGROUND: state_pen.background = val->color; break; + + case VTERM_N_ATTRS: + return 0; } return 1; @@ -651,6 +657,16 @@ int main(int argc, char **argv) goto abort_line; } + else if(strstartswith(line, "FOCUS ")) { + char *linep = line + 6; + if(streq(linep, "IN")) + vterm_state_focus_in(state); + else if(streq(linep, "OUT")) + vterm_state_focus_out(state); + else + goto abort_line; + } + else if(strstartswith(line, "MOUSEMOVE ")) { char *linep = line + 10; int row, col, len; diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -767,6 +767,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1639, +/**/ 1638, /**/ 1637,