From 9c60167ca8f3ad0a7e89314ce4929eceb3498f32 Mon Sep 17 00:00:00 2001 From: rrrooommmaaa Date: Thu, 24 Jan 2019 16:03:47 +0300 Subject: [PATCH] Vttest (#162) * Correct handling of DeviceAttributes request * added DECCOLM support * added DECALN control sequence support * added NEL support * bug fix: Erase To Cursor should be inclusive * added support for 'Origin Mode' (DECOM) -- top/bottom margins only * vttest test 1 screen 3: margin tests fixes * added support for intermediate controls inside CSI sequence * added support for LNM (Line Feed/New Line Mode) * removed obsolete 'intermediate' parameter * window resize on programmatic CSI resize * DECCOLM should clear screen on both set and reset * bug fix in autowrap mode * TestCursorMovement runs all test cases; screen template images updated Signed-off-by: Max Risuhin * bug fix: line mode messing with autowrap * added ResetVerticalMargins() method * IsAutoWrap(), IsNewLineMode() * corrected DECALN * fixed NEL to work in Line Feed mode * tyding up: removed map of 1 element --- buffer/buffer.go | 114 ++++++++++++++++++++++++------ buffer/buffer_test.go | 4 +- gui/gui.go | 70 +++++++++++++++--- gui/input.go | 8 +-- gui/renderer.go | 7 ++ main_test.go | 22 ++++++ terminal/ansi.go | 9 ++- terminal/csi.go | 70 +++++++++--------- terminal/modes.go | 39 +++++++++- terminal/output.go | 73 +++++++++++-------- terminal/scr_state.go | 30 ++++++++ terminal/sgr.go | 4 +- terminal/terminal.go | 36 ++++++++-- vttest/test-cursor-movement-1.png | Bin 0 -> 9756 bytes vttest/test-cursor-movement-2.png | Bin 16567 -> 16736 bytes vttest/test-cursor-movement-3.png | Bin 0 -> 10553 bytes vttest/test-cursor-movement-4.png | Bin 0 -> 12457 bytes vttest/test-cursor-movement-5.png | Bin 0 -> 7812 bytes vttest/test-cursor-movement-6.png | Bin 0 -> 8246 bytes 19 files changed, 375 insertions(+), 111 deletions(-) create mode 100644 terminal/scr_state.go create mode 100644 vttest/test-cursor-movement-1.png create mode 100644 vttest/test-cursor-movement-3.png create mode 100644 vttest/test-cursor-movement-4.png create mode 100644 vttest/test-cursor-movement-5.png create mode 100644 vttest/test-cursor-movement-6.png diff --git a/buffer/buffer.go b/buffer/buffer.go index d4deadd..08389e0 100644 --- a/buffer/buffer.go +++ b/buffer/buffer.go @@ -23,6 +23,8 @@ type Buffer struct { topMargin uint // see DECSTBM docs - this is for scrollable regions bottomMargin uint // see DECSTBM docs - this is for scrollable regions replaceMode bool // overwrite character at cursor or insert new + originMode bool // see DECOM docs - whether cursor is positioned within the margins or not + lineFeedMode bool autoWrap bool dirty bool selectionStart *Position @@ -317,6 +319,15 @@ func (buffer *Buffer) SetAutoWrap(enabled bool) { buffer.autoWrap = enabled } +func (buffer *Buffer) IsAutoWrap() bool { + return buffer.autoWrap +} + +func (buffer *Buffer) SetOriginMode(enabled bool) { + buffer.originMode = enabled + buffer.SetPosition(0, 0) +} + func (buffer *Buffer) SetInsertMode() { buffer.replaceMode = false } @@ -330,6 +341,11 @@ func (buffer *Buffer) SetVerticalMargins(top uint, bottom uint) { buffer.bottomMargin = bottom } +// ResetVerticalMargins resets margins to extreme positions +func (buffer *Buffer) ResetVerticalMargins() { + buffer.SetVerticalMargins(0, uint(buffer.viewHeight-1)) +} + func (buffer *Buffer) GetScrollOffset() uint { return buffer.scrollLinesFromBottom } @@ -419,11 +435,19 @@ func (buffer *Buffer) emitDisplayChange() { // Column returns cursor column func (buffer *Buffer) CursorColumn() uint16 { + // @todo originMode and left margin return buffer.cursorX } // Line returns cursor line func (buffer *Buffer) CursorLine() uint16 { + if buffer.originMode { + result := buffer.cursorY - uint16(buffer.topMargin) + if result < 0 { + result = 0 + } + return result + } return buffer.cursorY } @@ -492,8 +516,8 @@ func (buffer *Buffer) insertLine() { out := make([]Line, newLineCount) copy( - out[ : pos - ( uint64(len(buffer.lines)) + 1 - newLineCount )], - buffer.lines[ uint64(len(buffer.lines)) + 1 - newLineCount : pos] ) + out[:pos-(uint64(len(buffer.lines))+1-newLineCount)], + buffer.lines[uint64(len(buffer.lines))+1-newLineCount:pos]) out[pos] = newLine() copy(out[pos+1:], buffer.lines[pos:]) buffer.lines = out @@ -655,13 +679,13 @@ func (buffer *Buffer) Write(runes ...rune) { if buffer.autoWrap { - buffer.NewLine() + buffer.NewLineEx(true) newLine := buffer.getCurrentLine() if len(newLine.cells) == 0 { - newLine.cells = []Cell{{}} + newLine.cells = append(newLine.cells, buffer.defaultCell) } - cell := &newLine.cells[buffer.CursorColumn()] + cell := &newLine.cells[0] cell.setRune(r) cell.attr = buffer.cursorAttr @@ -694,6 +718,13 @@ func (buffer *Buffer) incrementCursorPosition() { } } +func (buffer *Buffer) inDoWrap() bool { + // xterm uses 'do_wrap' flag for this special terminal state + // we use the cursor position right after the boundary + // let's see how it works out + return buffer.cursorX == buffer.viewWidth // @todo rightMargin +} + func (buffer *Buffer) Backspace() { if buffer.cursorX == 0 { @@ -703,6 +734,9 @@ func (buffer *Buffer) Backspace() { } else { //@todo ring bell or whatever - actually i think the pty will trigger this } + } else if buffer.inDoWrap() { + // the "do_wrap" implementation + buffer.MovePosition(-2, 0) } else { buffer.MovePosition(-1, 0) } @@ -727,15 +761,33 @@ func (buffer *Buffer) CarriageReturn() { func (buffer *Buffer) Tab() { tabSize := 4 + max := tabSize + + // @todo rightMargin + if buffer.cursorX < buffer.viewWidth { + max = int(buffer.viewWidth - buffer.cursorX - 1) + } + shift := tabSize - (int(buffer.cursorX+1) % tabSize) + + if shift > max { + shift = max + } + for i := 0; i < shift; i++ { buffer.Write(' ') } } func (buffer *Buffer) NewLine() { + buffer.NewLineEx(false) +} - buffer.cursorX = 0 +func (buffer *Buffer) NewLineEx(forceCursorToMargin bool) { + + if buffer.IsNewLineMode() || forceCursorToMargin { + buffer.cursorX = 0 + } buffer.Index() for { @@ -747,21 +799,34 @@ func (buffer *Buffer) NewLine() { } } +func (buffer *Buffer) SetNewLineMode() { + buffer.lineFeedMode = false +} + +func (buffer *Buffer) SetLineFeedMode() { + buffer.lineFeedMode = true +} + +func (buffer *Buffer) IsNewLineMode() bool { + return buffer.lineFeedMode == false +} + func (buffer *Buffer) MovePosition(x int16, y int16) { var toX uint16 var toY uint16 - if int16(buffer.cursorX)+x < 0 { + if int16(buffer.CursorColumn())+x < 0 { toX = 0 } else { - toX = uint16(int16(buffer.cursorX) + x) + toX = uint16(int16(buffer.CursorColumn()) + x) } - if int16(buffer.cursorY)+y < 0 { + // should either use CursorLine() and SetPosition() or use absolutes, mind Origin Mode (DECOM) + if int16(buffer.CursorLine())+y < 0 { toY = 0 } else { - toY = uint16(int16(buffer.cursorY) + y) + toY = uint16(int16(buffer.CursorLine()) + y) } buffer.SetPosition(toX, toY) @@ -770,17 +835,26 @@ func (buffer *Buffer) MovePosition(x int16, y int16) { func (buffer *Buffer) SetPosition(col uint16, line uint16) { defer buffer.emitDisplayChange() - if col >= buffer.ViewWidth() { - col = buffer.ViewWidth() - 1 - //logrus.Errorf("Cannot set cursor position: column %d is outside of the current view width (%d columns)", col, buffer.ViewWidth()) + useCol := col + useLine := line + maxLine := buffer.ViewHeight() - 1 + + if buffer.originMode { + useLine += uint16(buffer.topMargin) + maxLine = uint16(buffer.bottomMargin) + // @todo left and right margins } - if line >= buffer.ViewHeight() { - line = buffer.ViewHeight() - 1 - //logrus.Errorf("Cannot set cursor position: line %d is outside of the current view height (%d lines)", line, buffer.ViewHeight()) + if useLine > maxLine { + useLine = maxLine } - buffer.cursorX = col - buffer.cursorY = line + if useCol >= buffer.ViewWidth() { + useCol = buffer.ViewWidth() - 1 + //logrus.Errorf("Cannot set cursor position: column %d is outside of the current view width (%d columns)", col, buffer.ViewWidth()) + } + + buffer.cursorX = useCol + buffer.cursorY = useLine } func (buffer *Buffer) GetVisibleLines() []Line { @@ -928,7 +1002,7 @@ func (buffer *Buffer) EraseDisplayToCursor() { defer buffer.emitDisplayChange() line := buffer.getCurrentLine() - for i := 0; i < int(buffer.cursorX); i++ { + for i := 0; i <= int(buffer.cursorX); i++ { if i >= len(line.cells) { break } @@ -1042,7 +1116,7 @@ func (buffer *Buffer) ResizeView(width uint16, height uint16) { line = buffer.getCurrentLine() buffer.cursorX = uint16((len(line.cells) - cXFromEndOfLine) - 1) - buffer.SetVerticalMargins(0, uint(buffer.viewHeight-1)) + buffer.ResetVerticalMargins() } func (buffer *Buffer) getMaxLines() uint64 { diff --git a/buffer/buffer_test.go b/buffer/buffer_test.go index 523635c..1b48f2e 100644 --- a/buffer/buffer_test.go +++ b/buffer/buffer_test.go @@ -508,7 +508,7 @@ func TestEraseDisplayToCursor(t *testing.T) { lines := b.GetVisibleLines() assert.Equal(t, "", lines[0].String()) assert.Equal(t, "", lines[1].String()) - assert.Equal(t, "\x00\x00\x00ng", lines[2].String()) + assert.Equal(t, "\x00\x00\x00\x00g", lines[2].String()) } @@ -634,4 +634,4 @@ func TestBufferMaxLines(t *testing.T) { assert.Equal(t, 2, len(b.lines)) assert.Equal(t, "funny", b.lines[0].String()) assert.Equal(t, "world", b.lines[1].String()) -} \ No newline at end of file +} diff --git a/gui/gui.go b/gui/gui.go index e6fca8a..204ad6b 100644 --- a/gui/gui.go +++ b/gui/gui.go @@ -2,6 +2,7 @@ package gui import ( "fmt" + "math" "image" "image/png" "os" @@ -28,8 +29,9 @@ type GUI struct { logger *zap.SugaredLogger config *config.Config terminal *terminal.Terminal - width int //window width in pixels - height int //window height in pixels + width int //window width in pixels + height int //window height in pixels + resizeCache *ResizeCache // resize cache formed by resizeToTerminal() dpiScale float32 fontMap *FontMap fontScale float32 @@ -59,6 +61,13 @@ func Max(x, y int) int { return y } +type ResizeCache struct { + Width int + Height int + Cols uint + Rows uint +} + func (g *GUI) GetMonitor() *glfw.Monitor { if g.window == nil { @@ -78,8 +87,8 @@ func (g *GUI) GetMonitor() *glfw.Monitor { for _, monitor := range monitors { mode := monitor.GetVideoMode() mx, my := monitor.GetPos() - overlap := Max(0, Min(x + w, mx + mode.Width) - Max(x, mx)) * - Max(0, Min(y + h, my + mode.Height) - Max(y, my)) + overlap := Max(0, Min(x+w, mx+mode.Width)-Max(x, mx)) * + Max(0, Min(y+h, my+mode.Height)-Max(y, my)) if bestMatch < overlap { bestMatch = overlap currentMonitor = monitor @@ -153,6 +162,35 @@ func (gui *GUI) scale() float32 { return float32(ww) / float32(pw) } +// can only be called on OS thread +func (gui *GUI) resizeToTerminal(newCols uint, newRows uint) { + + if gui.window.GetAttrib(glfw.Iconified) != 0 { + return + } + + gui.resizeLock.Lock() + defer gui.resizeLock.Unlock() + + cols, rows := gui.renderer.GetTermSize() + if cols == newCols && rows == newRows { + return + } + + gui.logger.Debugf("Initiating GUI resize to columns=%d rows=%d", newCols, newRows) + + gui.logger.Debugf("Calculating size...") + width, height := gui.renderer.GetRectangleSize(newCols, newRows) + + roundedWidth := int(math.Ceil(float64(width))) + roundedHeight := int(math.Ceil(float64(height))) + + gui.resizeCache = &ResizeCache{roundedWidth, roundedHeight, newCols, newRows} + + gui.logger.Debugf("Resizing window to %dx%d", roundedWidth, roundedHeight) + gui.window.SetSize(roundedWidth, roundedHeight) // will trigger resize() +} + // can only be called on OS thread func (gui *GUI) resize(w *glfw.Window, width int, height int) { @@ -174,14 +212,19 @@ func (gui *GUI) resize(w *glfw.Window, width int, height int) { gui.logger.Debugf("Setting renderer area...") gui.renderer.SetArea(0, 0, gui.Width(), gui.Height()) - gui.logger.Debugf("Calculating size in cols/rows...") - cols, rows := gui.renderer.GetTermSize() - - gui.logger.Debugf("Resizing internal terminal...") - if err := gui.terminal.SetSize(cols, rows); err != nil { - gui.logger.Errorf("Failed to resize terminal to %d cols, %d rows: %s", cols, rows, err) + if gui.resizeCache != nil && gui.resizeCache.Width == width && gui.resizeCache.Height == height { + gui.logger.Debugf("No need to resize internal terminal!") + } else { + gui.logger.Debugf("Calculating size in cols/rows...") + cols, rows := gui.renderer.GetTermSize() + gui.logger.Debugf("Resizing internal terminal...") + if err := gui.terminal.SetSize(cols, rows); err != nil { + gui.logger.Errorf("Failed to resize terminal to %d cols, %d rows: %s", cols, rows, err) + } } + gui.resizeCache = nil + gui.logger.Debugf("Setting viewport size...") gl.Viewport(0, 0, int32(gui.Width()), int32(gui.Height())) @@ -234,6 +277,7 @@ func (gui *GUI) Render() error { } titleChan := make(chan bool, 1) + resizeChan := make(chan bool, 1) gui.renderer = NewOpenGLRenderer(gui.config, gui.fontMap, 0, 0, gui.Width(), gui.Height(), gui.colourAttr, program) @@ -283,6 +327,7 @@ func (gui *GUI) Render() error { ) gui.terminal.AttachTitleChangeHandler(titleChan) + gui.terminal.AttachResizeHandler(resizeChan) ticker := time.NewTicker(time.Second) defer ticker.Stop() @@ -316,6 +361,9 @@ func (gui *GUI) Render() error { select { case <-titleChan: gui.window.SetTitle(gui.terminal.GetTitle()) + case <-resizeChan: + cols, rows := gui.terminal.GetSize() + gui.resizeToTerminal(uint(cols), uint(rows)) default: // this is more efficient than glfw.PollEvents() glfw.WaitEventsTimeout(0.02) // up to 50fps on no input, otherwise higher @@ -497,7 +545,7 @@ func (gui *GUI) createWindow() (*glfw.Window, error) { return nil, fmt.Errorf("failed to create window, please update your graphics drivers and try again") } - window.SetSizeLimits(int(300 * gui.dpiScale), int(150 * gui.dpiScale), 10000, 10000) + window.SetSizeLimits(int(300*gui.dpiScale), int(150*gui.dpiScale), 10000, 10000) window.MakeContextCurrent() window.Show() window.Focus() diff --git a/gui/input.go b/gui/input.go index 9b2515d..ab97d68 100644 --- a/gui/input.go +++ b/gui/input.go @@ -212,9 +212,7 @@ func (gui *GUI) key(w *glfw.Window, key glfw.Key, scancode int, action glfw.Acti 0x09, }) case glfw.KeyEnter: - gui.terminal.Write([]byte{ - 0x0d, - }) + gui.terminal.WriteReturn() case glfw.KeyKPEnter: if gui.terminal.IsApplicationCursorKeysModeEnabled() { gui.terminal.Write([]byte{ @@ -223,9 +221,7 @@ func (gui *GUI) key(w *glfw.Window, key glfw.Key, scancode int, action glfw.Acti 'M', }) } else { - gui.terminal.Write([]byte{ - 0x0d, - }) + gui.terminal.WriteReturn() } case glfw.KeyBackspace: if modsPressed(mods, glfw.ModAlt) { diff --git a/gui/renderer.go b/gui/renderer.go index 5a87e26..0f9ab5e 100644 --- a/gui/renderer.go +++ b/gui/renderer.go @@ -176,6 +176,13 @@ func (r *OpenGLRenderer) SetArea(areaX int, areaY int, areaWidth int, areaHeight r.termRows = uint(math.Floor(float64(float32(r.areaHeight) / r.cellHeight))) } +func (r *OpenGLRenderer) GetRectangleSize(col uint, row uint) (float32, float32) { + x := float32(float32(col) * r.cellWidth) + y := float32(float32(row) * r.cellHeight) + + return x, y +} + func (r *OpenGLRenderer) getRectangle(col uint, row uint) *rectangle { x := float32(float32(col) * r.cellWidth) y := float32(float32(row) * r.cellHeight) + r.cellHeight diff --git a/main_test.go b/main_test.go index 1eef9ea..152906f 100644 --- a/main_test.go +++ b/main_test.go @@ -65,11 +65,33 @@ func TestCursorMovement(t *testing.T) { if term.ActiveBuffer().Compare("vttest/test-cursor-movement-1") == false { t.Error(fmt.Sprint("ActiveBuffer doesn't match vttest template")) } + g.Screenshot ("test-cursor-movement-1.png") + compareImages("vttest/test-cursor-movement-1.png", "test-cursor-movement-1.png") enter(term) sleep() g.Screenshot ("test-cursor-movement-2.png") compareImages("vttest/test-cursor-movement-2.png", "test-cursor-movement-2.png") + + enter(term) + sleep() + g.Screenshot ("test-cursor-movement-3.png") + compareImages("vttest/test-cursor-movement-3.png", "test-cursor-movement-3.png") + + enter(term) + sleep() + g.Screenshot ("test-cursor-movement-4.png") + compareImages("vttest/test-cursor-movement-4.png", "test-cursor-movement-4.png") + + enter(term) + sleep() + g.Screenshot ("test-cursor-movement-5.png") + compareImages("vttest/test-cursor-movement-5.png", "test-cursor-movement-5.png") + + enter(term) + sleep() + g.Screenshot ("test-cursor-movement-6.png") + compareImages("vttest/test-cursor-movement-6.png", "test-cursor-movement-6.png") os.Exit(0) } diff --git a/terminal/ansi.go b/terminal/ansi.go index c05af3f..e56ddb1 100644 --- a/terminal/ansi.go +++ b/terminal/ansi.go @@ -11,9 +11,11 @@ var ansiSequenceMap = map[rune]escapeSequenceHandler{ '7': saveCursorHandler, '8': restoreCursorHandler, 'D': indexHandler, + 'E': nextLineHandler, // NEL 'M': reverseIndexHandler, 'P': sixelHandler, - 'c': risHandler, //RIS + 'c': risHandler, //RIS + '#': screenStateHandler, '(': swallowHandler(1), // character set bullshit ')': swallowHandler(1), // character set bullshit '*': swallowHandler(1), // character set bullshit @@ -68,3 +70,8 @@ func ansiHandler(pty chan rune, terminal *Terminal) error { return fmt.Errorf("Unknown ANSI control sequence byte: 0x%02X [%v]", b, string(b)) } + +func nextLineHandler(pty chan rune, terminal *Terminal) error { + terminal.ActiveBuffer().NewLineEx(true) + return nil +} diff --git a/terminal/csi.go b/terminal/csi.go index 7ed515d..ea39a6f 100644 --- a/terminal/csi.go +++ b/terminal/csi.go @@ -6,7 +6,7 @@ import ( "strings" ) -type csiSequenceHandler func(params []string, intermediate string, terminal *Terminal) error +type csiSequenceHandler func(params []string, terminal *Terminal) error type csiMapping struct { id rune @@ -56,19 +56,18 @@ type runeRange struct { var csiTerminators = runeRange{0x40, 0x7e} -func loadCSI(pty chan rune) (final rune, param string, intermediate string) { +func loadCSI(pty chan rune) (final rune, param string, intermediate []rune) { var b rune param = "" - intermediate = "" + intermediate = []rune{} CSI: for { b = <-pty switch true { case b >= 0x30 && b <= 0x3F: param = param + string(b) - case b >= 0x20 && b <= 0x2F: - //intermediate? useful? - intermediate += string(b) + case b > 0 && b <= 0x2F: + intermediate = append(intermediate, b) case b >= csiTerminators.min && b <= csiTerminators.max: final = b break CSI @@ -89,6 +88,11 @@ func splitParams(paramString string) []string { func csiHandler(pty chan rune, terminal *Terminal) error { final, param, intermediate := loadCSI(pty) + // process intermediate control codes before the CSI + for _, b := range intermediate { + terminal.processRune(b) + } + params := splitParams(param) for _, sequence := range csiSequences { @@ -97,16 +101,16 @@ func csiHandler(pty chan rune, terminal *Terminal) error { continue } x, y := terminal.ActiveBuffer().CursorColumn(), terminal.ActiveBuffer().CursorLine() - err := sequence.handler(params, intermediate, terminal) - terminal.logger.Debugf("CSI 0x%02X (ESC[%s%s%s) %s - %d,%d -> %d,%d", final, param, intermediate, string(final), sequence.description, x, y, terminal.ActiveBuffer().CursorColumn(), terminal.ActiveBuffer().CursorLine()) + err := sequence.handler(params, terminal) + terminal.logger.Debugf("CSI 0x%02X (ESC[%s%s) %s - %d,%d -> %d,%d", final, param, string(final), sequence.description, x, y, terminal.ActiveBuffer().CursorColumn(), terminal.ActiveBuffer().CursorLine()) return err } } - return fmt.Errorf("Unknown CSI control sequence: 0x%02X (ESC[%s%s%s)", final, param, intermediate, string(final)) + return fmt.Errorf("Unknown CSI control sequence: 0x%02X (ESC[%s%s)", final, param, string(final)) } -func csiSendDeviceAttributesHandler(params []string, intermediate string, terminal *Terminal) error { +func csiSendDeviceAttributesHandler(params []string, terminal *Terminal) error { // we are VT100 // for DA1 we'll respond 1;2 @@ -132,7 +136,7 @@ func csiSendDeviceAttributesHandler(params []string, intermediate string, termin return nil } -func csiDeviceStatusReportHandler(params []string, intermediate string, terminal *Terminal) error { +func csiDeviceStatusReportHandler(params []string, terminal *Terminal) error { if len(params) == 0 { return fmt.Errorf("Missing Device Status Report identifier") @@ -154,7 +158,7 @@ func csiDeviceStatusReportHandler(params []string, intermediate string, terminal return nil } -func csiCursorUpHandler(params []string, intermediate string, terminal *Terminal) error { +func csiCursorUpHandler(params []string, terminal *Terminal) error { distance := 1 if len(params) > 0 { var err error @@ -167,7 +171,7 @@ func csiCursorUpHandler(params []string, intermediate string, terminal *Terminal return nil } -func csiCursorDownHandler(params []string, intermediate string, terminal *Terminal) error { +func csiCursorDownHandler(params []string, terminal *Terminal) error { distance := 1 if len(params) > 0 { var err error @@ -181,7 +185,7 @@ func csiCursorDownHandler(params []string, intermediate string, terminal *Termin return nil } -func csiCursorForwardHandler(params []string, intermediate string, terminal *Terminal) error { +func csiCursorForwardHandler(params []string, terminal *Terminal) error { distance := 1 if len(params) > 0 { var err error @@ -195,7 +199,7 @@ func csiCursorForwardHandler(params []string, intermediate string, terminal *Ter return nil } -func csiCursorBackwardHandler(params []string, intermediate string, terminal *Terminal) error { +func csiCursorBackwardHandler(params []string, terminal *Terminal) error { distance := 1 if len(params) > 0 { var err error @@ -209,7 +213,7 @@ func csiCursorBackwardHandler(params []string, intermediate string, terminal *Te return nil } -func csiCursorNextLineHandler(params []string, intermediate string, terminal *Terminal) error { +func csiCursorNextLineHandler(params []string, terminal *Terminal) error { distance := 1 if len(params) > 0 { @@ -225,7 +229,7 @@ func csiCursorNextLineHandler(params []string, intermediate string, terminal *Te return nil } -func csiCursorPrecedingLineHandler(params []string, intermediate string, terminal *Terminal) error { +func csiCursorPrecedingLineHandler(params []string, terminal *Terminal) error { distance := 1 if len(params) > 0 { @@ -240,7 +244,7 @@ func csiCursorPrecedingLineHandler(params []string, intermediate string, termina return nil } -func csiCursorCharacterAbsoluteHandler(params []string, intermediate string, terminal *Terminal) error { +func csiCursorCharacterAbsoluteHandler(params []string, terminal *Terminal) error { distance := 1 if len(params) > 0 { var err error @@ -274,14 +278,14 @@ func parseCursorPosition(params []string) (x, y int) { return x, y } -func csiCursorPositionHandler(params []string, intermediate string, terminal *Terminal) error { +func csiCursorPositionHandler(params []string, terminal *Terminal) error { x, y := parseCursorPosition(params) terminal.ActiveBuffer().SetPosition(uint16(x-1), uint16(y-1)) return nil } -func csiScrollUpHandler(params []string, intermediate string, terminal *Terminal) error { +func csiScrollUpHandler(params []string, terminal *Terminal) error { distance := 1 if len(params) > 1 { return fmt.Errorf("Not supported") @@ -298,7 +302,7 @@ func csiScrollUpHandler(params []string, intermediate string, terminal *Terminal return nil } -func csiInsertBlankCharactersHandler(params []string, intermediate string, terminal *Terminal) error { +func csiInsertBlankCharactersHandler(params []string, terminal *Terminal) error { count := 1 if len(params) > 1 { return fmt.Errorf("Not supported") @@ -316,7 +320,7 @@ func csiInsertBlankCharactersHandler(params []string, intermediate string, termi return nil } -func csiInsertLinesHandler(params []string, intermediate string, terminal *Terminal) error { +func csiInsertLinesHandler(params []string, terminal *Terminal) error { count := 1 if len(params) > 1 { return fmt.Errorf("Not supported") @@ -334,7 +338,7 @@ func csiInsertLinesHandler(params []string, intermediate string, terminal *Termi return nil } -func csiDeleteLinesHandler(params []string, intermediate string, terminal *Terminal) error { +func csiDeleteLinesHandler(params []string, terminal *Terminal) error { count := 1 if len(params) > 1 { return fmt.Errorf("Not supported") @@ -352,7 +356,7 @@ func csiDeleteLinesHandler(params []string, intermediate string, terminal *Termi return nil } -func csiScrollDownHandler(params []string, intermediate string, terminal *Terminal) error { +func csiScrollDownHandler(params []string, terminal *Terminal) error { distance := 1 if len(params) > 1 { return fmt.Errorf("Not supported") @@ -370,7 +374,7 @@ func csiScrollDownHandler(params []string, intermediate string, terminal *Termin } // DECSTBM -func csiSetMarginsHandler(params []string, intermediate string, terminal *Terminal) error { +func csiSetMarginsHandler(params []string, terminal *Terminal) error { top := 1 bottom := int(terminal.ActiveBuffer().ViewHeight()) @@ -402,7 +406,7 @@ func csiSetMarginsHandler(params []string, intermediate string, terminal *Termin return nil } -func csiEraseCharactersHandler(params []string, intermediate string, terminal *Terminal) error { +func csiEraseCharactersHandler(params []string, terminal *Terminal) error { count := 1 if len(params) > 0 { var err error @@ -417,19 +421,19 @@ func csiEraseCharactersHandler(params []string, intermediate string, terminal *T return nil } -func csiResetModeHandler(params []string, intermediate string, terminal *Terminal) error { +func csiResetModeHandler(params []string, terminal *Terminal) error { return csiSetMode(strings.Join(params, ""), false, terminal) } -func csiSetModeHandler(params []string, intermediate string, terminal *Terminal) error { +func csiSetModeHandler(params []string, terminal *Terminal) error { return csiSetMode(strings.Join(params, ""), true, terminal) } -func csiWindowManipulation(params []string, intermediate string, terminal *Terminal) error { +func csiWindowManipulation(params []string, terminal *Terminal) error { return fmt.Errorf("Window manipulation is not yet supported") } -func csiLinePositionAbsolute(params []string, intermediate string, terminal *Terminal) error { +func csiLinePositionAbsolute(params []string, terminal *Terminal) error { row := 1 if len(params) > 0 { var err error @@ -444,7 +448,7 @@ func csiLinePositionAbsolute(params []string, intermediate string, terminal *Ter return nil } -func csiDeleteHandler(params []string, intermediate string, terminal *Terminal) error { +func csiDeleteHandler(params []string, terminal *Terminal) error { n := 1 if len(params) >= 1 { var err error @@ -459,7 +463,7 @@ func csiDeleteHandler(params []string, intermediate string, terminal *Terminal) } // CSI Ps J -func csiEraseInDisplayHandler(params []string, intermediate string, terminal *Terminal) error { +func csiEraseInDisplayHandler(params []string, terminal *Terminal) error { n := "0" if len(params) > 0 { n = params[0] @@ -481,7 +485,7 @@ func csiEraseInDisplayHandler(params []string, intermediate string, terminal *Te } // CSI Ps K -func csiEraseInLineHandler(params []string, intermediate string, terminal *Terminal) error { +func csiEraseInLineHandler(params []string, terminal *Terminal) error { n := "0" if len(params) > 0 { diff --git a/terminal/modes.go b/terminal/modes.go index 1a95468..ae69664 100644 --- a/terminal/modes.go +++ b/terminal/modes.go @@ -29,8 +29,39 @@ func csiSetMode(modeStr string, enabled bool, terminal *Terminal) error { } else { terminal.ActiveBuffer().SetReplaceMode() } + case "20": + if enabled { + terminal.ActiveBuffer().SetNewLineMode() + } else { + terminal.ActiveBuffer().SetLineFeedMode() + } case "?1": terminal.modes.ApplicationCursorKeys = enabled + case "?3": + _, lines := terminal.GetSize() + if enabled { + // DECCOLM - COLumn mode, 132 characters per line + terminal.SetSize(132, uint(lines)) + } else { + // DECCOLM - 80 characters per line (erases screen) + terminal.SetSize(80, uint(lines)) + } + terminal.Clear() + /* + case "?4": + // DECSCLM + // @todo smooth scrolling / jump scrolling + case "?5": + // DECSCNM + if enabled { + // @todo SCreeN Mode, black on white background + } else { + // @todo Normal screen (white on black background) + } + */ + case "?6": + // DECOM + terminal.ActiveBuffer().SetOriginMode(enabled) case "?7": // auto-wrap mode //DECAWM @@ -78,7 +109,13 @@ func csiSetMode(modeStr string, enabled bool, terminal *Terminal) error { case "?2004": terminal.SetBracketedPasteMode(enabled) default: - return fmt.Errorf("Unsupported CSI %sl code", modeStr) + code := "" + if enabled { + code = "h" + } else { + code = "l" + } + return fmt.Errorf("Unsupported CSI %s%s code", modeStr, code) } return nil diff --git a/terminal/output.go b/terminal/output.go index 9dd19a3..3dfd3df 100644 --- a/terminal/output.go +++ b/terminal/output.go @@ -8,66 +8,81 @@ import ( type TerminalCharSet int +// single rune handler +type runeHandler func(terminal *Terminal) error + type escapeSequenceHandler func(pty chan rune, terminal *Terminal) error -var escapeSequenceMap = map[rune]escapeSequenceHandler{ - 0x05: enqSequenceHandler, - 0x07: bellSequenceHandler, - 0x08: backspaceSequenceHandler, - 0x09: tabSequenceHandler, - 0x0a: newLineSequenceHandler, - 0x0b: newLineSequenceHandler, - 0x0c: newLineSequenceHandler, - 0x0d: carriageReturnSequenceHandler, - 0x0e: shiftOutSequenceHandler, - 0x0f: shiftInSequenceHandler, - 0x1b: ansiHandler, +var runeMap = map[rune]runeHandler{ + 0x05: enqHandler, + 0x07: bellHandler, + 0x08: backspaceHandler, + 0x09: tabHandler, + 0x0a: newLineHandler, + 0x0b: newLineHandler, + 0x0c: newLineHandler, + 0x0d: carriageReturnHandler, + 0x0e: shiftOutHandler, + 0x0f: shiftInHandler, } -func newLineSequenceHandler(pty chan rune, terminal *Terminal) error { +func newLineHandler(terminal *Terminal) error { terminal.ActiveBuffer().NewLine() terminal.isDirty = true return nil } -func tabSequenceHandler(pty chan rune, terminal *Terminal) error { +func tabHandler(terminal *Terminal) error { terminal.ActiveBuffer().Tab() terminal.isDirty = true return nil } -func carriageReturnSequenceHandler(pty chan rune, terminal *Terminal) error { +func carriageReturnHandler(terminal *Terminal) error { terminal.ActiveBuffer().CarriageReturn() terminal.isDirty = true return nil } -func backspaceSequenceHandler(pty chan rune, terminal *Terminal) error { +func backspaceHandler(terminal *Terminal) error { terminal.ActiveBuffer().Backspace() terminal.isDirty = true return nil } -func bellSequenceHandler(pty chan rune, terminal *Terminal) error { +func bellHandler(terminal *Terminal) error { // @todo ring bell - flash red or some shit? return nil } -func enqSequenceHandler(pty chan rune, terminal *Terminal) error { +func enqHandler(terminal *Terminal) error { terminal.logger.Errorf("Received ENQ!") return nil } -func shiftOutSequenceHandler(pty chan rune, terminal *Terminal) error { +func shiftOutHandler(terminal *Terminal) error { terminal.logger.Errorf("Received shift out") return nil } -func shiftInSequenceHandler(pty chan rune, terminal *Terminal) error { +func shiftInHandler(terminal *Terminal) error { terminal.logger.Errorf("Received shift in") return nil } +func (terminal *Terminal) processRune(b rune) { + if handler, ok := runeMap[b]; ok { + if err := handler(terminal); err != nil { + terminal.logger.Errorf("Error handling control code: %s", err) + } + terminal.isDirty = true + return + } + //terminal.logger.Debugf("Received character 0x%X: %q", b, string(b)) + terminal.ActiveBuffer().Write(b) + terminal.isDirty = true +} + func (terminal *Terminal) processInput(pty chan rune) { // https://en.wikipedia.org/wiki/ANSI_escape_code @@ -82,19 +97,15 @@ func (terminal *Terminal) processInput(pty chan rune) { b = <-pty - if b < 0x20 { - if handler, ok := escapeSequenceMap[b]; ok { - //terminal.logger.Debugf("Handling escape sequence: 0x%x", b) - if err := handler(pty, terminal); err != nil { - terminal.logger.Errorf("Error handling escape sequence: %s", err) - } - terminal.isDirty = true - continue + if b == 0x1b { + //terminal.logger.Debugf("Handling escape sequence: 0x%x", b) + if err := ansiHandler(pty, terminal); err != nil { + terminal.logger.Errorf("Error handling escape sequence: %s", err) } + terminal.isDirty = true + continue } - //terminal.logger.Debugf("Received character 0x%X: %q", b, string(b)) - terminal.ActiveBuffer().Write(b) - terminal.isDirty = true + terminal.processRune(b) } } diff --git a/terminal/scr_state.go b/terminal/scr_state.go new file mode 100644 index 0000000..752c20d --- /dev/null +++ b/terminal/scr_state.go @@ -0,0 +1,30 @@ +package terminal + +import "fmt" + +func screenStateHandler(pty chan rune, terminal *Terminal) error { + b := <-pty + switch b { + case '8': // DECALN -- Screen Alignment Pattern + // hide cursor? + buffer := terminal.ActiveBuffer() + buffer.ResetVerticalMargins() + buffer.ScrollToEnd() + + // Fill the whole screen with E's + count := buffer.ViewHeight() * buffer.ViewWidth() + for count > 0 { + buffer.Write('E') + count-- + if count > 0 && !buffer.IsAutoWrap() && count%buffer.ViewWidth() == 0 { + buffer.Index() + buffer.CarriageReturn() + } + } + // restore cursor + buffer.SetPosition(0, 0) + default: + return fmt.Errorf("Screen State code not supported: 0x%02X [%v]", b, string(b)) + } + return nil +} diff --git a/terminal/sgr.go b/terminal/sgr.go index b136679..87c40db 100644 --- a/terminal/sgr.go +++ b/terminal/sgr.go @@ -9,7 +9,7 @@ import ( "github.com/liamg/aminal/config" ) -func sgrSequenceHandler(params []string, intermediate string, terminal *Terminal) error { +func sgrSequenceHandler(params []string, terminal *Terminal) error { if len(params) == 0 { params = []string{"0"} @@ -137,7 +137,7 @@ func sgrSequenceHandler(params []string, intermediate string, terminal *Terminal terminal.ActiveBuffer().CursorAttr().BgColour = c return nil default: - return fmt.Errorf("Unknown SGR control sequence: (ESC[%s%sm)", params[i:], intermediate) + return fmt.Errorf("Unknown SGR control sequence: (ESC[%sm)", params[i:]) } } diff --git a/terminal/terminal.go b/terminal/terminal.go index 6b9e31a..68b0c44 100644 --- a/terminal/terminal.go +++ b/terminal/terminal.go @@ -3,12 +3,13 @@ package terminal import ( "bufio" "fmt" + "io" + "sync" + "github.com/liamg/aminal/buffer" "github.com/liamg/aminal/config" "github.com/liamg/aminal/platform" "go.uber.org/zap" - "io" - "sync" ) const ( @@ -39,6 +40,7 @@ type Terminal struct { size Winsize config *config.Config titleHandlers []chan bool + resizeHandlers []chan bool modes Modes mouseMode MouseMode bracketedPasteMode bool @@ -194,6 +196,10 @@ func (terminal *Terminal) AttachTitleChangeHandler(handler chan bool) { terminal.titleHandlers = append(terminal.titleHandlers, handler) } +func (terminal *Terminal) AttachResizeHandler(handler chan bool) { + terminal.resizeHandlers = append(terminal.resizeHandlers, handler) +} + func (terminal *Terminal) Modes() Modes { return terminal.modes } @@ -206,6 +212,14 @@ func (terminal *Terminal) emitTitleChange() { } } +func (terminal *Terminal) emitResize() { + for _, h := range terminal.resizeHandlers { + go func(c chan bool) { + c <- true + }(h) + } +} + func (terminal *Terminal) GetLogicalCursorX() uint16 { if terminal.ActiveBuffer().CursorColumn() >= terminal.ActiveBuffer().Width() { return 0 @@ -237,6 +251,14 @@ func (terminal *Terminal) Write(data []byte) error { return err } +func (terminal *Terminal) WriteReturn() error { + if terminal.ActiveBuffer().IsNewLineMode() { + return terminal.Write([]byte{0x0d, 0x0a}) + } else { + return terminal.Write([]byte{0x0d}) + } +} + func (terminal *Terminal) Paste(data []byte) error { if terminal.bracketedPasteMode { @@ -281,14 +303,20 @@ func (terminal *Terminal) SetSize(newCols uint, newLines uint) error { terminal.lock.Lock() defer terminal.lock.Unlock() - terminal.size.Width = uint16(newCols) - terminal.size.Height = uint16(newLines) + if terminal.size.Width == uint16(newCols) && terminal.size.Height == uint16(newLines) { + return nil + } err := terminal.pty.Resize(int(newCols), int(newLines)) if err != nil { return fmt.Errorf("Failed to set terminal size vai ioctl: Error no %d", err) } + terminal.size.Width = uint16(newCols) + terminal.size.Height = uint16(newLines) + terminal.ActiveBuffer().ResizeView(terminal.size.Width, terminal.size.Height) + + terminal.emitResize() return nil } diff --git a/vttest/test-cursor-movement-1.png b/vttest/test-cursor-movement-1.png new file mode 100644 index 0000000000000000000000000000000000000000..0adc66be136dea753c39f9a90df51b03046a4eb8 GIT binary patch literal 9756 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 diff --git a/vttest/test-cursor-movement-3.png b/vttest/test-cursor-movement-3.png new file mode 100644 index 0000000000000000000000000000000000000000..bd2a54a5401e06f709704c50c05915d33fe493ed GIT binary patch literal 10553 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 aPcw2Rw^81jg8za5IR!s+qUhMg+y4*Pi_TC0 literal 0 HcmV?d00001 diff --git a/vttest/test-cursor-movement-5.png b/vttest/test-cursor-movement-5.png new file mode 100644 index 0000000000000000000000000000000000000000..f0340112ad03c9f27b10339e44b4a82c2e1a0457 GIT binary patch literal 7812 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