scrollback buffer

This commit is contained in:
Liam Galvin 2018-08-10 13:46:35 +01:00
parent c2b7f94f87
commit 06526f4beb
6 changed files with 100 additions and 10 deletions

View File

@ -14,6 +14,7 @@ type Buffer struct {
displayChangeHandlers []chan bool displayChangeHandlers []chan bool
savedX uint16 savedX uint16
savedY uint16 savedY uint16
scrollLinesFromBottom uint
} }
// NewBuffer creates a new terminal buffer // NewBuffer creates a new terminal buffer
@ -28,6 +29,49 @@ func NewBuffer(viewCols uint16, viewLines uint16, attr CellAttributes) *Buffer {
return b 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() { func (buffer *Buffer) SaveCursor() {
buffer.savedX = buffer.cursorX buffer.savedX = buffer.cursorX
buffer.savedY = buffer.cursorY 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 // 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) { func (buffer *Buffer) Write(runes ...rune) {
// scroll to bottom on input
buffer.scrollLinesFromBottom = 0
for _, r := range runes { for _, r := range runes {
if r == 0x0a { if r == 0x0a {
buffer.NewLine() buffer.NewLine()
@ -249,8 +297,9 @@ func (buffer *Buffer) SetPosition(col uint16, line uint16) {
func (buffer *Buffer) GetVisibleLines() []Line { func (buffer *Buffer) GetVisibleLines() []Line {
lines := []Line{} lines := []Line{}
for i := buffer.Height() - int(buffer.ViewHeight()); i < buffer.Height(); i++ { for i := buffer.Height() - int(buffer.ViewHeight()); i < buffer.Height(); i++ {
if i >= 0 && i < len(buffer.lines) { y := i - int(buffer.scrollLinesFromBottom)
lines = append(lines, buffer.lines[i]) if y >= 0 && y < len(buffer.lines) {
lines = append(lines, buffer.lines[y])
} }
} }
return lines return lines

View File

@ -422,9 +422,9 @@ func TestBackspaceWithWrap(t *testing.T) {
b.Backspace() b.Backspace()
b.Backspace() b.Backspace()
b.Backspace() b.Backspace()
b.EraseLineFromCursor()
lines := b.GetVisibleLines() lines := b.GetVisibleLines()
assert.Equal(t, "hello\x00\x00\x00\x00\x00", lines[0].String()) 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) { func TestHorizontalResizeView(t *testing.T) {

View File

@ -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 // Cleanse removes null bytes from the end of the row
func (line *Line) Cleanse() { func (line *Line) Cleanse() {
cut := 0 cut := 0

View File

@ -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) { func (gui *GUI) getTermSize() (int, int) {
if gui.renderer == nil { if gui.renderer == nil {
return 0, 0 return 0, 0
@ -113,6 +122,7 @@ func (gui *GUI) Render() error {
gui.window.SetFramebufferSizeCallback(gui.resize) gui.window.SetFramebufferSizeCallback(gui.resize)
gui.window.SetKeyCallback(gui.key) gui.window.SetKeyCallback(gui.key)
gui.window.SetCharCallback(gui.char) gui.window.SetCharCallback(gui.char)
gui.window.SetScrollCallback(gui.glfwScrollCallback)
gui.window.SetRefreshCallback(func(w *glfw.Window) { gui.window.SetRefreshCallback(func(w *glfw.Window) {
select { select {
case changeChan <- true: case changeChan <- true:
@ -187,17 +197,18 @@ func (gui *GUI) Render() error {
if gui.config.Rendering.AlwaysRepaint || frames > 0 { if gui.config.Rendering.AlwaysRepaint || frames > 0 {
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
cols, rows := gui.getTermSize()
for row := 0; row < rows; row++ { lines := gui.terminal.GetVisibleLines()
for col := 0; col < cols; col++ { for y := 0; y < len(lines); y++ {
gui.renderer.DrawCell(gui.terminal.GetCell(col, row), col, row) for x, cell := range lines[y].Cells() {
gui.renderer.DrawCell(cell, x, y)
} }
} }
if gui.terminal.Modes().ShowCursor { if gui.terminal.Modes().ShowCursor {
cx := int(gui.terminal.GetLogicalCursorX()) cx := int(gui.terminal.GetLogicalCursorX())
cy := int(gui.terminal.GetLogicalCursorY()) cy := int(gui.terminal.GetLogicalCursorY())
cy = int(cy) + int(gui.terminal.GetScrollOffset())
gui.renderer.DrawCursor(cx, cy, gui.config.ColourScheme.Cursor) gui.renderer.DrawCursor(cx, cy, gui.config.ColourScheme.Cursor)
} }

View File

@ -12,7 +12,7 @@ import (
type Renderer interface { type Renderer interface {
SetArea(areaX int, areaY int, areaWidth int, areaHeight int) 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) DrawCursor(col int, row int, colour config.Colour)
GetTermSize() (int, int) 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) //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 return
} }

View File

@ -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 { func (terminal *Terminal) GetCell(col int, row int) *buffer.Cell {
return terminal.buffer.GetCell(col, row) return terminal.buffer.GetCell(col, row)
} }