mirror of https://github.com/liamg/aminal.git
scrollback buffer
This commit is contained in:
parent
c2b7f94f87
commit
06526f4beb
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
19
gui/gui.go
19
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) {
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue