alt buffer

This commit is contained in:
Liam Galvin 2018-08-12 15:16:26 +01:00
parent 3ac0d62d72
commit 6d10ffd17c
11 changed files with 224 additions and 305 deletions

View File

@ -86,15 +86,15 @@ func (buffer *Buffer) CursorAttr() *CellAttributes {
return &buffer.cursorAttr
}
func (buffer *Buffer) GetCell(viewCol int, viewRow int) *Cell {
func (buffer *Buffer) GetCell(viewCol uint16, viewRow uint16) *Cell {
rawLine := buffer.convertViewLineToRawLine(uint16(viewRow))
rawLine := buffer.convertViewLineToRawLine(viewRow)
if viewCol < 0 || rawLine < 0 || int(rawLine) >= len(buffer.lines) {
return nil
}
line := &buffer.lines[rawLine]
if viewCol >= len(line.cells) {
if int(viewCol) >= len(line.cells) {
return nil
}
return &line.cells[viewCol]
@ -245,6 +245,23 @@ func (buffer *Buffer) Backspace() {
}
}
func (buffer *Buffer) BackspaceDelete() {
if buffer.cursorX == 0 {
line := buffer.getCurrentLine()
if line.wrapped {
buffer.MovePosition(int16(buffer.Width()-1), -1)
buffer.GetCell(buffer.cursorX, buffer.cursorY).erase()
} else {
//@todo ring bell or whatever
fmt.Println("BELL?")
}
} else {
buffer.MovePosition(-1, 0)
buffer.GetCell(buffer.cursorX, buffer.cursorY).erase()
}
}
func (buffer *Buffer) CarriageReturn() {
defer buffer.emitDisplayChange()
buffer.cursorX = 0
@ -425,6 +442,7 @@ func (buffer *Buffer) EraseDisplayToCursor() {
}
func (buffer *Buffer) ResizeView(width uint16, height uint16) {
defer buffer.emitDisplayChange()
if buffer.viewHeight == 0 {

View File

@ -1,58 +0,0 @@
mode: set
gitlab.com/liamg/raft/buffer/buffer.go:17.59,25.2 3 1
gitlab.com/liamg/raft/buffer/buffer.go:28.45,30.2 1 1
gitlab.com/liamg/raft/buffer/buffer.go:33.43,35.2 1 1
gitlab.com/liamg/raft/buffer/buffer.go:38.40,40.40 2 1
gitlab.com/liamg/raft/buffer/buffer.go:43.2,43.75 1 1
gitlab.com/liamg/raft/buffer/buffer.go:40.40,42.3 1 1
gitlab.com/liamg/raft/buffer/buffer.go:47.38,49.2 1 1
gitlab.com/liamg/raft/buffer/buffer.go:51.42,53.2 1 1
gitlab.com/liamg/raft/buffer/buffer.go:55.36,57.2 1 1
gitlab.com/liamg/raft/buffer/buffer.go:59.43,61.2 1 1
gitlab.com/liamg/raft/buffer/buffer.go:63.53,64.49 1 1
gitlab.com/liamg/raft/buffer/buffer.go:64.49,66.3 1 1
gitlab.com/liamg/raft/buffer/buffer.go:70.44,71.26 1 1
gitlab.com/liamg/raft/buffer/buffer.go:71.26,73.16 2 1
gitlab.com/liamg/raft/buffer/buffer.go:77.3,78.53 2 1
gitlab.com/liamg/raft/buffer/buffer.go:81.3,84.35 4 1
gitlab.com/liamg/raft/buffer/buffer.go:73.16,75.12 2 1
gitlab.com/liamg/raft/buffer/buffer.go:78.53,80.4 1 1
gitlab.com/liamg/raft/buffer/buffer.go:88.49,90.46 1 1
gitlab.com/liamg/raft/buffer/buffer.go:90.46,92.3 1 1
gitlab.com/liamg/raft/buffer/buffer.go:92.8,93.44 1 1
gitlab.com/liamg/raft/buffer/buffer.go:93.44,98.4 4 1
gitlab.com/liamg/raft/buffer/buffer.go:98.9,100.50 2 1
gitlab.com/liamg/raft/buffer/buffer.go:100.50,105.5 4 1
gitlab.com/liamg/raft/buffer/buffer.go:105.10,106.58 1 0
gitlab.com/liamg/raft/buffer/buffer.go:107.5,108.26 2 0
gitlab.com/liamg/raft/buffer/buffer.go:114.33,116.25 1 1
gitlab.com/liamg/raft/buffer/buffer.go:124.2,124.43 1 1
gitlab.com/liamg/raft/buffer/buffer.go:116.25,118.19 2 1
gitlab.com/liamg/raft/buffer/buffer.go:118.19,121.4 2 1
gitlab.com/liamg/raft/buffer/buffer.go:124.43,127.3 2 1
gitlab.com/liamg/raft/buffer/buffer.go:127.8,130.3 2 1
gitlab.com/liamg/raft/buffer/buffer.go:133.54,135.33 1 1
gitlab.com/liamg/raft/buffer/buffer.go:139.2,139.33 1 1
gitlab.com/liamg/raft/buffer/buffer.go:143.2,143.86 1 1
gitlab.com/liamg/raft/buffer/buffer.go:135.33,137.3 1 1
gitlab.com/liamg/raft/buffer/buffer.go:139.33,141.3 1 1
gitlab.com/liamg/raft/buffer/buffer.go:146.60,147.31 1 1
gitlab.com/liamg/raft/buffer/buffer.go:151.2,151.33 1 1
gitlab.com/liamg/raft/buffer/buffer.go:155.2,156.23 2 1
gitlab.com/liamg/raft/buffer/buffer.go:147.31,150.3 2 1
gitlab.com/liamg/raft/buffer/buffer.go:151.33,154.3 2 1
gitlab.com/liamg/raft/buffer/buffer.go:159.48,161.80 2 1
gitlab.com/liamg/raft/buffer/buffer.go:166.2,166.14 1 1
gitlab.com/liamg/raft/buffer/buffer.go:161.80,162.38 1 1
gitlab.com/liamg/raft/buffer/buffer.go:162.38,164.4 1 1
gitlab.com/liamg/raft/buffer/buffer.go:171.31,172.48 1 1
gitlab.com/liamg/raft/buffer/buffer.go:175.2,175.26 1 1
gitlab.com/liamg/raft/buffer/buffer.go:172.48,174.3 1 1
gitlab.com/liamg/raft/buffer/buffer.go:178.63,183.2 2 1
gitlab.com/liamg/raft/buffer/cell.go:20.21,22.2 1 1
gitlab.com/liamg/raft/buffer/cell.go:24.35,27.2 2 1
gitlab.com/liamg/raft/buffer/line.go:8.21,13.2 1 1
gitlab.com/liamg/raft/buffer/line.go:15.44,17.2 1 1
gitlab.com/liamg/raft/buffer/line.go:19.35,21.34 2 1
gitlab.com/liamg/raft/buffer/line.go:24.2,24.22 1 1
gitlab.com/liamg/raft/buffer/line.go:21.34,23.3 1 1

View File

@ -75,7 +75,7 @@ func (gui *GUI) glfwScrollCallback(w *glfw.Window, xoff float64, yoff float64) {
}
}
func (gui *GUI) getTermSize() (int, int) {
func (gui *GUI) getTermSize() (uint, uint) {
if gui.renderer == nil {
return 0, 0
}
@ -201,14 +201,14 @@ func (gui *GUI) Render() error {
lines := gui.terminal.GetVisibleLines()
for y := 0; y < len(lines); y++ {
for x, cell := range lines[y].Cells() {
gui.renderer.DrawCell(cell, x, y)
gui.renderer.DrawCell(cell, uint(x), uint(y))
}
}
if gui.terminal.Modes().ShowCursor {
cx := int(gui.terminal.GetLogicalCursorX())
cy := int(gui.terminal.GetLogicalCursorY())
cy = int(cy) + int(gui.terminal.GetScrollOffset())
cx := uint(gui.terminal.GetLogicalCursorX())
cy := uint(gui.terminal.GetLogicalCursorY())
cy = cy + uint(gui.terminal.GetScrollOffset())
gui.renderer.DrawCursor(cx, cy, gui.config.ColourScheme.Cursor)
}

View File

@ -12,9 +12,9 @@ import (
type Renderer interface {
SetArea(areaX int, areaY int, areaWidth int, areaHeight int)
DrawCell(cell buffer.Cell, col int, row int)
DrawCursor(col int, row int, colour config.Colour)
GetTermSize() (int, int)
DrawCell(cell buffer.Cell, col uint, row uint)
DrawCursor(col uint, row uint, colour config.Colour)
GetTermSize() (uint, uint)
}
type OpenGLRenderer struct {
@ -27,10 +27,10 @@ type OpenGLRenderer struct {
cellWidth float32
cellHeight float32
verticalCellPadding float32
termCols int
termRows int
cellPositions map[[2]int][2]float32
rectangles map[[2]int]*rectangle
termCols uint
termRows uint
cellPositions map[[2]uint][2]float32
rectangles map[[2]uint]*rectangle
config config.Config
colourAttr uint32
program uint32
@ -124,8 +124,8 @@ func NewOpenGLRenderer(config config.Config, font *glfont.Font, fontScale int32,
areaX: areaX,
areaY: areaY,
fontScale: fontScale,
cellPositions: map[[2]int][2]float32{},
rectangles: map[[2]int]*rectangle{},
cellPositions: map[[2]uint][2]float32{},
rectangles: map[[2]uint]*rectangle{},
config: config,
colourAttr: colourAttr,
program: program,
@ -134,7 +134,7 @@ func NewOpenGLRenderer(config config.Config, font *glfont.Font, fontScale int32,
return r
}
func (r *OpenGLRenderer) GetTermSize() (int, int) {
func (r *OpenGLRenderer) GetTermSize() (uint, uint) {
return r.termCols, r.termRows
}
@ -156,31 +156,31 @@ func (r *OpenGLRenderer) SetFont(font *glfont.Font) { // @todo check for monospa
r.verticalCellPadding = (0.25 * float32(r.fontScale))
r.cellWidth = font.Width(1, "X")
r.cellHeight = font.Height(1, "X") + (r.verticalCellPadding * 2) // vertical padding
r.termCols = int(math.Floor(float64(float32(r.areaWidth) / r.cellWidth)))
r.termRows = int(math.Floor(float64(float32(r.areaHeight) / r.cellHeight)))
r.termCols = uint(math.Floor(float64(float32(r.areaWidth) / r.cellWidth)))
r.termRows = uint(math.Floor(float64(float32(r.areaHeight) / r.cellHeight)))
r.calculatePositions()
r.generateRectangles()
}
func (r *OpenGLRenderer) calculatePositions() {
for line := 0; line < r.termRows; line++ {
for col := 0; col < r.termCols; col++ {
for line := uint(0); line < r.termRows; line++ {
for col := uint(0); col < r.termCols; col++ {
// rounding to whole pixels makes everything nice
x := float32(math.Round(float64((float32(col) * r.cellWidth))))
y := float32(math.Round(float64(
(float32(line) * r.cellHeight) + (r.cellHeight / 2) + r.verticalCellPadding,
)))
r.cellPositions[[2]int{col, line}] = [2]float32{x, y}
r.cellPositions[[2]uint{col, line}] = [2]float32{x, y}
}
}
}
func (r *OpenGLRenderer) generateRectangles() {
gl.UseProgram(r.program)
for line := 0; line < r.termRows; line++ {
for col := 0; col < r.termCols; col++ {
for line := uint(0); line < r.termRows; line++ {
for col := uint(0); col < r.termCols; col++ {
rect, ok := r.rectangles[[2]int{col, line}]
rect, ok := r.rectangles[[2]uint{col, line}]
if ok {
rect.Free()
}
@ -188,14 +188,14 @@ func (r *OpenGLRenderer) generateRectangles() {
// rounding to whole pixels makes everything nice
x := float32(float64((float32(col) * r.cellWidth)))
y := float32(float64((float32(line) * r.cellHeight) + (r.cellHeight)))
r.rectangles[[2]int{col, line}] = r.newRectangle(x, y, r.colourAttr)
r.rectangles[[2]uint{col, line}] = r.newRectangle(x, y, r.colourAttr)
}
}
}
func (r *OpenGLRenderer) DrawCursor(col int, row int, colour config.Colour) {
func (r *OpenGLRenderer) DrawCursor(col uint, row uint, colour config.Colour) {
rect, ok := r.rectangles[[2]int{col, row}]
rect, ok := r.rectangles[[2]uint{col, row}]
if !ok { // probably trying to draw during resize - perhaps add a mutex?
return
}
@ -218,7 +218,7 @@ 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 uint, row uint) {
if cell.Attr().Hidden || (cell.Rune() == 0x00) {
return
@ -235,7 +235,7 @@ func (r *OpenGLRenderer) DrawCell(cell buffer.Cell, col int, row int) {
bg = cell.Bg()
}
pos, ok := r.cellPositions[[2]int{col, row}]
pos, ok := r.cellPositions[[2]uint{col, row}]
if !ok {
panic(fmt.Sprintf("Missing position data for cell at %d,%d", col, row))
}
@ -245,7 +245,7 @@ func (r *OpenGLRenderer) DrawCell(cell buffer.Cell, col int, row int) {
// don't bother rendering rectangles that are the same colour as the background
if bg != r.config.ColourScheme.Background {
rect, ok := r.rectangles[[2]int{col, row}]
rect, ok := r.rectangles[[2]uint{col, row}]
if !ok {
panic(fmt.Sprintf("Missing rectangle data for cell at %d,%d", col, row))
}

View File

@ -84,6 +84,8 @@ func main() {
// parse this
conf := getConfig()
os.Setenv("TERM", "xterm-256color")
logger, err := getLogger(conf)
if err != nil {
fmt.Printf("Failed to create logger: %s\n", err)

View File

@ -15,36 +15,36 @@ var ansiSequenceMap = map[rune]escapeSequenceHandler{
func indexHandler(buffer chan rune, terminal *Terminal) error {
// @todo is thus right?
// "This sequence causes the active position to move downward one line without changing the column position. If the active position is at the bottom margin, a scroll up is performed."
if terminal.buffer.CursorLine() == terminal.buffer.ViewHeight()-1 {
terminal.buffer.NewLine()
if terminal.ActiveBuffer().CursorLine() == terminal.ActiveBuffer().ViewHeight()-1 {
terminal.ActiveBuffer().NewLine()
return nil
}
terminal.buffer.MovePosition(0, 1)
terminal.ActiveBuffer().MovePosition(0, 1)
return nil
}
func reverseIndexHandler(buffer chan rune, terminal *Terminal) error {
terminal.buffer.MovePosition(0, -1)
terminal.ActiveBuffer().MovePosition(0, -1)
return nil
}
func saveCursorHandler(buffer chan rune, terminal *Terminal) error {
terminal.buffer.SaveCursor()
terminal.ActiveBuffer().SaveCursor()
return nil
}
func restoreCursorHandler(buffer chan rune, terminal *Terminal) error {
terminal.buffer.RestoreCursor()
terminal.ActiveBuffer().RestoreCursor()
return nil
}
func ansiHandler(buffer chan rune, terminal *Terminal) error {
func ansiHandler(pty chan rune, terminal *Terminal) error {
// if the byte is an escape character, read the next byte to determine which one
b := <-buffer
b := <-pty
handler, ok := ansiSequenceMap[b]
if ok {
return handler(buffer, terminal)
return handler(pty, terminal)
}
switch b {
@ -52,7 +52,7 @@ func ansiHandler(buffer chan rune, terminal *Terminal) error {
case 'c':
terminal.logger.Errorf("RIS not yet supported")
case '(':
b = <-buffer
b = <-pty
switch b {
case 'A': //uk @todo handle these?
//terminal.charSet = C0
@ -60,7 +60,7 @@ func ansiHandler(buffer chan rune, terminal *Terminal) error {
//terminal.charSet = C0
}
case ')':
b = <-buffer
b = <-pty
switch b {
case 'A': //uk @todo handle these?
//terminal.charSet = C1
@ -68,7 +68,7 @@ func ansiHandler(buffer chan rune, terminal *Terminal) error {
//terminal.charSet = C1
}
case '*':
b = <-buffer
b = <-pty
switch b {
case 'A': //uk @todo handle these?
//terminal.charSet = C2
@ -76,7 +76,7 @@ func ansiHandler(buffer chan rune, terminal *Terminal) error {
//terminal.charSet = C2
}
case '+':
b = <-buffer
b = <-pty
switch b {
case 'A': //uk @todo handle these?
//terminal.charSet = C3
@ -90,7 +90,7 @@ func ansiHandler(buffer chan rune, terminal *Terminal) error {
case '?':
pm := ""
for {
b = <-buffer
b = <-pty
switch b {
case 'h':
switch pm {

View File

@ -22,10 +22,28 @@ func csiSetMode(modeStr string, enabled bool, terminal *Terminal) error {
switch modeStr {
case "?1":
terminal.modes.ApplicationCursorKeys = enabled
case "?12":
case "?12", "?13":
terminal.modes.BlinkingCursor = enabled
case "?25":
terminal.modes.ShowCursor = enabled
case "?47", "?1047":
if enabled {
terminal.UseAltBuffer()
} else {
terminal.UseMainBuffer()
}
case "?1048":
if enabled {
terminal.ActiveBuffer().SaveCursor()
} else {
terminal.ActiveBuffer().RestoreCursor()
}
case "?1049":
if enabled {
terminal.UseAltBuffer()
} else {
terminal.UseMainBuffer()
}
default:
return fmt.Errorf("Unsupported CSI %sl code", modeStr)
}
@ -43,7 +61,7 @@ func csiEraseCharactersHandler(params []string, intermediate string, terminal *T
}
}
terminal.buffer.EraseCharacters(count)
terminal.ActiveBuffer().EraseCharacters(count)
return nil
}
@ -71,7 +89,7 @@ func csiLinePositionAbsolute(params []string, intermediate string, terminal *Ter
}
}
terminal.buffer.SetPosition(uint16(col), terminal.buffer.CursorLine())
terminal.ActiveBuffer().SetPosition(uint16(col), terminal.ActiveBuffer().CursorLine())
return nil
}
@ -79,7 +97,7 @@ func csiLinePositionAbsolute(params []string, intermediate string, terminal *Ter
type csiSequenceHandler func(params []string, intermediate string, terminal *Terminal) error
// CSI: Control Sequence Introducer [
func csiHandler(buffer chan rune, terminal *Terminal) error {
func csiHandler(pty chan rune, terminal *Terminal) error {
var final rune
var b rune
var err error
@ -87,7 +105,7 @@ func csiHandler(buffer chan rune, terminal *Terminal) error {
intermediate := ""
CSI:
for {
b = <-buffer
b = <-pty
switch true {
case b >= 0x30 && b <= 0x3F:
param = param + string(b)
@ -117,7 +135,7 @@ CSI:
distance = 1
}
}
terminal.buffer.MovePosition(0, -int16(distance))
terminal.ActiveBuffer().MovePosition(0, -int16(distance))
case 'B':
distance := 1
if len(params) > 0 {
@ -128,7 +146,7 @@ CSI:
}
}
terminal.buffer.MovePosition(0, int16(distance))
terminal.ActiveBuffer().MovePosition(0, int16(distance))
case 'C':
distance := 1
@ -140,7 +158,7 @@ CSI:
}
}
terminal.buffer.MovePosition(int16(distance), 0)
terminal.ActiveBuffer().MovePosition(int16(distance), 0)
case 'D':
@ -153,7 +171,7 @@ CSI:
}
}
terminal.buffer.MovePosition(-int16(distance), 0)
terminal.ActiveBuffer().MovePosition(-int16(distance), 0)
case 'E':
distance := 1
@ -165,8 +183,8 @@ CSI:
}
}
terminal.buffer.MovePosition(0, int16(distance))
terminal.buffer.SetPosition(0, terminal.buffer.CursorLine())
terminal.ActiveBuffer().MovePosition(0, int16(distance))
terminal.ActiveBuffer().SetPosition(0, terminal.ActiveBuffer().CursorLine())
case 'F':
@ -178,8 +196,8 @@ CSI:
distance = 1
}
}
terminal.buffer.MovePosition(0, -int16(distance))
terminal.buffer.SetPosition(0, terminal.buffer.CursorLine())
terminal.ActiveBuffer().MovePosition(0, -int16(distance))
terminal.ActiveBuffer().SetPosition(0, terminal.ActiveBuffer().CursorLine())
case 'G':
@ -192,7 +210,7 @@ CSI:
}
}
terminal.buffer.SetPosition(uint16(distance-1), terminal.buffer.CursorLine())
terminal.ActiveBuffer().SetPosition(uint16(distance-1), terminal.ActiveBuffer().CursorLine())
case 'H', 'f':
@ -213,7 +231,7 @@ CSI:
}
}
terminal.buffer.SetPosition(uint16(x-1), uint16(y-1))
terminal.ActiveBuffer().SetPosition(uint16(x-1), uint16(y-1))
default:
err = fmt.Errorf("Unknown CSI control sequence: 0x%02X (ESC[%s%s%s)", final, param, intermediate, string(final))
@ -232,7 +250,9 @@ func csiDeleteHandler(params []string, intermediate string, terminal *Terminal)
n = 1
}
}
_ = n
terminal.ActiveBuffer().EraseCharacters(n)
return nil
}
@ -246,11 +266,11 @@ func csiEraseInDisplayHandler(params []string, intermediate string, terminal *Te
switch n {
case "0", "":
terminal.buffer.EraseDisplayFromCursor()
terminal.ActiveBuffer().EraseDisplayFromCursor()
case "1":
terminal.buffer.EraseDisplayToCursor()
terminal.ActiveBuffer().EraseDisplayToCursor()
case "2":
terminal.buffer.EraseDisplay()
terminal.ActiveBuffer().EraseDisplay()
default:
return fmt.Errorf("Unsupported ED: CSI %s J", n)
}
@ -268,11 +288,11 @@ func csiEraseInLineHandler(params []string, intermediate string, terminal *Termi
switch n {
case "0", "": //erase adter cursor
terminal.buffer.EraseLineFromCursor()
terminal.ActiveBuffer().EraseLineFromCursor()
case "1": // erase to cursor inclusive
terminal.buffer.EraseLineToCursor()
terminal.ActiveBuffer().EraseLineToCursor()
case "2": // erase entire
terminal.buffer.EraseLine()
terminal.ActiveBuffer().EraseLine()
default:
return fmt.Errorf("Unsupported EL: CSI %s K", n)
}

View File

@ -1,91 +0,0 @@
package terminal
import "testing"
func TestDelete(t *testing.T) {
terminal := &Terminal{
lines: []Line{
{
Cells: []Cell{
{
r: 'a',
},
{
r: 'b',
},
{
r: 'c',
},
{
r: 'd',
},
{
r: 'e',
},
},
},
{
Cells: []Cell{
{
r: 'f',
},
{
r: 'g',
},
{
r: 'h',
},
{
r: 'i',
},
{
r: 'j',
},
},
},
{
Cells: []Cell{
{
r: 'k',
},
{
r: 'l',
},
{
r: 'm',
},
{
r: 'n',
},
{
r: 'o',
},
},
},
},
}
terminal.position = Position{
Col: 3,
Line: 1,
}
if err := terminal.delete(2); err != nil {
t.Errorf("Delete failed: %s", err)
}
if len(terminal.lines) != 3 {
t.Errorf("No. of lines has changed by deleting characters")
}
if "fgh" != terminal.lines[1].String() {
t.Errorf("Unexpected string after deletion: %s", terminal.lines[1].String())
}
if "abcde" != terminal.lines[0].String() {
t.Errorf("Unexpected string after deletion: %s", terminal.lines[0].String())
}
if "klmno" != terminal.lines[2].String() {
t.Errorf("Unexpected string after deletion: %s", terminal.lines[2].String())
}
}

View File

@ -8,7 +8,7 @@ import (
type TerminalCharSet int
type escapeSequenceHandler func(buffer chan rune, terminal *Terminal) error
type escapeSequenceHandler func(pty chan rune, terminal *Terminal) error
var escapeSequenceMap = map[rune]escapeSequenceHandler{
0x1b: ansiHandler,
@ -28,7 +28,7 @@ func (terminal *Terminal) Resume() {
}
}
func (terminal *Terminal) processInput(ctx context.Context, buffer chan rune) {
func (terminal *Terminal) processInput(ctx context.Context, pty chan rune) {
// https://en.wikipedia.org/wiki/ANSI_escape_code
@ -47,12 +47,12 @@ func (terminal *Terminal) processInput(ctx context.Context, buffer chan rune) {
//if terminal.config.slomo
//time.Sleep(time.Millisecond * 100)
b := <-buffer
b := <-pty
handler, ok := escapeSequenceMap[b]
if ok {
if err := handler(buffer, terminal); err != nil {
if err := handler(pty, terminal); err != nil {
terminal.logger.Errorf("Error handling escape sequence 0x%X: %s", b, err)
}
continue
@ -62,19 +62,19 @@ func (terminal *Terminal) processInput(ctx context.Context, buffer chan rune) {
switch b {
case 0x0a:
terminal.buffer.NewLine()
terminal.ActiveBuffer().NewLine()
case 0x0d:
terminal.buffer.CarriageReturn()
terminal.ActiveBuffer().CarriageReturn()
case 0x08:
// backspace
terminal.buffer.Backspace()
terminal.ActiveBuffer().Backspace()
case 0x07:
// @todo ring bell - flash red or some shit?
default:
// render character at current location
// fmt.Printf("%s\n", string([]byte{b}))
if b >= 0x20 {
terminal.buffer.Write(b)
terminal.ActiveBuffer().Write(b)
} else {
terminal.logger.Error("Non-readable rune received: 0x%X", b)
}

View File

@ -12,103 +12,103 @@ func sgrSequenceHandler(params []string, intermediate string, terminal *Terminal
param := params[i]
switch param {
case "00", "0", "":
attr := terminal.buffer.CursorAttr()
attr := terminal.ActiveBuffer().CursorAttr()
*attr = buffer.CellAttributes{
FgColour: terminal.config.ColourScheme.Foreground,
BgColour: terminal.config.ColourScheme.Background,
}
case "1", "01":
terminal.buffer.CursorAttr().Bold = true
terminal.ActiveBuffer().CursorAttr().Bold = true
case "2", "02":
terminal.buffer.CursorAttr().Dim = true
terminal.ActiveBuffer().CursorAttr().Dim = true
case "4", "04":
terminal.buffer.CursorAttr().Underline = true
terminal.ActiveBuffer().CursorAttr().Underline = true
case "5", "05":
terminal.buffer.CursorAttr().Blink = true
terminal.ActiveBuffer().CursorAttr().Blink = true
case "7", "07":
terminal.buffer.CursorAttr().Reverse = true
terminal.ActiveBuffer().CursorAttr().Reverse = true
case "8", "08":
terminal.buffer.CursorAttr().Hidden = true
terminal.ActiveBuffer().CursorAttr().Hidden = true
case "21":
terminal.buffer.CursorAttr().Bold = false
terminal.ActiveBuffer().CursorAttr().Bold = false
case "22":
terminal.buffer.CursorAttr().Dim = false
terminal.ActiveBuffer().CursorAttr().Dim = false
case "24":
terminal.buffer.CursorAttr().Underline = false
terminal.ActiveBuffer().CursorAttr().Underline = false
case "25":
terminal.buffer.CursorAttr().Blink = false
terminal.ActiveBuffer().CursorAttr().Blink = false
case "27":
terminal.buffer.CursorAttr().Reverse = false
terminal.ActiveBuffer().CursorAttr().Reverse = false
case "28":
terminal.buffer.CursorAttr().Hidden = false
terminal.ActiveBuffer().CursorAttr().Hidden = false
case "39":
terminal.buffer.CursorAttr().FgColour = terminal.config.ColourScheme.Foreground
terminal.ActiveBuffer().CursorAttr().FgColour = terminal.config.ColourScheme.Foreground
case "30":
terminal.buffer.CursorAttr().FgColour = terminal.config.ColourScheme.Black
terminal.ActiveBuffer().CursorAttr().FgColour = terminal.config.ColourScheme.Black
case "31":
terminal.buffer.CursorAttr().FgColour = terminal.config.ColourScheme.Red
terminal.ActiveBuffer().CursorAttr().FgColour = terminal.config.ColourScheme.Red
case "32":
terminal.buffer.CursorAttr().FgColour = terminal.config.ColourScheme.Green
terminal.ActiveBuffer().CursorAttr().FgColour = terminal.config.ColourScheme.Green
case "33":
terminal.buffer.CursorAttr().FgColour = terminal.config.ColourScheme.Yellow
terminal.ActiveBuffer().CursorAttr().FgColour = terminal.config.ColourScheme.Yellow
case "34":
terminal.buffer.CursorAttr().FgColour = terminal.config.ColourScheme.Blue
terminal.ActiveBuffer().CursorAttr().FgColour = terminal.config.ColourScheme.Blue
case "35":
terminal.buffer.CursorAttr().FgColour = terminal.config.ColourScheme.Magenta
terminal.ActiveBuffer().CursorAttr().FgColour = terminal.config.ColourScheme.Magenta
case "36":
terminal.buffer.CursorAttr().FgColour = terminal.config.ColourScheme.Cyan
terminal.ActiveBuffer().CursorAttr().FgColour = terminal.config.ColourScheme.Cyan
case "37":
terminal.buffer.CursorAttr().FgColour = terminal.config.ColourScheme.White
terminal.ActiveBuffer().CursorAttr().FgColour = terminal.config.ColourScheme.White
case "90":
terminal.buffer.CursorAttr().FgColour = terminal.config.ColourScheme.DarkGrey
terminal.ActiveBuffer().CursorAttr().FgColour = terminal.config.ColourScheme.DarkGrey
case "91":
terminal.buffer.CursorAttr().FgColour = terminal.config.ColourScheme.LightRed
terminal.ActiveBuffer().CursorAttr().FgColour = terminal.config.ColourScheme.LightRed
case "92":
terminal.buffer.CursorAttr().FgColour = terminal.config.ColourScheme.LightGreen
terminal.ActiveBuffer().CursorAttr().FgColour = terminal.config.ColourScheme.LightGreen
case "93":
terminal.buffer.CursorAttr().FgColour = terminal.config.ColourScheme.LightYellow
terminal.ActiveBuffer().CursorAttr().FgColour = terminal.config.ColourScheme.LightYellow
case "94":
terminal.buffer.CursorAttr().FgColour = terminal.config.ColourScheme.LightBlue
terminal.ActiveBuffer().CursorAttr().FgColour = terminal.config.ColourScheme.LightBlue
case "95":
terminal.buffer.CursorAttr().FgColour = terminal.config.ColourScheme.LightMagenta
terminal.ActiveBuffer().CursorAttr().FgColour = terminal.config.ColourScheme.LightMagenta
case "96":
terminal.buffer.CursorAttr().FgColour = terminal.config.ColourScheme.LightCyan
terminal.ActiveBuffer().CursorAttr().FgColour = terminal.config.ColourScheme.LightCyan
case "97":
terminal.buffer.CursorAttr().FgColour = terminal.config.ColourScheme.White
terminal.ActiveBuffer().CursorAttr().FgColour = terminal.config.ColourScheme.White
case "49":
terminal.buffer.CursorAttr().BgColour = terminal.config.ColourScheme.Background
terminal.ActiveBuffer().CursorAttr().BgColour = terminal.config.ColourScheme.Background
case "40":
terminal.buffer.CursorAttr().BgColour = terminal.config.ColourScheme.Black
terminal.ActiveBuffer().CursorAttr().BgColour = terminal.config.ColourScheme.Black
case "41":
terminal.buffer.CursorAttr().BgColour = terminal.config.ColourScheme.Red
terminal.ActiveBuffer().CursorAttr().BgColour = terminal.config.ColourScheme.Red
case "42":
terminal.buffer.CursorAttr().BgColour = terminal.config.ColourScheme.Green
terminal.ActiveBuffer().CursorAttr().BgColour = terminal.config.ColourScheme.Green
case "43":
terminal.buffer.CursorAttr().BgColour = terminal.config.ColourScheme.Yellow
terminal.ActiveBuffer().CursorAttr().BgColour = terminal.config.ColourScheme.Yellow
case "44":
terminal.buffer.CursorAttr().BgColour = terminal.config.ColourScheme.Blue
terminal.ActiveBuffer().CursorAttr().BgColour = terminal.config.ColourScheme.Blue
case "45":
terminal.buffer.CursorAttr().BgColour = terminal.config.ColourScheme.Magenta
terminal.ActiveBuffer().CursorAttr().BgColour = terminal.config.ColourScheme.Magenta
case "46":
terminal.buffer.CursorAttr().BgColour = terminal.config.ColourScheme.Cyan
terminal.ActiveBuffer().CursorAttr().BgColour = terminal.config.ColourScheme.Cyan
case "47":
terminal.buffer.CursorAttr().BgColour = terminal.config.ColourScheme.White
terminal.ActiveBuffer().CursorAttr().BgColour = terminal.config.ColourScheme.White
case "100":
terminal.buffer.CursorAttr().BgColour = terminal.config.ColourScheme.DarkGrey
terminal.ActiveBuffer().CursorAttr().BgColour = terminal.config.ColourScheme.DarkGrey
case "101":
terminal.buffer.CursorAttr().BgColour = terminal.config.ColourScheme.LightRed
terminal.ActiveBuffer().CursorAttr().BgColour = terminal.config.ColourScheme.LightRed
case "102":
terminal.buffer.CursorAttr().BgColour = terminal.config.ColourScheme.LightGreen
terminal.ActiveBuffer().CursorAttr().BgColour = terminal.config.ColourScheme.LightGreen
case "103":
terminal.buffer.CursorAttr().BgColour = terminal.config.ColourScheme.LightYellow
terminal.ActiveBuffer().CursorAttr().BgColour = terminal.config.ColourScheme.LightYellow
case "104":
terminal.buffer.CursorAttr().BgColour = terminal.config.ColourScheme.LightBlue
terminal.ActiveBuffer().CursorAttr().BgColour = terminal.config.ColourScheme.LightBlue
case "105":
terminal.buffer.CursorAttr().BgColour = terminal.config.ColourScheme.LightMagenta
terminal.ActiveBuffer().CursorAttr().BgColour = terminal.config.ColourScheme.LightMagenta
case "106":
terminal.buffer.CursorAttr().BgColour = terminal.config.ColourScheme.LightCyan
terminal.ActiveBuffer().CursorAttr().BgColour = terminal.config.ColourScheme.LightCyan
case "107":
terminal.buffer.CursorAttr().BgColour = terminal.config.ColourScheme.White
terminal.ActiveBuffer().CursorAttr().BgColour = terminal.config.ColourScheme.White
default:
return fmt.Errorf("Unknown SGR control sequence: (ESC[%s%sm)", param, intermediate)
}

View File

@ -15,8 +15,14 @@ import (
"go.uber.org/zap"
)
const (
MainBuffer uint8 = 0
AltBuffer uint8 = 1
)
type Terminal struct {
buffer *buffer.Buffer
buffers []*buffer.Buffer
activeBufferIndex uint8
lock sync.Mutex
pty *os.File
logger *zap.SugaredLogger
@ -50,10 +56,16 @@ type Position struct {
func New(pty *os.File, logger *zap.SugaredLogger, config config.Config) *Terminal {
return &Terminal{
buffer: buffer.NewBuffer(0, 0, buffer.CellAttributes{
buffers: []*buffer.Buffer{
buffer.NewBuffer(1, 1, buffer.CellAttributes{
FgColour: config.ColourScheme.Foreground,
BgColour: config.ColourScheme.Background,
}),
buffer.NewBuffer(1, 1, buffer.CellAttributes{
FgColour: config.ColourScheme.Foreground,
BgColour: config.ColourScheme.Background,
}),
},
pty: pty,
logger: logger,
config: config,
@ -66,38 +78,54 @@ func New(pty *os.File, logger *zap.SugaredLogger, config config.Config) *Termina
}
}
func (terminal *Terminal) UseMainBuffer() {
terminal.activeBufferIndex = MainBuffer
terminal.SetSize(uint(terminal.size.Width), uint(terminal.size.Height))
}
func (terminal *Terminal) UseAltBuffer() {
terminal.activeBufferIndex = AltBuffer
terminal.SetSize(uint(terminal.size.Width), uint(terminal.size.Height))
}
func (terminal *Terminal) ActiveBuffer() *buffer.Buffer {
return terminal.buffers[terminal.activeBufferIndex]
}
func (terminal *Terminal) GetScrollOffset() uint {
return terminal.buffer.GetScrollOffset()
return terminal.ActiveBuffer().GetScrollOffset()
}
func (terminal *Terminal) ScrollDown(lines uint16) {
terminal.buffer.ScrollDown(lines)
terminal.ActiveBuffer().ScrollDown(lines)
}
func (terminal *Terminal) ScrollUp(lines uint16) {
terminal.buffer.ScrollUp(lines)
terminal.ActiveBuffer().ScrollUp(lines)
}
func (terminal *Terminal) ScrollPageDown() {
terminal.buffer.ScrollPageDown()
terminal.ActiveBuffer().ScrollPageDown()
}
func (terminal *Terminal) ScrollPageUp() {
terminal.buffer.ScrollPageUp()
terminal.ActiveBuffer().ScrollPageUp()
}
func (terminal *Terminal) ScrollToEnd() {
terminal.buffer.ScrollToEnd()
terminal.ActiveBuffer().ScrollToEnd()
}
func (terminal *Terminal) GetVisibleLines() []buffer.Line {
return terminal.buffer.GetVisibleLines()
return terminal.ActiveBuffer().GetVisibleLines()
}
func (terminal *Terminal) GetCell(col int, row int) *buffer.Cell {
return terminal.buffer.GetCell(col, row)
func (terminal *Terminal) GetCell(col uint16, row uint16) *buffer.Cell {
return terminal.ActiveBuffer().GetCell(col, row)
}
func (terminal *Terminal) AttachDisplayChangeHandler(handler chan bool) {
terminal.buffer.AttachDisplayChangeHandler(handler)
for i := range terminal.buffers {
terminal.buffers[i].AttachDisplayChangeHandler(handler)
}
}
func (terminal *Terminal) AttachTitleChangeHandler(handler chan bool) {
@ -117,19 +145,19 @@ func (terminal *Terminal) emitTitleChange() {
}
func (terminal *Terminal) GetLogicalCursorX() uint16 {
if terminal.buffer.CursorColumn() >= terminal.buffer.Width() {
if terminal.ActiveBuffer().CursorColumn() >= terminal.ActiveBuffer().Width() {
return 0
}
return terminal.buffer.CursorColumn()
return terminal.ActiveBuffer().CursorColumn()
}
func (terminal *Terminal) GetLogicalCursorY() uint16 {
if terminal.buffer.CursorColumn() >= terminal.buffer.Width() {
return terminal.buffer.CursorLine() + 1
if terminal.ActiveBuffer().CursorColumn() >= terminal.ActiveBuffer().Width() {
return terminal.ActiveBuffer().CursorLine() + 1
}
return terminal.buffer.CursorLine()
return terminal.ActiveBuffer().CursorLine()
}
func (terminal *Terminal) GetTitle() string {
@ -174,21 +202,21 @@ func (terminal *Terminal) Read() error {
}
func (terminal *Terminal) Clear() {
terminal.buffer.Clear()
terminal.ActiveBuffer().Clear()
}
func (terminal *Terminal) GetSize() (int, int) {
return int(terminal.size.Width), int(terminal.size.Height)
}
func (terminal *Terminal) SetSize(newCols int, newLines int) error {
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)
terminal.buffer.ResizeView(terminal.size.Width, terminal.size.Height)
terminal.ActiveBuffer().ResizeView(terminal.size.Width, terminal.size.Height)
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(terminal.pty.Fd()),
uintptr(syscall.TIOCSWINSZ), uintptr(unsafe.Pointer(&terminal.size)))