diff --git a/buffer/buffer.go b/buffer/buffer.go index 8fa1424..bbf580f 100644 --- a/buffer/buffer.go +++ b/buffer/buffer.go @@ -19,6 +19,7 @@ type Buffer struct { bottomMargin uint // see DECSTBM docs - this is for scrollable regions replaceMode bool // overwrite character at cursor or insert new autoWrap bool + dirty bool } // NewBuffer creates a new terminal buffer @@ -35,6 +36,14 @@ func NewBuffer(viewCols uint16, viewLines uint16, attr CellAttributes) *Buffer { return b } +func (buffer *Buffer) IsDirty() bool { + if !buffer.dirty { + return false + } + buffer.dirty = false + return true +} + func (buffer *Buffer) SetAutoWrap(enabled bool) { buffer.autoWrap = enabled } @@ -132,23 +141,8 @@ func (buffer *Buffer) GetCell(viewCol uint16, viewRow uint16) *Cell { return &line.cells[viewCol] } -func (buffer *Buffer) AttachDisplayChangeHandler(handler chan bool) { - if buffer.displayChangeHandlers == nil { - buffer.displayChangeHandlers = []chan bool{} - } - - buffer.displayChangeHandlers = append(buffer.displayChangeHandlers, handler) -} - func (buffer *Buffer) emitDisplayChange() { - for _, channel := range buffer.displayChangeHandlers { - go func(c chan bool) { - select { - case c <- true: - default: - } - }(channel) - } + buffer.dirty = true } // Column returns cursor column @@ -219,7 +213,7 @@ func (buffer *Buffer) insertLine() { copy(out[0:], before) pos := buffer.RawLine() - for i := topIndex; i <= bottomIndex; i++ { + for i := topIndex; i < bottomIndex; i++ { if i < pos { out[i] = buffer.lines[i] } else { @@ -227,7 +221,7 @@ func (buffer *Buffer) insertLine() { } } - copy(out[bottomIndex:], after) + copy(out[bottomIndex+1:], after) out[pos] = newLine() buffer.lines = out @@ -260,17 +254,18 @@ func (buffer *Buffer) Index() { if uint(buffer.cursorY) < buffer.bottomMargin { buffer.cursorY++ + } else { + + topIndex := buffer.convertViewLineToRawLine(uint16(buffer.topMargin)) + bottomIndex := buffer.convertViewLineToRawLine(uint16(buffer.bottomMargin)) + + for i := topIndex; i < bottomIndex; i++ { + buffer.lines[i] = buffer.lines[i+1] + } + + buffer.lines[bottomIndex] = newLine() } - topIndex := buffer.convertViewLineToRawLine(uint16(buffer.topMargin)) - bottomIndex := buffer.convertViewLineToRawLine(uint16(buffer.bottomMargin)) - - for i := topIndex; i < bottomIndex; i++ { - buffer.lines[i] = buffer.lines[i+1] - } - - buffer.lines[buffer.RawLine()] = newLine() - return } @@ -289,17 +284,17 @@ func (buffer *Buffer) ReverseIndex() { if uint(buffer.cursorY) > buffer.topMargin { buffer.cursorY-- + } else { + + topIndex := buffer.convertViewLineToRawLine(uint16(buffer.topMargin)) + bottomIndex := buffer.convertViewLineToRawLine(uint16(buffer.bottomMargin)) + + for i := bottomIndex; i > topIndex; i-- { + buffer.lines[i] = buffer.lines[i-1] + } + + buffer.lines[topIndex] = newLine() } - - topIndex := buffer.convertViewLineToRawLine(uint16(buffer.topMargin)) - bottomIndex := buffer.convertViewLineToRawLine(uint16(buffer.bottomMargin)) - - for i := bottomIndex; i > topIndex; i-- { - buffer.lines[i] = buffer.lines[i-1] - } - - buffer.lines[buffer.RawLine()] = newLine() - return } @@ -520,10 +515,9 @@ func (buffer *Buffer) EraseLineFromCursor() { if len(line.cells) > 0 { cx := buffer.cursorX - if int(cx) >= len(line.cells) { - return // nothing to delete + if int(cx) < len(line.cells) { + line.cells = line.cells[:buffer.cursorX] } - line.cells = line.cells[:buffer.cursorX] } max := int(buffer.ViewWidth()) - len(line.cells) @@ -545,6 +539,15 @@ func (buffer *Buffer) EraseDisplay() { } } +func (buffer *Buffer) DeleteChars(n int) { + defer buffer.emitDisplayChange() + + line := buffer.getCurrentLine() + before := line.cells[:buffer.cursorX] + after := line.cells[int(buffer.cursorX)+n:] + line.cells = append(before, after...) +} + func (buffer *Buffer) EraseCharacters(n int) { defer buffer.emitDisplayChange() diff --git a/buffer/buffer_test.go b/buffer/buffer_test.go index 9b864e5..330b06e 100644 --- a/buffer/buffer_test.go +++ b/buffer/buffer_test.go @@ -3,7 +3,6 @@ package buffer import ( "strings" "testing" - "time" "github.com/stretchr/testify/require" @@ -298,29 +297,6 @@ func TestCursorAttr(t *testing.T) { assert.Equal(t, &b.cursorAttr, b.CursorAttr()) } -func TestAttachingHandlers(t *testing.T) { - b := NewBuffer(80, 2, CellAttributes{}) - displayHandler := make(chan bool, 1) - b.AttachDisplayChangeHandler(displayHandler) - require.Equal(t, 1, len(b.displayChangeHandlers)) - assert.Equal(t, b.displayChangeHandlers[0], displayHandler) -} - -func TestEmitDisplayHandlers(t *testing.T) { - b := NewBuffer(80, 2, CellAttributes{}) - displayHandler := make(chan bool, 1) - b.AttachDisplayChangeHandler(displayHandler) - b.emitDisplayChange() - time.Sleep(time.Millisecond * 50) - ok := false - select { - case <-displayHandler: - ok = true - default: - } - assert.True(t, ok) -} - func TestCursorPositionQuerying(t *testing.T) { b := NewBuffer(80, 20, CellAttributes{}) b.cursorX = 17 diff --git a/gui/input.go b/gui/input.go index b0ed1cd..febf3db 100644 --- a/gui/input.go +++ b/gui/input.go @@ -158,8 +158,6 @@ func (gui *GUI) key(w *glfw.Window, key glfw.Key, scancode int, action glfw.Acti if gui.terminal.IsApplicationCursorKeysModeEnabled() { gui.terminal.Write([]byte{ 0x1b, - 'O', - 0x1b, }) } else { gui.terminal.Write([]byte{ diff --git a/terminal/csi.go b/terminal/csi.go index b122c5f..d1e8b1f 100644 --- a/terminal/csi.go +++ b/terminal/csi.go @@ -28,7 +28,7 @@ var csiSequences = []csiMapping{ csiMapping{id: 'l', handler: csiResetModeHandler, expectedParams: &expectedParams{min: 1, max: 1}, description: "Reset Mode (RM)"}, csiMapping{id: 'm', handler: sgrSequenceHandler, description: "Character Attributes (SGR)"}, csiMapping{id: 'n', handler: csiDeviceStatusReportHandler, description: "Device Status Report (DSR)"}, - csiMapping{id: 'r', handler: csiSetMarginsHandler, expectedParams: &expectedParams{min: 2, max: 2}, description: "Set Scrolling Region [top;bottom] (default = full size of window) (DECSTBM), VT100"}, + csiMapping{id: 'r', handler: csiSetMarginsHandler, expectedParams: &expectedParams{min: 0, max: 2}, description: "Set Scrolling Region [top;bottom] (default = full size of window) (DECSTBM), VT100"}, csiMapping{id: 't', handler: csiWindowManipulation, description: "Window manipulation"}, csiMapping{id: 'A', handler: csiCursorUpHandler, description: "Cursor Up Ps Times (default = 1) (CUU)"}, csiMapping{id: 'B', handler: csiCursorDownHandler, description: "Cursor Down Ps Times (default = 1) (CUD)"}, @@ -77,8 +77,9 @@ CSI: if sequence.expectedParams != nil && (uint8(len(params)) < sequence.expectedParams.min || uint8(len(params)) > sequence.expectedParams.max) { continue } - terminal.logger.Debugf("CSI 0x%02X (ESC[%s%s%s) %s", final, param, intermediate, string(final), sequence.description) + 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()) return err } } @@ -377,8 +378,7 @@ func csiDeleteHandler(params []string, intermediate string, terminal *Terminal) } } - terminal.ActiveBuffer().EraseCharacters(n) - + terminal.ActiveBuffer().DeleteChars(n) return nil } diff --git a/terminal/output.go b/terminal/output.go index 99b6064..1ff4d10 100644 --- a/terminal/output.go +++ b/terminal/output.go @@ -98,7 +98,7 @@ func (terminal *Terminal) processInput(ctx context.Context, pty chan rune) { } else { //terminal.logger.Debugf("Received character 0x%X: %q", b, string(b)) if b >= 0x20 { - terminal.logger.Debugf("%c", b) + //terminal.logger.Debugf("%c", b) terminal.ActiveBuffer().Write(b) } else { terminal.logger.Error("Non-readable rune received: 0x%X", b) diff --git a/terminal/terminal.go b/terminal/terminal.go index 6a42990..b5c1fc6 100644 --- a/terminal/terminal.go +++ b/terminal/terminal.go @@ -100,7 +100,7 @@ func (terminal *Terminal) SetBracketedPasteMode(enabled bool) { func (terminal *Terminal) CheckDirty() bool { d := terminal.isDirty terminal.isDirty = false - return d + return d || terminal.ActiveBuffer().IsDirty() } func (terminal *Terminal) SetDirty() { @@ -166,12 +166,6 @@ func (terminal *Terminal) GetCell(col uint16, row uint16) *buffer.Cell { return terminal.ActiveBuffer().GetCell(col, row) } -func (terminal *Terminal) AttachDisplayChangeHandler(handler chan bool) { - for i := range terminal.buffers { - terminal.buffers[i].AttachDisplayChangeHandler(handler) - } -} - func (terminal *Terminal) AttachTitleChangeHandler(handler chan bool) { terminal.titleHandlers = append(terminal.titleHandlers, handler) }