From 0c7499bf7e34fd4aae62214d973507daa54f5988 Mon Sep 17 00:00:00 2001 From: rrrooommmaaa Date: Fri, 1 Feb 2019 20:03:41 +0300 Subject: [PATCH] Vttest 2 (#190) * added Tab Stops support * added support for Screen Mode (DECSCNM) -- reverse colors * bug fix: cursor rendition in Origin Mode * bug fix: SGR parameters handling * Save/Restore Cursor updates. Partial charset implementation. --- buffer/buffer.go | 17 +++++++++++ buffer/terminal_state.go | 3 ++ terminal/ansi.go | 4 +-- terminal/charset.go | 65 ++++++++++++++++++++++++++++++++++++++++ terminal/output.go | 20 +++++++++++-- terminal/sgr.go | 2 +- 6 files changed, 105 insertions(+), 6 deletions(-) create mode 100644 terminal/charset.go diff --git a/buffer/buffer.go b/buffer/buffer.go index af6c65c..8eb43c9 100644 --- a/buffer/buffer.go +++ b/buffer/buffer.go @@ -14,11 +14,14 @@ type Buffer struct { displayChangeHandlers []chan bool savedX uint16 savedY uint16 + savedCursorAttr *CellAttributes dirty bool selectionStart *Position selectionEnd *Position selectionComplete bool // whether the selected text can update or whether it is final terminalState *TerminalState + savedCharsets []*map[rune]rune + savedCurrentCharset int } type Position struct { @@ -355,13 +358,27 @@ func (buffer *Buffer) ScrollToEnd() { } func (buffer *Buffer) SaveCursor() { + copiedAttr := buffer.terminalState.CursorAttr + buffer.savedCursorAttr = &copiedAttr buffer.savedX = buffer.terminalState.cursorX buffer.savedY = buffer.terminalState.cursorY + buffer.savedCharsets = make([]*map[rune]rune, len(buffer.terminalState.Charsets)) + copy(buffer.savedCharsets, buffer.terminalState.Charsets) + buffer.savedCurrentCharset = buffer.terminalState.CurrentCharset } func (buffer *Buffer) RestoreCursor() { + if buffer.savedCursorAttr != nil { + copiedAttr := *buffer.savedCursorAttr + buffer.terminalState.CursorAttr = copiedAttr // @todo ignore colors? + } buffer.terminalState.cursorX = buffer.savedX buffer.terminalState.cursorY = buffer.savedY + if buffer.savedCharsets != nil { + buffer.terminalState.Charsets = make([]*map[rune]rune, len(buffer.savedCharsets)) + copy(buffer.terminalState.Charsets, buffer.savedCharsets) + buffer.terminalState.CurrentCharset = buffer.savedCurrentCharset + } } func (buffer *Buffer) CursorAttr() *CellAttributes { diff --git a/buffer/terminal_state.go b/buffer/terminal_state.go index e39f211..4883b81 100644 --- a/buffer/terminal_state.go +++ b/buffer/terminal_state.go @@ -16,6 +16,8 @@ type TerminalState struct { AutoWrap bool maxLines uint64 tabStops map[uint16]struct{} + Charsets []*map[rune]rune // array of 2 charsets, nil means ASCII (no conversion) + CurrentCharset int // active charset index in Charsets array, valid values are 0 or 1 } // NewTerminalMode creates a new terminal state @@ -30,6 +32,7 @@ func NewTerminalState(viewCols uint16, viewLines uint16, attr CellAttributes, ma viewHeight: viewLines, topMargin: 0, bottomMargin: uint(viewLines - 1), + Charsets: []*map[rune]rune{nil, nil}, LineFeedMode: true, } b.TabReset() diff --git a/terminal/ansi.go b/terminal/ansi.go index 170b1c9..3dae73d 100644 --- a/terminal/ansi.go +++ b/terminal/ansi.go @@ -17,8 +17,8 @@ var ansiSequenceMap = map[rune]escapeSequenceHandler{ 'P': sixelHandler, 'c': risHandler, //RIS '#': screenStateHandler, - '(': swallowHandler(1), // character set bullshit - ')': swallowHandler(1), // character set bullshit + '(': scs0Handler, // select character set into G0 + ')': scs1Handler, // select character set into G1 '*': swallowHandler(1), // character set bullshit '+': swallowHandler(1), // character set bullshit '>': swallowHandler(0), // numeric char selection //@todo diff --git a/terminal/charset.go b/terminal/charset.go new file mode 100644 index 0000000..08c2f2a --- /dev/null +++ b/terminal/charset.go @@ -0,0 +1,65 @@ +package terminal + +import "fmt" + +var charSets = map[rune]*map[rune]rune{ + '0': &decSpecGraphics, + 'B': nil, // ASCII + // @todo 1,2,A +} + +var decSpecGraphics = map[rune]rune{ + 0x5f: 0x00A0, // NO-BREAK SPACE + 0x60: 0x25C6, // BLACK DIAMOND + 0x61: 0x2592, // MEDIUM SHADE + 0x62: 0x2409, // SYMBOL FOR HORIZONTAL TABULATION + 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 + 0x68: 0x2424, // SYMBOL FOR NEWLINE + 0x69: 0x240B, // SYMBOL FOR VERTICAL TABULATION + 0x6a: 0x2518, // BOX DRAWINGS LIGHT UP AND LEFT + 0x6b: 0x2510, // BOX DRAWINGS LIGHT DOWN AND LEFT + 0x6c: 0x250C, // BOX DRAWINGS LIGHT DOWN AND RIGHT + 0x6d: 0x2514, // BOX DRAWINGS LIGHT UP AND RIGHT + 0x6e: 0x253C, // BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL + 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: 0x2264, // LESS-THAN OR EQUAL TO + 0x7a: 0x2265, // GREATER-THAN OR EQUAL TO + 0x7b: 0x03C0, // GREEK SMALL LETTER PI + 0x7c: 0x2260, // NOT EQUAL TO + 0x7d: 0x00A3, // POUND SIGN + 0x7e: 0x00B7, // MIDDLE DOT +} + +func scs0Handler(pty chan rune, terminal *Terminal) error { + return scsHandler(pty, terminal, 0) +} + +func scs1Handler(pty chan rune, terminal *Terminal) error { + return scsHandler(pty, terminal, 1) +} + +func scsHandler(pty chan rune, terminal *Terminal, which int) error { + b := <-pty + + cs, ok := charSets[b] + if ok { + terminal.logger.Debugf("Selected charset %v into G%v", string(b), which) + terminal.terminalState.Charsets[which] = cs + return nil + } + terminal.terminalState.Charsets[which] = nil + return fmt.Errorf("Unknown SCS charset code: 0x%02X [%v]", b, string(b)) +} diff --git a/terminal/output.go b/terminal/output.go index 3dfd3df..a528263 100644 --- a/terminal/output.go +++ b/terminal/output.go @@ -61,12 +61,14 @@ func enqHandler(terminal *Terminal) error { } func shiftOutHandler(terminal *Terminal) error { - terminal.logger.Errorf("Received shift out") + terminal.logger.Debugf("Received shift out") + terminal.terminalState.CurrentCharset = 1 return nil } func shiftInHandler(terminal *Terminal) error { - terminal.logger.Errorf("Received shift in") + terminal.logger.Debugf("Received shift in") + terminal.terminalState.CurrentCharset = 0 return nil } @@ -79,10 +81,22 @@ func (terminal *Terminal) processRune(b rune) { return } //terminal.logger.Debugf("Received character 0x%X: %q", b, string(b)) - terminal.ActiveBuffer().Write(b) + terminal.ActiveBuffer().Write(terminal.translateRune(b)) terminal.isDirty = true } +func (terminal *Terminal) translateRune(b rune) rune { + table := terminal.terminalState.Charsets[terminal.terminalState.CurrentCharset] + if table == nil { + return b + } + chr, ok := (*table)[b] + if ok { + return chr + } + return b +} + func (terminal *Terminal) processInput(pty chan rune) { // https://en.wikipedia.org/wiki/ANSI_escape_code diff --git a/terminal/sgr.go b/terminal/sgr.go index cc19451..c164a51 100644 --- a/terminal/sgr.go +++ b/terminal/sgr.go @@ -20,7 +20,7 @@ func sgrSequenceHandler(params []string, terminal *Terminal) error { p := strings.Replace(strings.Replace(params[i], "[", "", -1), "]", "", -1) switch p { - case "00", "0": + case "00", "0", "": attr := terminal.ActiveBuffer().CursorAttr() *attr = buffer.CellAttributes{ FgColour: terminal.config.ColourScheme.Foreground,