From 06526f4beb609f033619414be8feab643c0ed2bb Mon Sep 17 00:00:00 2001 From: Liam Galvin Date: Fri, 10 Aug 2018 13:46:35 +0100 Subject: [PATCH] scrollback buffer --- buffer/buffer.go | 53 +++++++++++++++++++++++++++++++++++++++++-- buffer/buffer_test.go | 2 +- buffer/line.go | 4 ++++ gui/gui.go | 19 ++++++++++++---- gui/renderer.go | 6 ++--- terminal/terminal.go | 26 +++++++++++++++++++++ 6 files changed, 100 insertions(+), 10 deletions(-) diff --git a/buffer/buffer.go b/buffer/buffer.go index 601f904..32820c9 100644 --- a/buffer/buffer.go +++ b/buffer/buffer.go @@ -14,6 +14,7 @@ type Buffer struct { displayChangeHandlers []chan bool savedX uint16 savedY uint16 + scrollLinesFromBottom uint } // NewBuffer creates a new terminal buffer @@ -28,6 +29,49 @@ func NewBuffer(viewCols uint16, viewLines uint16, attr CellAttributes) *Buffer { return b } +func (buffer *Buffer) GetScrollOffset() uint { + return buffer.scrollLinesFromBottom +} + +func (buffer *Buffer) ScrollDown(lines uint16) { + + if buffer.Height() < int(buffer.ViewHeight()) { + return + } + + defer buffer.emitDisplayChange() + if uint(lines) > buffer.scrollLinesFromBottom { + lines = uint16(buffer.scrollLinesFromBottom) + } + buffer.scrollLinesFromBottom -= uint(lines) +} + +func (buffer *Buffer) ScrollUp(lines uint16) { + + if buffer.Height() < int(buffer.ViewHeight()) { + return + } + + defer buffer.emitDisplayChange() + + if uint(lines)+buffer.scrollLinesFromBottom >= (uint(buffer.Height()) - uint(buffer.ViewHeight())) { + buffer.scrollLinesFromBottom = uint(buffer.Height()) - uint(buffer.ViewHeight()) + } else { + buffer.scrollLinesFromBottom += uint(lines) + } +} + +func (buffer *Buffer) ScrollPageDown() { + buffer.ScrollDown(buffer.viewHeight) +} +func (buffer *Buffer) ScrollPageUp() { + buffer.ScrollUp(buffer.viewHeight) +} +func (buffer *Buffer) ScrollToEnd() { + defer buffer.emitDisplayChange() + buffer.scrollLinesFromBottom = 0 +} + func (buffer *Buffer) SaveCursor() { buffer.savedX = buffer.cursorX buffer.savedY = buffer.cursorY @@ -117,6 +161,10 @@ func (buffer *Buffer) ViewHeight() uint16 { // Write will write a rune to the terminal at the position of the cursor, and increment the cursor position func (buffer *Buffer) Write(runes ...rune) { + + // scroll to bottom on input + buffer.scrollLinesFromBottom = 0 + for _, r := range runes { if r == 0x0a { buffer.NewLine() @@ -249,8 +297,9 @@ func (buffer *Buffer) SetPosition(col uint16, line uint16) { func (buffer *Buffer) GetVisibleLines() []Line { lines := []Line{} for i := buffer.Height() - int(buffer.ViewHeight()); i < buffer.Height(); i++ { - if i >= 0 && i < len(buffer.lines) { - lines = append(lines, buffer.lines[i]) + y := i - int(buffer.scrollLinesFromBottom) + if y >= 0 && y < len(buffer.lines) { + lines = append(lines, buffer.lines[y]) } } return lines diff --git a/buffer/buffer_test.go b/buffer/buffer_test.go index 35f38bf..8fb97e5 100644 --- a/buffer/buffer_test.go +++ b/buffer/buffer_test.go @@ -422,9 +422,9 @@ func TestBackspaceWithWrap(t *testing.T) { b.Backspace() b.Backspace() b.Backspace() + b.EraseLineFromCursor() lines := b.GetVisibleLines() assert.Equal(t, "hello\x00\x00\x00\x00\x00", lines[0].String()) - assert.Equal(t, "\x00\x00\x00\x00\x00", lines[1].String()) } func TestHorizontalResizeView(t *testing.T) { diff --git a/buffer/line.go b/buffer/line.go index dacefd4..763b1ad 100644 --- a/buffer/line.go +++ b/buffer/line.go @@ -12,6 +12,10 @@ func newLine() Line { } } +func (line *Line) Cells() []Cell { + return line.cells +} + // Cleanse removes null bytes from the end of the row func (line *Line) Cleanse() { cut := 0 diff --git a/gui/gui.go b/gui/gui.go index 41f7e47..6f1a53e 100644 --- a/gui/gui.go +++ b/gui/gui.go @@ -66,6 +66,15 @@ func (gui *GUI) resize(w *glfw.Window, width int, height int) { } +func (gui *GUI) glfwScrollCallback(w *glfw.Window, xoff float64, yoff float64) { + //fmt.Printf("Scroll x=%f y=%f\n", xoff, yoff) + if yoff > 0 { + gui.terminal.ScrollUp(1) + } else { + gui.terminal.ScrollDown(1) + } +} + func (gui *GUI) getTermSize() (int, int) { if gui.renderer == nil { return 0, 0 @@ -113,6 +122,7 @@ func (gui *GUI) Render() error { gui.window.SetFramebufferSizeCallback(gui.resize) gui.window.SetKeyCallback(gui.key) gui.window.SetCharCallback(gui.char) + gui.window.SetScrollCallback(gui.glfwScrollCallback) gui.window.SetRefreshCallback(func(w *glfw.Window) { select { case changeChan <- true: @@ -187,17 +197,18 @@ func (gui *GUI) Render() error { if gui.config.Rendering.AlwaysRepaint || frames > 0 { gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) - cols, rows := gui.getTermSize() - for row := 0; row < rows; row++ { - for col := 0; col < cols; col++ { - gui.renderer.DrawCell(gui.terminal.GetCell(col, row), col, row) + lines := gui.terminal.GetVisibleLines() + for y := 0; y < len(lines); y++ { + for x, cell := range lines[y].Cells() { + gui.renderer.DrawCell(cell, x, y) } } if gui.terminal.Modes().ShowCursor { cx := int(gui.terminal.GetLogicalCursorX()) cy := int(gui.terminal.GetLogicalCursorY()) + cy = int(cy) + int(gui.terminal.GetScrollOffset()) gui.renderer.DrawCursor(cx, cy, gui.config.ColourScheme.Cursor) } diff --git a/gui/renderer.go b/gui/renderer.go index fd8d764..65a1304 100644 --- a/gui/renderer.go +++ b/gui/renderer.go @@ -12,7 +12,7 @@ import ( type Renderer interface { SetArea(areaX int, areaY int, areaWidth int, areaHeight int) - DrawCell(cell *buffer.Cell, col int, row int) + DrawCell(cell buffer.Cell, col int, row int) DrawCursor(col int, row int, colour config.Colour) GetTermSize() (int, int) } @@ -218,9 +218,9 @@ func (r *OpenGLRenderer) DrawCursor(col int, row int, colour config.Colour) { //gl.PolygonMode(gl.FRONT_AND_BACK, gl.FILL) } -func (r *OpenGLRenderer) DrawCell(cell *buffer.Cell, col int, row int) { +func (r *OpenGLRenderer) DrawCell(cell buffer.Cell, col int, row int) { - if cell == nil || cell.Attr().Hidden || (cell.Rune() == 0x00) { + if cell.Attr().Hidden || (cell.Rune() == 0x00) { return } diff --git a/terminal/terminal.go b/terminal/terminal.go index ef959f1..6532b88 100644 --- a/terminal/terminal.go +++ b/terminal/terminal.go @@ -66,6 +66,32 @@ func New(pty *os.File, logger *zap.SugaredLogger, config config.Config) *Termina } } +func (terminal *Terminal) GetScrollOffset() uint { + return terminal.buffer.GetScrollOffset() +} + +func (terminal *Terminal) ScrollDown(lines uint16) { + terminal.buffer.ScrollDown(lines) +} + +func (terminal *Terminal) ScrollUp(lines uint16) { + terminal.buffer.ScrollUp(lines) +} + +func (terminal *Terminal) ScrollPageDown() { + terminal.buffer.ScrollPageDown() +} +func (terminal *Terminal) ScrollPageUp() { + terminal.buffer.ScrollPageUp() +} +func (terminal *Terminal) ScrollToEnd() { + terminal.buffer.ScrollToEnd() +} + +func (terminal *Terminal) GetVisibleLines() []buffer.Line { + return terminal.buffer.GetVisibleLines() +} + func (terminal *Terminal) GetCell(col int, row int) *buffer.Cell { return terminal.buffer.GetCell(col, row) }