# HG changeset patch # User Bram Moolenaar # Date 1637783104 -3600 # Node ID f93337ae0612d9d64713a25e4349fb4ffa53834e # Parent 511655f714e9a74c35d7053c4e3bb5197f9732b5 patch 8.2.3666: libvterm is outdated Commit: https://github.com/vim/vim/commit/7da341560ec8db7e81cd80092b046b60a482fbbe Author: Bram Moolenaar Date: Wed Nov 24 19:30:55 2021 +0000 patch 8.2.3666: libvterm is outdated Problem: Libvterm is outdated. Solution: Include patches from revision 769 to revision 789. diff --git a/Filelist b/Filelist --- a/Filelist +++ b/Filelist @@ -377,6 +377,7 @@ SRC_ALL = \ src/libvterm/t/30state_pen.test \ src/libvterm/t/31state_rep.test \ src/libvterm/t/32state_flow.test \ + src/libvterm/t/40state_selection.test \ src/libvterm/t/60screen_ascii.test \ src/libvterm/t/61screen_unicode.test \ src/libvterm/t/62screen_damage.test \ diff --git a/src/libvterm/Makefile b/src/libvterm/Makefile --- a/src/libvterm/Makefile +++ b/src/libvterm/Makefile @@ -37,7 +37,7 @@ INCFILES=$(TBLFILES:.tbl=.inc) HFILES_INT=$(sort $(wildcard src/*.h)) $(HFILES) VERSION_MAJOR=0 -VERSION_MINOR=1 +VERSION_MINOR=2 VERSION_CURRENT=0 VERSION_REVISION=0 @@ -97,7 +97,7 @@ install-inc: install -d $(DESTDIR)$(INCDIR) install -m644 $(HFILES) $(DESTDIR)$(INCDIR) install -d $(DESTDIR)$(LIBDIR)/pkgconfig - sed -e "s,@PREFIX@,$(PREFIX)," -e "s,@LIBDIR@,$(LIBDIR)," -e "s,@VERSION@,$(VERSION)," $(DESTDIR)$(LIBDIR)/pkgconfig/vterm.pc + sed -e "s,@INCDIR@,$(INCDIR)," -e "s,@LIBDIR@,$(LIBDIR)," -e "s,@VERSION@,$(VERSION)," $(DESTDIR)$(LIBDIR)/pkgconfig/vterm.pc install-lib: $(LIBRARY) install -d $(DESTDIR)$(LIBDIR) 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 @@ -4,228 +4,275 @@ between states. 1 = VT100 2 = VT220 3 = VT320 +x = xterm - C0 controls + C0 controls + +123 0x00 = NUL +123x 0x07 = BEL +123x 0x08 = BS +123x 0x09 = HT +123x 0x0A = LF +123x 0x0B = VT +123x 0x0C = FF +123x 0x0D = CR +123x 0x0E = LS1 +123x 0x0F = LS0 + (0x18 = CAN) + (0x1A = SUB) + (0x1B = ESC) -123 0x00 = NUL -123 0x07 = BEL -123 0x08 = BS -123 0x09 = HT -123 0x0A = LF -123 0x0B = VT -123 0x0C = FF -123 0x0D = CR -123 0x0E = LS1 -123 0x0F = LS0 - (0x18 = CAN) - (0x1A = SUB) - (0x1B = ESC) +123 0x7f = DEL (ignored) + + C1 controls -123 0x7f = DEL (ignored) - - C1 controls +123x 0x84 = IND +123x 0x85 = NEL +123x 0x88 = HTS +123x 0x8D = RI + 23x 0x8E = SS2 + 23x 0x8F = SS3 + (0x90 = DCS) + (0x98 = SOS) + (0x9B = CSI) + (0x9C = ST) + (0x9D = OSC) + (0x9E = PM) + (0x9F = APC) -123 0x84 = IND -123 0x85 = NEL -123 0x88 = HTS -123 0x8D = RI - 23 0x8e = SS2 - 23 0x8f = SS3 - (0x90 = DCS) - (0x9B = CSI) - (0x9C = ST) - (0x9D = OSC) + Escape sequences + - excluding sequences that are C1 aliases - Escape sequences - - excluding sequences that are C1 aliases +123x ESC ( = SCS, select character set G0 +123x ESC ) = SCS, select character set G1 + 23x ESC * = SCS, select character set G2 + 23x ESC + = SCS, select character set G3 +123x ESC 7 = DECSC - save cursor +123x ESC 8 = DECRC - restore cursor +123x ESC # 3 = DECDHL, double-height line (top half) +123x ESC # 4 = DECDHL, double-height line (bottom half) +123x ESC # 5 = DECSWL, single-width single-height line +123x ESC # 6 = DECDWL, double-width single-height line +123x ESC # 8 = DECALN +123 ESC < = Ignored (used by VT100 to exit VT52 mode) +123x ESC = = DECKPAM, keypad application mode +123x ESC > = DECKPNM, keypad numeric mode + 23x ESC Sp F = S7C1T + 23x ESC Sp G = S8C1T + (ESC P = DCS) + (ESC X = SOS) + (ESC [ = CSI) + (ESC \ = ST) + (ESC ] = OSC) + (ESC ^ = PM) + (ESC _ = APC) +123x ESC c = RIS, reset initial state + 3x ESC n = LS2 + 3x ESC o = LS3 + 3x ESC | = LS3R + 3x ESC } = LS2R + 3x ESC ~ = LS1R -123 ESC () = SCS, select character set (G0, G1) - 23 ESC *+ = SCS, select character set (G2, G3) -123 ESC 7 = DECSC - save cursor -123 ESC 8 = DECRC - restore cursor -123 ESC # 3 = DECDHL, double-height line (top half) -123 ESC # 4 = DECDHL, double-height line (bottom half) -123 ESC # 5 = DECSWL, single-width single-height line -123 ESC # 6 = DECDWL, double-width single-height line -123 ESC # 8 = DECALN -123 ESC < = Ignored (used by VT100 to exit VT52 mode) -123 ESC = = DECKPAM, keypad application mode -123 ESC > = DECKPNM, keypad numeric mode - 23 ESC Sp F = S7C1T - 23 ESC Sp G = S8C1T - (ESC P = DCS) - (ESC [ = CSI) - (ESC \ = ST) - (ESC ] = OSC) -123 ESC c = RIS, reset initial state - 3 ESC n = LS2 - 3 ESC o = LS3 - 3 ESC ~ = LS1R - 3 ESC } = LS2R - 3 ESC | = LS3R + DCSes - DCSes - - 3 DCS $ q ST = DECRQSS - 3 m = Request SGR - Sp q = Request DECSCUSR - 3 " q = Request DECSCA - 3 r = Request DECSTBM - s = Request DECSLRM + 3x DCS $ q ST = DECRQSS + 3x m = Request SGR + x Sp q = Request DECSCUSR + 3x " q = Request DECSCA + 3x r = Request DECSTBM + x s = Request DECSLRM - CSIs - 23 CSI @ = ICH -123 CSI A = CUU -123 CSI B = CUD -123 CSI C = CUF -123 CSI D = CUB - CSI E = CNL - CSI F = CPL - CSI G = CHA -123 CSI H = CUP - CSI I = CHT -123 CSI J = ED - 23 CSI ? J = DECSED, selective erase in display -123 CSI K = EL - 23 CSI ? K = DECSEL, selective erase in line - 23 CSI L = IL - 23 CSI M = DL - 23 CSI P = DCH - CSI S = SU - CSI T = SD - 23 CSI X = ECH - CSI Z = CBT - CSI ` = HPA - CSI a = HPR - CSI b = REP -123 CSI c = DA, device attributes -123 0 = DA - 23 CSI > c = DECSDA - 23 0 = SDA - CSI d = VPA - CSI e = VPR -123 CSI f = HVP -123 CSI g = TBC -123 CSI h = SM, Set mode -123 CSI ? h = DECSM, DEC set mode - CSI j = HPB - CSI k = VPB -123 CSI l = RM, Reset mode -123 CSI ? l = DECRM, DEC reset mode -123 CSI m = SGR, Set Graphic Rendition -123 CSI n = DSR, Device Status Report - 23 5 = operating status - 23 6 = CPR = cursor position - 23 CSI ? n = DECDSR; behaves as DSR but uses CSI ? instead of CSI to respond - 23 CSI ! p = DECSTR, soft terminal reset - 3 CSI ? $ p = DECRQM, request mode - CSI Sp q = DECSCUSR (odd numbers blink, even numbers solid) - 1 or 2 = block - 3 or 4 = underline - 5 or 6 = I-beam to left - 23 CSI " q = DECSCA, select character attributes -123 CSI r = DECSTBM - CSI s = DECSLRM - CSI ' } = DECIC - CSI ' ~ = DECDC + CSIs + 23x CSI @ = ICH +123x CSI A = CUU +123x CSI B = CUD +123x CSI C = CUF +123x CSI D = CUB + x CSI E = CNL + x CSI F = CPL + x CSI G = CHA +123x CSI H = CUP + x CSI I = CHT +123x CSI J = ED + 23x CSI ? J = DECSED, selective erase in display +123x CSI K = EL + 23x CSI ? K = DECSEL, selective erase in line + 23x CSI L = IL + 23x CSI M = DL + 23x CSI P = DCH + x CSI S = SU + x CSI T = SD + 23x CSI X = ECH + x CSI Z = CBT + x CSI ` = HPA + x CSI a = HPR + x CSI b = REP +123x CSI c = DA, device attributes +123 0 = DA + 23x CSI > c = DECSDA + 23 0 = SDA + x CSI d = VPA + x CSI e = VPR +123x CSI f = HVP +123x CSI g = TBC +123x CSI h = SM, Set mode +123x CSI ? h = DECSM, DEC set mode + CSI j = HPB + CSI k = VPB +123x CSI l = RM, Reset mode +123x CSI ? l = DECRM, DEC reset mode +123x CSI m = SGR, Set Graphic Rendition +123x CSI n = DSR, Device Status Report + 23x 5 = operating status + 23x 6 = CPR = cursor position + 23x CSI ? n = DECDSR; behaves as DSR but uses CSI ? instead of CSI to respond + 23x CSI ! p = DECSTR, soft terminal reset + 3x CSI ? $ p = DECRQM, request private mode + x CSI Sp q = DECSCUSR (odd numbers blink, even numbers solid) + 1 or 2 = block + 3 or 4 = underline + 5 or 6 = I-beam to left + 23x CSI " q = DECSCA, select character attributes +123x CSI r = DECSTBM + x CSI s = DECSLRM + x CSI ' } = DECIC + x CSI ' ~ = DECDC - OSCs + OSCs - OSC 0; = Set icon name and title - OSC 1; = Set icon name - OSC 2; = Set title + x OSC 0; = Set icon name and title + x OSC 1; = Set icon name + x OSC 2; = Set title + x OSC 52; = Selection management - Standard modes + Standard modes - 23 SM 4 = IRM -123 SM 20 = NLM, linefeed/newline + 23x SM 4 = IRM +123x SM 20 = NLM, linefeed/newline - DEC modes + DEC modes -123 DECSM 1 = DECCKM, cursor keys -123 DECSM 5 = DECSCNM, screen -123 DECSM 6 = DECOM, origin -123 DECSM 7 = DECAWM, autowrap - DECSM 12 = Cursor blink - 23 DECSM 25 = DECTCEM, text cursor enable - DECSM 69 = DECVSSM, vertical screen split - 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 - DECSM 1047 = Altscreen - DECSM 1048 = Save cursor - DECSM 1049 = 1047 + 1048 - DECSM 2004 = Bracketed paste +123x DECSM 1 = DECCKM, cursor keys +123x DECSM 5 = DECSCNM, screen +123x DECSM 6 = DECOM, origin +123x DECSM 7 = DECAWM, autowrap + x DECSM 12 = Cursor blink + 23x DECSM 25 = DECTCEM, text cursor enable + x DECSM 69 = DECVSSM, vertical screen split + x DECSM 1000 = Mouse click/release tracking + x DECSM 1002 = Mouse click/release/drag tracking + x DECSM 1003 = Mouse all movements tracking + x DECSM 1004 = Focus in/out reporting + x DECSM 1005 = Mouse protocol extended (UTF-8) - not recommended + x DECSM 1006 = Mouse protocol SGR + x DECSM 1015 = Mouse protocol rxvt + x DECSM 1047 = Altscreen + x DECSM 1048 = Save cursor + x DECSM 1049 = 1047 + 1048 + x DECSM 2004 = Bracketed paste - Graphic Renditions + Graphic Renditions -123 SGR 0 = Reset -123 SGR 1 = Bold on - SGR 3 = Italic on -123 SGR 4 = Underline single - SGR 4:x = Underline style -123 SGR 5 = Blink on -123 SGR 7 = Reverse on - SGR 8 = Conceal on - SGR 9 = Strikethrough on - SGR 10-19 = Select font - SGR 21 = Underline double - 23 SGR 22 = Bold off - SGR 23 = Italic off - 23 SGR 24 = Underline off - 23 SGR 25 = Blink off - 23 SGR 27 = Reverse off - SGR 28 = Conceal off - SGR 29 = Strikethrough off - SGR 30-37 = Foreground ANSI - SGR 38 = Foreground alternative palette - SGR 39 = Foreground default - SGR 40-47 = Background ANSI - SGR 48 = Background alternative palette - SGR 49 = Background default - SGR 90-97 = Foreground ANSI high-intensity - SGR 100-107 = Background ANSI high-intensity +123x SGR 0 = Reset +123x SGR 1 = Bold on + x SGR 3 = Italic on +123x SGR 4 = Underline single + SGR 4:x = Underline style +123x SGR 5 = Blink on +123x SGR 7 = Reverse on + x SGR 8 = Conceal on + x SGR 9 = Strikethrough on + SGR 10-19 = Select font + x SGR 21 = Underline double + 23x SGR 22 = Bold off + x SGR 23 = Italic off + 23x SGR 24 = Underline off + 23x SGR 25 = Blink off + 23x SGR 27 = Reverse off + x SGR 28 = Conceal off + x SGR 29 = Strikethrough off + x SGR 30-37 = Foreground ANSI + x SGR 38 = Foreground alternative palette + x SGR 39 = Foreground default + x SGR 40-47 = Background ANSI + x SGR 48 = Background alternative palette + x SGR 49 = Background default + x SGR 90-97 = Foreground ANSI high-intensity + x SGR 100-107 = Background ANSI high-intensity The state storage used by ESC 7 and DECSM 1048/1049 is shared. - Unimplemented sequences: + Unimplemented sequences: The following sequences are not recognised by libvterm. -123 0x05 = ENQ - 3 0x11 = DC1 (XON) - 3 0x13 = DC3 (XOFF) -12 ESC Z = DECID, identify terminal - DCS $ q = [DECRQSS] - 3 " p = Request DECSCL - 3 $ } = Request DECSASD - 3 $ ~ = Request DECSSDT - 23 DCS { = DECDLD, down-line-loadable character set - 23 DCS | = DECUDK, user-defined key - 23 CSI i = DEC printer control - 23 CSI " p = DECSCL, set compatibility level -1 CSI q = DECLL, load LEDs - 3 CSI $ u = DECRQTSR, request terminal state report - 3 1 = terminal state report - 3 CSI & u = DECRQUPSS, request user-preferred supplemental set - 3 CSI $ w = DECRQPSR, request presentation state report - 3 1 = cursor information report - 3 2 = tab stop report -1 CSI x = DECREQTPARM, request terminal parameters -123 CSI y = DECTST, invoke confidence test - 3 CSI $ } = DECSASD, select active status display - 3 CSI $ ~ = DECSSDT, select status line type - 23 SM 2 = KAM, keyboard action -123 SM 12 = SRM, send/receive -123 DECSM 2 = DECANM, ANSI/VT52 -123 DECSM 3 = DECCOLM, 132 column -123 DECSM 4 = DECSCLM, scrolling -123 DECSM 8 = DECARM, auto-repeat -12 DECSM 9 = DECINLM, interlace - 23 DECSM 18 = DECPFF, print form feed - 23 DECSM 19 = DECPEX, print extent - 23 DECSM 42 = DECNRCM, national/multinational character +123x 0x05 = ENQ + 3 0x11 = DC1 (XON) + 3 0x13 = DC3 (XOFF) + x ESC % @ = Select default character set + x ESC % G = Select UTF-8 character set + x ESC 6 = DECBI, Back Index +12 ESC Z = DECID, identify terminal + x DCS + Q = XTGETXRES, Request resource values + DCS $ q = [DECRQSS] + 3x " p = Request DECSCL + x t = Request DECSLPP + x $ | = Request DECSCPP + x * | = Request DECSLNS + 3 $ } = Request DECSASD + 3 $ ~ = Request DECSSDT + x DCS + p = XTSETTCAP, set termcap/terminfo data + x DCS + q = XTGETTCAP, request termcap/terminfo + 23 DCS { = DECDLD, down-line-loadable character set + 23x DCS | = DECUDK, user-defined key + x CSI Sp @ = Shift left columns + x CSI Sp A = Shift right columns + x CSI # P = XTPUSHCOLORS, push current dynamic colours to stack + x CSI # Q = XTPOPCOLORS, pop dynamic colours from stack + x CSI # R = XTREPORTCOLORS, report current entry on palette stack + x CSI ? S = XTSMGRAPHICS, set/request graphics attribute + x CSI > T = XTRMTITLE, reset title mode features + 23x CSI i = DEC printer control + x CSI > m = XTMODKEYS, set key modifier options + x CSI > n = (XTMODKEYS), reset key modifier options + x CSI $ p = DECRQM, request ANSI mode + 23x CSI " p = DECSCL, set compatibility level + x CSI > p = XTSMPOINTER, set resource value pointer mode +1 x CSI q = DECLL, load LEDs + x CSI ? r = XTRESTORE, restore DEC private mode values + x CSI $ r = DECCARA, change attributes in rectangular area + x CSI > s = XTSHIFTESCAPE, set/reset shift-escape options + x CSI ? s = XTSAVE, save DEC private mode values + x CSI t = XTWINOPS, window operations + x CSI > t = XTSMTITLE, set title mode features + x CSI $ t = DECRARA, reset attributes in rectangular area + 3 CSI $ u = DECRQTSR, request terminal state report + 3 1 = terminal state report + 3 CSI & u = DECRQUPSS, request user-preferred supplemental set + x CSI $ v = DECCRA, copy rectangular area + 3x CSI $ w = DECRQPSR, request presentation state report + 3x 1 = cursor information report + 3x 2 = tab stop report + x CSI ' w = DECEFR, enable filter rectangle +1 x CSI x = DECREQTPARM, request terminal parameters + x CSI * x = DECSACE, select attribute change extent + x CSI $ x = DECFRA, fill rectangular area +123 CSI y = DECTST, invoke confidence test + x CSI $ z = DECERA, erase rectangular area + x CSI # { = XTPUSHSGR, push video attributes onto stack + x CSI $ { = DECSERA, selective erase in rectangular area + x CSI # | = XTREPORTSGR, report selected graphic rendition + x CSI $ | = DECSCPP, select columns per page + x CSI # } = XTPOPSGR, pop video attributes from stack + 3 CSI $ } = DECSASD, select active status display + 3 CSI $ ~ = DECSSDT, select status line type + 23 SM 2 = KAM, keyboard action +123 SM 12 = SRM, send/receive +123 DECSM 2 = DECANM, ANSI/VT52 +123 DECSM 3 = DECCOLM, 132 column +123 DECSM 4 = DECSCLM, scrolling +123 DECSM 8 = DECARM, auto-repeat +12 DECSM 9 = DECINLM, interlace + 23 DECSM 18 = DECPFF, print form feed + 23 DECSM 19 = DECPEX, print extent + 23 DECSM 42 = DECNRCM, national/multinational character 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 @@ -17,10 +17,11 @@ extern "C" { // from stdint.h typedef unsigned char uint8_t; +typedef unsigned short uint16_t; typedef unsigned int uint32_t; #define VTERM_VERSION_MAJOR 0 -#define VTERM_VERSION_MINOR 1 +#define VTERM_VERSION_MINOR 2 #define VTERM_CHECK_VERSION \ vterm_check_version(VTERM_VERSION_MAJOR, VTERM_VERSION_MINOR) @@ -267,6 +268,14 @@ enum { VTERM_N_PROP_MOUSES }; +typedef enum { + VTERM_SELECTION_CLIPBOARD = (1<<0), + VTERM_SELECTION_PRIMARY = (1<<1), + VTERM_SELECTION_SECONDARY = (1<<2), + VTERM_SELECTION_SELECT = (1<<3), + VTERM_SELECTION_CUT0 = (1<<4), /* also CUT1 .. CUT7 by bitshifting */ +} VTermSelectionMask; + typedef struct { const uint32_t *chars; int width; @@ -375,6 +384,9 @@ typedef struct { 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; @@ -419,8 +431,16 @@ typedef struct { 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); @@ -457,6 +477,11 @@ const VTermLineInfo *vterm_state_get_lin */ 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 // ------------ 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 @@ -89,6 +89,9 @@ void vterm_mouse_button(VTerm *vt, int b if (!(state->mouse_flags & MOUSE_WANT_CLICK)) return; + if(!state->mouse_flags) + return; + if(button < 4) { output_mouse(state, button-1, pressed, mod, state->mouse_col, state->mouse_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 @@ -77,10 +77,25 @@ static void string_fragment(VTerm *vt, c break; case DCS: - if(len && vt->parser.callbacks && vt->parser.callbacks->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: @@ -112,6 +127,9 @@ size_t vterm_input_write(VTerm *vt, cons break; case OSC: case DCS: + case APC: + case PM: + case SOS: string_start = bytes; break; } @@ -150,6 +168,9 @@ size_t vterm_input_write(VTerm *vt, cons // fallthrough } else if(c < 0x20) { // other C0 + if(vt->parser.state == SOS) + continue; // All other C0s permitted in SOS + if(vterm_get_special_pty_type() == 2) { if(c == 0x08) // BS // Set the trick for BS output after a sequence, to delay backspace @@ -176,7 +197,8 @@ size_t vterm_input_write(VTerm *vt, cons ((!IS_STRING_STATE() || c == 0x5c))) { c += 0x40; c1_allowed = TRUE; - string_len -= 1; + if(string_len) + string_len -= 1; vt->parser.in_esc = FALSE; } else { @@ -279,6 +301,9 @@ size_t vterm_input_write(VTerm *vt, cons 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(); @@ -308,6 +333,12 @@ string_state: 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); @@ -318,6 +349,18 @@ string_state: 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; @@ -340,8 +383,12 @@ string_state: } } - if(string_start) - string_fragment(vt, string_start, bytes + pos - string_start, FALSE); + if(string_start) { + size_t string_len = bytes + pos - string_start; + if(vt->parser.in_esc) + string_len -= 1; + string_fragment(vt, string_start, string_len, FALSE); + } return len; } 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 @@ -79,6 +79,10 @@ static VTermState *vterm_state_new(VTerm state->callbacks = NULL; state->cbdata = NULL; + state->selection.callbacks = NULL; + state->selection.user = NULL; + state->selection.buffer = NULL; + vterm_state_newpen(state); state->bold_is_highbright = 0; @@ -1615,6 +1619,174 @@ static int on_csi(const char *leader, co return 1; } +static char base64_one(uint8_t b) +{ + if(b < 26) + return 'A' + b; + else if(b < 52) + return 'a' + b - 26; + else if(b < 62) + return '0' + b - 52; + else if(b == 62) + return '+'; + else if(b == 63) + return '/'; + return 0; +} + +static uint8_t unbase64one(char c) +{ + if(c >= 'A' && c <= 'Z') + return c - 'A'; + else if(c >= 'a' && c <= 'z') + return c - 'a' + 26; + else if(c >= '0' && c <= '9') + return c - '0' + 52; + else if(c == '+') + return 62; + else if(c == '/') + return 63; + + return 0xFF; +} + +static void osc_selection(VTermState *state, VTermStringFragment frag) +{ + if(frag.initial) { + state->tmp.selection.mask = 0; + state->tmp.selection.state = SELECTION_INITIAL; + } + + while(!state->tmp.selection.state && frag.len) { + /* Parse selection parameter */ + switch(frag.str[0]) { + case 'c': + state->tmp.selection.mask |= VTERM_SELECTION_CLIPBOARD; + break; + case 'p': + state->tmp.selection.mask |= VTERM_SELECTION_PRIMARY; + break; + case 'q': + state->tmp.selection.mask |= VTERM_SELECTION_SECONDARY; + break; + case 's': + state->tmp.selection.mask |= VTERM_SELECTION_SELECT; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + state->tmp.selection.mask |= (VTERM_SELECTION_CUT0 << (frag.str[0] - '0')); + break; + + case ';': + state->tmp.selection.state = SELECTION_SELECTED; + if(!state->tmp.selection.mask) + state->tmp.selection.mask = VTERM_SELECTION_SELECT|VTERM_SELECTION_CUT0; + break; + } + + frag.str++; + frag.len--; + } + + if(!frag.len) + return; + + if(state->tmp.selection.state == SELECTION_SELECTED) { + if(frag.str[0] == '?') { + state->tmp.selection.state = SELECTION_QUERY; + } + else { + state->tmp.selection.state = SELECTION_SET_INITIAL; + state->tmp.selection.recvpartial = 0; + } + } + + if(state->tmp.selection.state == SELECTION_QUERY) { + if(state->selection.callbacks->query) + (*state->selection.callbacks->query)(state->tmp.selection.mask, state->selection.user); + return; + } + + if(state->selection.callbacks->set) { + size_t bufcur = 0; + char *buffer = state->selection.buffer; + + uint32_t x = 0; /* Current decoding value */ + int n = 0; /* Number of sextets consumed */ + + if(state->tmp.selection.recvpartial) { + n = state->tmp.selection.recvpartial >> 24; + x = state->tmp.selection.recvpartial & 0x03FFFF; /* could be up to 18 bits of state in here */ + + state->tmp.selection.recvpartial = 0; + } + + while((state->selection.buflen - bufcur) >= 3 && frag.len) { + if(frag.str[0] == '=') { + if(n == 2) { + buffer[0] = (x >> 4) & 0xFF; + buffer += 1, bufcur += 1; + } + if(n == 3) { + buffer[0] = (x >> 10) & 0xFF; + buffer[1] = (x >> 2) & 0xFF; + buffer += 2, bufcur += 2; + } + + while(frag.len && frag.str[0] == '=') + frag.str++, frag.len--; + + n = 0; + } + else { + uint8_t b = unbase64one(frag.str[0]); + if(b == 0xFF) { + DEBUG_LOG1("base64decode bad input %02X\n", (uint8_t)frag.str[0]); + } + else { + x = (x << 6) | b; + n++; + } + frag.str++, frag.len--; + + if(n == 4) { + buffer[0] = (x >> 16) & 0xFF; + buffer[1] = (x >> 8) & 0xFF; + buffer[2] = (x >> 0) & 0xFF; + + buffer += 3, bufcur += 3; + x = 0; + n = 0; + } + } + + if(!frag.len || (state->selection.buflen - bufcur) < 3) { + if(bufcur) { + (*state->selection.callbacks->set)(state->tmp.selection.mask, (VTermStringFragment){ + .str = state->selection.buffer, + .len = bufcur, + .initial = state->tmp.selection.state == SELECTION_SET_INITIAL, + .final = frag.final, + }, state->selection.user); + state->tmp.selection.state = SELECTION_SET; + } + + buffer = state->selection.buffer; + bufcur = 0; + } + } + + if(n) + state->tmp.selection.recvpartial = (n << 24) | x; + } +} + static int on_osc(int command, VTermStringFragment frag, void *user) { VTermState *state = user; @@ -1656,6 +1828,12 @@ static int on_osc(int command, VTermStri settermprop_string(state, VTERM_PROP_CURSORCOLOR, frag); return 1; + case 52: + if(state->selection.callbacks) + osc_selection(state, frag); + + return 1; + default: if(state->fallbacks && state->fallbacks->osc) if((*state->fallbacks->osc)(command, frag, state->fbdata)) @@ -1718,12 +1896,14 @@ static void request_status_string(VTermS case 'r': // Query DECSTBM - vterm_push_output_sprintf_dcs(vt, "1$r%d;%dr", state->scrollregion_top+1, SCROLLREGION_BOTTOM(state)); + vterm_push_output_sprintf_str(vt, C1_DCS, TRUE, + "1$r%d;%dr", state->scrollregion_top+1, SCROLLREGION_BOTTOM(state)); return; case 's': // Query DECSLRM - vterm_push_output_sprintf_dcs(vt, "1$r%d;%ds", SCROLLREGION_LEFT(state)+1, SCROLLREGION_RIGHT(state)); + vterm_push_output_sprintf_str(vt, C1_DCS, TRUE, + "1$r%d;%ds", SCROLLREGION_LEFT(state)+1, SCROLLREGION_RIGHT(state)); return; case ' '|('q'<<8): { @@ -1736,17 +1916,19 @@ static void request_status_string(VTermS } if(state->mode.cursor_blink) reply--; - vterm_push_output_sprintf_dcs(vt, "1$r%d q", reply); + vterm_push_output_sprintf_str(vt, C1_DCS, TRUE, + "1$r%d q", reply); return; } case '\"'|('q'<<8): // Query DECSCA - vterm_push_output_sprintf_dcs(vt, "1$r%d\"q", state->protected_cell ? 1 : 2); + vterm_push_output_sprintf_str(vt, C1_DCS, TRUE, + "1$r%d\"q", state->protected_cell ? 1 : 2); return; } - vterm_push_output_sprintf_dcs(state->vt, "0$r%s", tmp); + vterm_push_output_sprintf_str(state->vt, C1_DCS, TRUE, "0$r%s", tmp); } static int on_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user) @@ -1765,6 +1947,42 @@ static int on_dcs(const char *command, s return 0; } +static int on_apc(VTermStringFragment frag, void *user) +{ + VTermState *state = user; + + if(state->fallbacks && state->fallbacks->apc) + if((*state->fallbacks->apc)(frag, state->fbdata)) + return 1; + + /* No DEBUG_LOG because all APCs are unhandled */ + return 0; +} + +static int on_pm(VTermStringFragment frag, void *user) +{ + VTermState *state = user; + + if(state->fallbacks && state->fallbacks->pm) + if((*state->fallbacks->pm)(frag, state->fbdata)) + return 1; + + /* No DEBUG_LOG because all PMs are unhandled */ + return 0; +} + +static int on_sos(VTermStringFragment frag, void *user) +{ + VTermState *state = user; + + if(state->fallbacks && state->fallbacks->sos) + if((*state->fallbacks->sos)(frag, state->fbdata)) + return 1; + + /* No DEBUG_LOG because all SOSs are unhandled */ + return 0; +} + static int on_resize(int rows, int cols, void *user) { VTermState *state = user; @@ -1866,6 +2084,9 @@ static const VTermParserCallbacks parser on_csi, // csi on_osc, // osc on_dcs, // dcs + on_apc, // apc + on_pm, // pm + on_sos, // sos on_resize // resize }; @@ -1909,6 +2130,8 @@ void vterm_state_reset(VTermState *state state->mode.bracketpaste = 0; state->mode.report_focus = 0; + state->mouse_flags = 0; + state->vt->mode.ctrl8bit = 0; { @@ -2087,3 +2310,96 @@ const VTermLineInfo *vterm_state_get_lin { return state->lineinfo + row; } + +void vterm_state_set_selection_callbacks(VTermState *state, const VTermSelectionCallbacks *callbacks, void *user, + char *buffer, size_t buflen) +{ + if(buflen && !buffer) + buffer = vterm_allocator_malloc(state->vt, buflen); + + state->selection.callbacks = callbacks; + state->selection.user = user; + state->selection.buffer = buffer; + state->selection.buflen = buflen; +} + +void vterm_state_send_selection(VTermState *state, VTermSelectionMask mask, VTermStringFragment frag) +{ + VTerm *vt = state->vt; + + if(frag.initial) { + /* TODO: support sending more than one mask bit */ + static char selection_chars[] = "cpqs"; + int idx; + for(idx = 0; idx < 4; idx++) + if(mask & (1 << idx)) + break; + + vterm_push_output_sprintf_str(vt, C1_OSC, FALSE, "52;%c;", selection_chars[idx]); + + state->tmp.selection.sendpartial = 0; + } + + if(frag.len) { + size_t bufcur = 0; + char *buffer = state->selection.buffer; + + uint32_t x = 0; + int n = 0; + + if(state->tmp.selection.sendpartial) { + n = state->tmp.selection.sendpartial >> 24; + x = state->tmp.selection.sendpartial & 0xFFFFFF; + + state->tmp.selection.sendpartial = 0; + } + + while((state->selection.buflen - bufcur) >= 4 && frag.len) { + x = (x << 8) | frag.str[0]; + n++; + frag.str++, frag.len--; + + if(n == 3) { + buffer[0] = base64_one((x >> 18) & 0x3F); + buffer[1] = base64_one((x >> 12) & 0x3F); + buffer[2] = base64_one((x >> 6) & 0x3F); + buffer[3] = base64_one((x >> 0) & 0x3F); + + buffer += 4, bufcur += 4; + x = 0; + n = 0; + } + + if(!frag.len || (state->selection.buflen - bufcur) < 4) { + if(bufcur) + vterm_push_output_bytes(vt, state->selection.buffer, bufcur); + + buffer = state->selection.buffer; + bufcur = 0; + } + } + + if(n) + state->tmp.selection.sendpartial = (n << 24) | x; + } + + if(frag.final) { + if(state->tmp.selection.sendpartial) { + int n = state->tmp.selection.sendpartial >> 24; + uint32_t x = state->tmp.selection.sendpartial & 0xFFFFFF; + char *buffer = state->selection.buffer; + + /* n is either 1 or 2 now */ + x <<= (n == 1) ? 16 : 8; + + buffer[0] = base64_one((x >> 18) & 0x3F); + buffer[1] = base64_one((x >> 12) & 0x3F); + buffer[2] = (n == 1) ? '=' : base64_one((x >> 6) & 0x3F); + buffer[3] = '='; + + vterm_push_output_sprintf_str(vt, 0, TRUE, "%.*s", 4, buffer); + } + else + vterm_push_output_sprintf_str(vt, 0, TRUE, ""); + } +} 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 @@ -200,27 +200,35 @@ INTERNAL void vterm_push_output_sprintf_ va_end(args); } -INTERNAL void vterm_push_output_sprintf_dcs(VTerm *vt, const char *fmt, ...) +INTERNAL void vterm_push_output_sprintf_str(VTerm *vt, unsigned char ctrl, int term, const char *fmt, ...) { size_t cur; va_list args; - cur = SNPRINTF(vt->tmpbuffer, vt->tmpbuffer_len, - vt->mode.ctrl8bit ? "\x90" : ESC_S "P"); // DCS + 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; - vterm_push_output_bytes(vt, vt->tmpbuffer, cur); + if(cur >= vt->tmpbuffer_len) + return; + vterm_push_output_bytes(vt, vt->tmpbuffer, cur); + } va_start(args, fmt); vterm_push_output_vsprintf(vt, fmt, args); va_end(args); - cur = SNPRINTF(vt->tmpbuffer, vt->tmpbuffer_len, - vt->mode.ctrl8bit ? "\x9C" : ESC_S "\\"); // ST - if(cur >= vt->tmpbuffer_len) - return; - vterm_push_output_bytes(vt, vt->tmpbuffer, cur); + if(term) { + cur = SNPRINTF(vt->tmpbuffer, vt->tmpbuffer_len, + 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) 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 @@ -154,7 +154,26 @@ struct VTermState /* 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, + } state : 8; + uint32_t recvpartial; + uint32_t sendpartial; + } selection; } tmp; + + struct { + const VTermSelectionCallbacks *callbacks; + void *user; + char *buffer; + size_t buflen; + } selection; }; struct VTerm @@ -181,6 +200,9 @@ struct VTerm OSC_COMMAND, OSC, DCS, + APC, + PM, + SOS, } state; unsigned int in_esc : 1; @@ -248,7 +270,7 @@ void vterm_push_output_bytes(VTerm *vt, void vterm_push_output_vsprintf(VTerm *vt, const char *format, va_list args); void vterm_push_output_sprintf(VTerm *vt, const char *format, ...); void vterm_push_output_sprintf_ctrl(VTerm *vt, unsigned char ctrl, const char *fmt, ...); -void vterm_push_output_sprintf_dcs(VTerm *vt, const char *fmt, ...); +void vterm_push_output_sprintf_str(VTerm *vt, unsigned char ctrl, int term, const char *fmt, ...); void vterm_state_free(VTermState *state); diff --git a/src/libvterm/t/02parser.test b/src/libvterm/t/02parser.test --- a/src/libvterm/t/02parser.test +++ b/src/libvterm/t/02parser.test @@ -17,15 +17,15 @@ PUSH "\x1f" PUSH "\x83" control 0x83 -PUSH "\x9f" - control 0x9f +PUSH "\x99" + control 0x99 !C1 7bit PUSH "\e\x43" control 0x83 -PUSH "\e\x5f" - control 0x9f +PUSH "\e\x59" + control 0x99 !High bytes PUSH "\xa0\xcc\xfe" @@ -184,6 +184,12 @@ PUSH "\ePHello\e\\" PUSH "\x{90}Hello\x9c" dcs ["Hello"] +!Split write of 7bit ST +PUSH "\ePABC\e" + dcs ["ABC" +PUSH "\\" + dcs ] + !Escape cancels DCS, starts Escape PUSH "\ePSomething\e9" escape "9" @@ -198,6 +204,48 @@ PUSH "\ePBy\ne\x07" control 10 dcs "e"] +!APC BEL +PUSH "\e_Hello\x07" + apc ["Hello"] + +!APC ST (7bit) +PUSH "\e_Hello\e\\" + apc ["Hello"] + +!APC ST (8bit) +PUSH "\x{9f}Hello\x9c" + apc ["Hello"] + +!PM BEL +PUSH "\e^Hello\x07" + pm ["Hello"] + +!PM ST (7bit) +PUSH "\e^Hello\e\\" + pm ["Hello"] + +!PM ST (8bit) +PUSH "\x{9e}Hello\x9c" + pm ["Hello"] + +!SOS BEL +PUSH "\eXHello\x07" + sos ["Hello"] + +!SOS ST (7bit) +PUSH "\eXHello\e\\" + sos ["Hello"] + +!SOS ST (8bit) +PUSH "\x{98}Hello\x9c" + sos ["Hello"] + +!SOS can contain any C0 or C1 code +PUSH "\eXABC\x01DEF\e\\" + sos ["ABC\x01DEF"] +PUSH "\eXABC\x99DEF\e\\" + sos ["ABC\x{99}DEF"] + !NUL ignored PUSH "\x{00}" diff --git a/src/libvterm/t/17state_mouse.test b/src/libvterm/t/17state_mouse.test --- a/src/libvterm/t/17state_mouse.test +++ b/src/libvterm/t/17state_mouse.test @@ -170,3 +170,12 @@ PUSH "\e[?1006\$p" output "\e[?1006;2\$y" PUSH "\e[?1015\$p" output "\e[?1015;1\$y" + +!Mouse disabled reports nothing +RESET + settermprop 1 true + settermprop 2 true + settermprop 7 1 +MOUSEMOVE 0,0 0 +MOUSEBTN d 1 0 +MOUSEBTN u 1 0 diff --git a/src/libvterm/t/29state_fallback.test b/src/libvterm/t/29state_fallback.test --- a/src/libvterm/t/29state_fallback.test +++ b/src/libvterm/t/29state_fallback.test @@ -17,3 +17,15 @@ PUSH "\e]27;Something\e\\" !Unrecognised DCS PUSH "\ePz123\e\\" dcs ["z123"] + +!Unrecognised APC +PUSH "\e_z123\e\\" + apc ["z123"] + +!Unrecognised PM +PUSH "\e^z123\e\\" + pm ["z123"] + +!Unrecognised SOS +PUSH "\eXz123\e\\" + sos ["z123"] diff --git a/src/libvterm/t/40state_selection.test b/src/libvterm/t/40state_selection.test new file mode 100644 --- /dev/null +++ b/src/libvterm/t/40state_selection.test @@ -0,0 +1,55 @@ +INIT +UTF8 1 +WANTSTATE + +!Set clipboard; final chunk len 4 +PUSH "\e]52;c;SGVsbG8s\e\\" + selection-set mask=0001 ["Hello,"] + +!Set clipboard; final chunk len 3 +PUSH "\e]52;c;SGVsbG8sIHc=\e\\" + selection-set mask=0001 ["Hello, w"] + +!Set clipboard; final chunk len 2 +PUSH "\e]52;c;SGVsbG8sIHdvcmxkCg==\e\\" + selection-set mask=0001 ["Hello, world\n"] + +!Set clipboard; split between chunks +PUSH "\e]52;c;SGVs" + selection-set mask=0001 ["Hel" +PUSH "bG8s\e\\" + selection-set mask=0001 "lo,"] + +!Set clipboard; split within chunk +PUSH "\e]52;c;SGVsbG" + selection-set mask=0001 ["Hel" +PUSH "8s\e\\" + selection-set mask=0001 "lo,"] + +!Query clipboard +PUSH "\e]52;c;?\e\\" + selection-query mask=0001 + +!Send clipboard; final chunk len 4 +SELECTION 1 ["Hello,"] + output "\e]52;c;SGVsbG8s\e\\" + +!Send clipboard; final chunk len 3 +SELECTION 1 ["Hello, w"] + output "\e]52;c;SGVsbG8sIHc=\e\\" + +!Send clipboard; final chunk len 2 +SELECTION 1 ["Hello, world\n"] + output "\e]52;c;SGVsbG8sIHdvcmxkCg==\e\\" + +!Send clipboard; split between chunks +SELECTION 1 ["Hel" + output "\e]52;c;SGVs" +SELECTION 1 "lo,"] + output "bG8s\e\\" + +!Send clipboard; split within chunk +SELECTION 1 ["Hello" + output "\e]52;c;SGVs" +SELECTION 1 ","] + output "bG8s\e\\" 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 @@ -13,7 +13,8 @@ static size_t inplace_hex2bytes(char *s) while(*inpos) { unsigned int ch; - sscanf(inpos, "%2x", &ch); + if(sscanf(inpos, "%2x", &ch) < 1) + break; *outpos = ch; outpos += 1; inpos += 2; } @@ -98,7 +99,7 @@ static void term_output(const char *s, s static void printhex(const char *s, size_t len) { while(len--) - printf("%02x", (s++)[0]); + printf("%02x", (uint8_t)(s++)[0]); } static int parser_text(const char bytes[], size_t len, void *user UNUSED) @@ -216,6 +217,57 @@ static int parser_dcs(const char *comman return 1; } +static int parser_apc(VTermStringFragment frag, void *user UNUSED) +{ + printf("apc "); + + if(frag.initial) + printf("["); + + printhex(frag.str, frag.len); + + if(frag.final) + printf("]"); + + printf("\n"); + + return 1; +} + +static int parser_pm(VTermStringFragment frag, void *user UNUSED) +{ + printf("pm "); + + if(frag.initial) + printf("["); + + printhex(frag.str, frag.len); + + if(frag.final) + printf("]"); + + printf("\n"); + + return 1; +} + +static int parser_sos(VTermStringFragment frag, void *user UNUSED) +{ + printf("sos "); + + if(frag.initial) + printf("["); + + printhex(frag.str, frag.len); + + if(frag.final) + printf("]"); + + printf("\n"); + + return 1; +} + static VTermParserCallbacks parser_cbs = { parser_text, // text parser_control, // control @@ -223,6 +275,9 @@ static VTermParserCallbacks parser_cbs = parser_csi, // csi parser_osc, // osc parser_dcs, // dcs + parser_apc, // apc + parser_pm, // pm + parser_sos, // sos NULL // resize }; @@ -230,7 +285,10 @@ static VTermStateFallbacks fallbacks = { parser_control, // control parser_csi, // csi parser_osc, // osc - parser_dcs // dcs + parser_dcs, // dcs + parser_apc, // dcs + parser_pm, // pm + parser_sos // sos }; /* These callbacks are shared by State and Screen */ @@ -414,6 +472,31 @@ VTermStateCallbacks state_cbs = { state_setlineinfo, // setlineinfo }; +static int selection_set(VTermSelectionMask mask, VTermStringFragment frag, void *user UNUSED) +{ + printf("selection-set mask=%04X ", mask); + if(frag.initial) + printf("["); + printhex(frag.str, frag.len); + if(frag.final) + printf("]"); + printf("\n"); + + return 1; +} + +static int selection_query(VTermSelectionMask mask, void *user UNUSED) +{ + printf("selection-query mask=%04X\n", mask); + + return 1; +} + +VTermSelectionCallbacks selection_cbs = { + .set = selection_set, + .query = selection_query, +}; + static int want_screen_damage = 0; static int want_screen_damage_cells = 0; static int screen_damage(VTermRect rect, void *user UNUSED) @@ -555,6 +638,7 @@ int main(int argc UNUSED, char **argv UN if(!state) { state = vterm_obtain_state(vt); vterm_state_set_callbacks(state, &state_cbs, NULL); + vterm_state_set_selection_callbacks(state, &selection_cbs, NULL, NULL, 1024); vterm_state_set_bold_highbright(state, 1); vterm_state_reset(state, 1); } @@ -768,6 +852,32 @@ int main(int argc UNUSED, char **argv UN vterm_mouse_button(vt, button, (press == 'd' || press == 'D'), mod); } + else if(strstartswith(line, "SELECTION ")) { + char *linep = line + 10; + unsigned int mask; + int len; + VTermStringFragment frag = { 0 }; + sscanf(linep, "%x%n", &mask, &len); + linep += len; + while(linep[0] == ' ') + linep++; + if(linep[0] == '[') { + frag.initial = TRUE; + linep++; + while(linep[0] == ' ') + linep++; + } + frag.len = inplace_hex2bytes(linep); + frag.str = linep; + linep += frag.len * 2; + while(linep[0] == ' ') + linep++; + if(linep[0] == ']') { + frag.final = TRUE; + } + vterm_state_send_selection(state, mask, frag); + } + else if(strstartswith(line, "DAMAGEMERGE ")) { char *linep = line + 12; while(linep[0] == ' ') diff --git a/src/libvterm/t/run-test.pl b/src/libvterm/t/run-test.pl --- a/src/libvterm/t/run-test.pl +++ b/src/libvterm/t/run-test.pl @@ -85,6 +85,11 @@ sub do_line my $string = eval($2); $line = "$1 " . unpack "H*", $string; } + elsif( $line =~ m/^(SELECTION \d+) +(\[?)(.*?)(\]?)$/ ) { + # we're evil + my $string = eval($3); + $line = "$1 $2 " . unpack( "H*", $string ) . " $4"; + } do_onetest if defined $command; @@ -113,15 +118,18 @@ sub do_line $line = "$cmd $initial" . join( "", map sprintf("%02x", $_), unpack "C*", length $data ? eval($data) : "" ) . "$final"; } - elsif( $line =~ m/^(escape|dcs) (\[?)(.*?)(\]?)$/ ) { - $line = "$1 $2" . join( "", map sprintf("%02x", $_), unpack "C*", eval($3) ) . "$4"; + elsif( $line =~ m/^(escape|dcs|apc|pm|sos) (\[?)(.*?)(\]?)$/ ) { + $line = "$1 $2" . join( "", map sprintf("%02x", $_), unpack "C*", length $3 ? eval($3) : "" ) . "$4"; } elsif( $line =~ m/^putglyph (\S+) (.*)$/ ) { $line = "putglyph " . join( ",", map sprintf("%x", $_), eval($1) ) . " $2"; } - elsif( $line =~ m/^(?:movecursor|scrollrect|moverect|erase|damage|sb_pushline|sb_popline|settermprop|setmousefunc) / ) { + elsif( $line =~ m/^(?:movecursor|scrollrect|moverect|erase|damage|sb_pushline|sb_popline|settermprop|setmousefunc|selection-query) / ) { # no conversion } + elsif( $line =~ m/^(selection-set) (.*?) (\[?)(.*?)(\]?)$/ ) { + $line = "$1 $2 $3" . join( "", map sprintf("%02x", $_), unpack "C*", eval($4) ) . "$5"; + } else { warn "Unrecognised test expectation '$line'\n"; } diff --git a/src/libvterm/vterm.pc.in b/src/libvterm/vterm.pc.in --- a/src/libvterm/vterm.pc.in +++ b/src/libvterm/vterm.pc.in @@ -1,6 +1,5 @@ -prefix=@PREFIX@ libdir=@LIBDIR@ -includedir=${prefix}/include +includedir=@INCDIR@ Name: vterm Description: Abstract VT220/Xterm/ECMA-48 emulation library diff --git a/src/terminal.c b/src/terminal.c --- a/src/terminal.c +++ b/src/terminal.c @@ -4526,7 +4526,10 @@ static VTermStateFallbacks state_fallbac NULL, // control parse_csi, // csi parse_osc, // osc - NULL // dcs + NULL, // dcs + NULL, // apc + NULL, // pm + NULL // sos }; /* diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 3666, +/**/ 3665, /**/ 3664,