mirror of https://github.com/liamg/aminal.git
lots more progress
This commit is contained in:
parent
e54e0e2182
commit
d5f5cf8592
108
buffer/buffer.go
108
buffer/buffer.go
|
@ -2,8 +2,6 @@ package buffer
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Buffer struct {
|
||||
|
@ -46,7 +44,7 @@ func (buffer *Buffer) GetCell(viewCol int, viewRow int) *Cell {
|
|||
return &line.cells[viewCol]
|
||||
}
|
||||
|
||||
func (buffer *Buffer) attachDisplayChangeHandler(handler chan bool) {
|
||||
func (buffer *Buffer) AttachDisplayChangeHandler(handler chan bool) {
|
||||
if buffer.displayChangeHandlers == nil {
|
||||
buffer.displayChangeHandlers = []chan bool{}
|
||||
}
|
||||
|
@ -57,7 +55,10 @@ func (buffer *Buffer) attachDisplayChangeHandler(handler chan bool) {
|
|||
func (buffer *Buffer) emitDisplayChange() {
|
||||
for _, channel := range buffer.displayChangeHandlers {
|
||||
go func(c chan bool) {
|
||||
c <- true
|
||||
select {
|
||||
case c <- true:
|
||||
default:
|
||||
}
|
||||
}(channel)
|
||||
}
|
||||
}
|
||||
|
@ -132,44 +133,64 @@ func (buffer *Buffer) Write(runes ...rune) {
|
|||
|
||||
func (buffer *Buffer) incrementCursorPosition() {
|
||||
|
||||
if buffer.CursorColumn()+1 < buffer.Width() {
|
||||
defer buffer.emitDisplayChange()
|
||||
|
||||
if buffer.CursorColumn()+1 < buffer.Width() { // if not at end of line
|
||||
|
||||
buffer.cursorX++
|
||||
} else {
|
||||
if buffer.cursorY == buffer.viewHeight-1 { // if we're on the last line, we can't move the cursor down, we have to move the buffer up, i.e. add a new line
|
||||
|
||||
} else { // we're at the end of the current line
|
||||
|
||||
if buffer.cursorY == buffer.viewHeight-1 {
|
||||
// if we're on the last line, we can't move the cursor down, we have to move the buffer up, i.e. add a new line
|
||||
|
||||
line := newLine()
|
||||
line.setWrapped(true)
|
||||
buffer.lines = append(buffer.lines, line)
|
||||
buffer.cursorX = 0
|
||||
|
||||
} else {
|
||||
// if we're not on the bottom line...
|
||||
|
||||
buffer.cursorX = 0
|
||||
if buffer.Height() < int(buffer.ViewHeight()) {
|
||||
buffer.cursorY++
|
||||
|
||||
_, err := buffer.getCurrentLine()
|
||||
if err != nil {
|
||||
line := newLine()
|
||||
line.setWrapped(true)
|
||||
buffer.lines = append(buffer.lines, line)
|
||||
buffer.cursorY++
|
||||
} else {
|
||||
// @todo test this branch
|
||||
line := &buffer.lines[buffer.RawLine()]
|
||||
line.setWrapped(true)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) CarriageReturn() {
|
||||
|
||||
defer buffer.emitDisplayChange()
|
||||
|
||||
line, err := buffer.getCurrentLine()
|
||||
if err != nil {
|
||||
// @todo check this...
|
||||
buffer.cursorX = 0
|
||||
return
|
||||
}
|
||||
if buffer.cursorX == 0 && line.wrapped {
|
||||
buffer.cursorY--
|
||||
if len(line.cells) == 0 {
|
||||
rawLine := int(buffer.RawLine())
|
||||
buffer.lines = append(buffer.lines[:rawLine], buffer.lines[rawLine+1:]...)
|
||||
}
|
||||
} else {
|
||||
buffer.cursorX = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) NewLine() {
|
||||
|
||||
defer buffer.emitDisplayChange()
|
||||
|
||||
// if we're at the beginning of a line which wrapped from the previous one, and we need a new line, we can effectively not add a new line, and set the current one to non-wrapped
|
||||
if buffer.cursorX == 0 {
|
||||
line := &buffer.lines[buffer.RawLine()]
|
||||
|
@ -190,25 +211,45 @@ func (buffer *Buffer) NewLine() {
|
|||
|
||||
func (buffer *Buffer) MovePosition(x int16, y int16) {
|
||||
|
||||
var toX uint16
|
||||
var toY uint16
|
||||
|
||||
if int16(buffer.cursorX)+x < 0 {
|
||||
x = -int16(buffer.cursorX)
|
||||
toX = 0
|
||||
} else {
|
||||
toX = uint16(int16(buffer.cursorX) + x)
|
||||
}
|
||||
|
||||
if int16(buffer.cursorY)+y < 0 {
|
||||
y = -int16(buffer.cursorY)
|
||||
toY = 0
|
||||
} else {
|
||||
toY = uint16(int16(buffer.cursorY) + y)
|
||||
}
|
||||
|
||||
buffer.SetPosition(uint16(int16(buffer.cursorX)+x), uint16(int16(buffer.cursorY)+y))
|
||||
buffer.SetPosition(toX, toY)
|
||||
}
|
||||
|
||||
func (buffer *Buffer) ShowCursor() {
|
||||
|
||||
}
|
||||
|
||||
func (buffer *Buffer) HideCursor() {
|
||||
|
||||
}
|
||||
|
||||
func (buffer *Buffer) SetCursorBlink(enabled bool) {
|
||||
|
||||
}
|
||||
|
||||
func (buffer *Buffer) SetPosition(col uint16, line uint16) {
|
||||
defer buffer.emitDisplayChange()
|
||||
if col >= buffer.ViewWidth() {
|
||||
col = buffer.ViewWidth() - 1
|
||||
logrus.Errorf("Cannot set cursor position: column %d is outside of the current view width (%d columns)", col, buffer.ViewWidth())
|
||||
//logrus.Errorf("Cannot set cursor position: column %d is outside of the current view width (%d columns)", col, buffer.ViewWidth())
|
||||
}
|
||||
if line >= buffer.ViewHeight() {
|
||||
line = buffer.ViewHeight() - 1
|
||||
logrus.Errorf("Cannot set cursor position: line %d is outside of the current view height (%d lines)", line, buffer.ViewHeight())
|
||||
//logrus.Errorf("Cannot set cursor position: line %d is outside of the current view height (%d lines)", line, buffer.ViewHeight())
|
||||
}
|
||||
buffer.cursorX = col
|
||||
buffer.cursorY = line
|
||||
|
@ -227,10 +268,11 @@ func (buffer *Buffer) GetVisibleLines() []Line {
|
|||
// tested to here
|
||||
|
||||
func (buffer *Buffer) Clear() {
|
||||
defer buffer.emitDisplayChange()
|
||||
for i := 0; i < int(buffer.ViewHeight()); i++ {
|
||||
buffer.lines = append(buffer.lines, newLine())
|
||||
}
|
||||
buffer.SetPosition(0, 0)
|
||||
buffer.SetPosition(0, 0) // do we need to set position?
|
||||
}
|
||||
|
||||
func (buffer *Buffer) getCurrentLine() (*Line, error) {
|
||||
|
@ -243,6 +285,7 @@ func (buffer *Buffer) getCurrentLine() (*Line, error) {
|
|||
}
|
||||
|
||||
func (buffer *Buffer) EraseLine() {
|
||||
defer buffer.emitDisplayChange()
|
||||
line, err := buffer.getCurrentLine()
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -251,6 +294,7 @@ func (buffer *Buffer) EraseLine() {
|
|||
}
|
||||
|
||||
func (buffer *Buffer) EraseLineToCursor() {
|
||||
defer buffer.emitDisplayChange()
|
||||
line, err := buffer.getCurrentLine()
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -263,42 +307,52 @@ func (buffer *Buffer) EraseLineToCursor() {
|
|||
}
|
||||
|
||||
func (buffer *Buffer) EraseLineAfterCursor() {
|
||||
defer buffer.emitDisplayChange()
|
||||
line, err := buffer.getCurrentLine()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for i := int(buffer.cursorX + 1); i < len(line.cells); i++ {
|
||||
line.cells[i].erase()
|
||||
|
||||
max := int(buffer.cursorX + 1)
|
||||
if max > len(line.cells) {
|
||||
max = len(line.cells)
|
||||
}
|
||||
|
||||
line.cells = line.cells[:max]
|
||||
}
|
||||
|
||||
func (buffer *Buffer) EraseDisplayAfterCursor() {
|
||||
defer buffer.emitDisplayChange()
|
||||
line, err := buffer.getCurrentLine()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
line.cells = line.cells[:buffer.cursorX]
|
||||
for i := int(buffer.RawLine() + 1); i < buffer.Height(); i++ {
|
||||
if i < len(buffer.lines) {
|
||||
buffer.lines[i].cells = []Cell{}
|
||||
for i := buffer.cursorY + 1; i < buffer.ViewHeight(); i++ {
|
||||
rawLine := buffer.convertViewLineToRawLine(i)
|
||||
if int(rawLine) < len(buffer.lines) {
|
||||
buffer.lines[int(rawLine)].cells = []Cell{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) EraseDisplayToCursor() {
|
||||
defer buffer.emitDisplayChange()
|
||||
line, err := buffer.getCurrentLine()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
line.cells = line.cells[buffer.cursorX+1:]
|
||||
for i := 0; i < int(buffer.RawLine()); i++ {
|
||||
if i < len(buffer.lines) {
|
||||
buffer.lines[i].cells = []Cell{}
|
||||
for i := uint16(0); i < buffer.cursorY; i++ {
|
||||
rawLine := buffer.convertViewLineToRawLine(i)
|
||||
if int(rawLine) < len(buffer.lines) {
|
||||
buffer.lines[int(rawLine)].cells = []Cell{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (buffer *Buffer) ResizeView(width uint16, height uint16) {
|
||||
defer buffer.emitDisplayChange()
|
||||
buffer.viewWidth = width
|
||||
buffer.viewHeight = height
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
)
|
||||
|
||||
func TestOffsets(t *testing.T) {
|
||||
b := NewBuffer(10, 8)
|
||||
b := NewBuffer(10, 8, CellAttributes{})
|
||||
test := "hellothere\nhellothere\nhellothere\nhellothere\nhellothere\nhellothere\nhellothere\nhellothere\nhellothere\nhellothere\nhellothere\nhellothere\n?"
|
||||
b.Write([]rune(test)...)
|
||||
assert.Equal(t, uint16(10), b.ViewWidth())
|
||||
|
@ -19,7 +19,7 @@ func TestOffsets(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestBufferCreation(t *testing.T) {
|
||||
b := NewBuffer(10, 20)
|
||||
b := NewBuffer(10, 20, CellAttributes{})
|
||||
assert.Equal(t, uint16(10), b.Width())
|
||||
assert.Equal(t, uint16(20), b.ViewHeight())
|
||||
assert.Equal(t, uint16(0), b.CursorColumn())
|
||||
|
@ -29,7 +29,7 @@ func TestBufferCreation(t *testing.T) {
|
|||
|
||||
func TestBufferCursorIncrement(t *testing.T) {
|
||||
|
||||
b := NewBuffer(5, 4)
|
||||
b := NewBuffer(5, 4, CellAttributes{})
|
||||
b.incrementCursorPosition()
|
||||
require.Equal(t, uint16(1), b.CursorColumn())
|
||||
require.Equal(t, uint16(0), b.CursorLine())
|
||||
|
@ -76,7 +76,7 @@ func TestBufferCursorIncrement(t *testing.T) {
|
|||
}
|
||||
func TestBufferWrite(t *testing.T) {
|
||||
|
||||
b := NewBuffer(5, 20)
|
||||
b := NewBuffer(5, 20, CellAttributes{})
|
||||
|
||||
assert.Equal(t, uint16(0), b.CursorColumn())
|
||||
assert.Equal(t, uint16(0), b.CursorLine())
|
||||
|
@ -110,7 +110,7 @@ func TestBufferWrite(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestWritingNewLineAsFirstRuneOnWrappedLine(t *testing.T) {
|
||||
b := NewBuffer(3, 20)
|
||||
b := NewBuffer(3, 20, CellAttributes{})
|
||||
b.Write('a', 'b', 'c')
|
||||
assert.Equal(t, uint16(0), b.cursorX)
|
||||
b.Write(0x0a)
|
||||
|
@ -124,7 +124,7 @@ func TestWritingNewLineAsFirstRuneOnWrappedLine(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestWritingNewLineAsSecondRuneOnWrappedLine(t *testing.T) {
|
||||
b := NewBuffer(3, 20)
|
||||
b := NewBuffer(3, 20, CellAttributes{})
|
||||
b.Write('a', 'b', 'c', 'd')
|
||||
b.Write(0x0a)
|
||||
b.Write('e', 'f')
|
||||
|
@ -143,7 +143,7 @@ func TestWritingNewLineAsSecondRuneOnWrappedLine(t *testing.T) {
|
|||
|
||||
func TestSetPosition(t *testing.T) {
|
||||
|
||||
b := NewBuffer(120, 80)
|
||||
b := NewBuffer(120, 80, CellAttributes{})
|
||||
assert.Equal(t, 0, int(b.CursorColumn()))
|
||||
assert.Equal(t, 0, int(b.CursorLine()))
|
||||
b.SetPosition(60, 10)
|
||||
|
@ -159,7 +159,7 @@ func TestSetPosition(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestMovePosition(t *testing.T) {
|
||||
b := NewBuffer(120, 80)
|
||||
b := NewBuffer(120, 80, CellAttributes{})
|
||||
assert.Equal(t, 0, int(b.CursorColumn()))
|
||||
assert.Equal(t, 0, int(b.CursorLine()))
|
||||
b.MovePosition(-1, -1)
|
||||
|
@ -181,7 +181,7 @@ func TestMovePosition(t *testing.T) {
|
|||
|
||||
func TestVisibleLines(t *testing.T) {
|
||||
|
||||
b := NewBuffer(80, 10)
|
||||
b := NewBuffer(80, 10, CellAttributes{})
|
||||
b.Write([]rune("hello 1\n")...)
|
||||
b.Write([]rune("hello 2\n")...)
|
||||
b.Write([]rune("hello 3\n")...)
|
||||
|
@ -205,7 +205,7 @@ func TestVisibleLines(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClearWithoutFullView(t *testing.T) {
|
||||
b := NewBuffer(80, 10)
|
||||
b := NewBuffer(80, 10, CellAttributes{})
|
||||
b.Write([]rune("hello 1\n")...)
|
||||
b.Write([]rune("hello 2\n")...)
|
||||
b.Write([]rune("hello 3")...)
|
||||
|
@ -217,7 +217,7 @@ func TestClearWithoutFullView(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClearWithFullView(t *testing.T) {
|
||||
b := NewBuffer(80, 5)
|
||||
b := NewBuffer(80, 5, CellAttributes{})
|
||||
b.Write([]rune("hello 1\n")...)
|
||||
b.Write([]rune("hello 2\n")...)
|
||||
b.Write([]rune("hello 3\n")...)
|
||||
|
@ -233,7 +233,32 @@ func TestClearWithFullView(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCarriageReturn(t *testing.T) {
|
||||
b := NewBuffer(80, 20, CellAttributes{})
|
||||
b.Write([]rune("hello!\rsecret")...)
|
||||
lines := b.GetVisibleLines()
|
||||
assert.Equal(t, "secret", lines[0].String())
|
||||
}
|
||||
|
||||
func TestCarriageReturnOnWrappedLine(t *testing.T) {
|
||||
b := NewBuffer(80, 6, CellAttributes{})
|
||||
b.Write([]rune("hello!\rsecret")...)
|
||||
lines := b.GetVisibleLines()
|
||||
assert.Equal(t, "secret", lines[0].String())
|
||||
}
|
||||
|
||||
func TestCarriageReturnOnOverWrappedLine(t *testing.T) {
|
||||
b := NewBuffer(6, 10, CellAttributes{})
|
||||
b.Write([]rune("hello there!\rsecret sauce")...)
|
||||
lines := b.GetVisibleLines()
|
||||
require.Equal(t, 4, len(lines))
|
||||
assert.Equal(t, "hello ", lines[0].String())
|
||||
assert.Equal(t, "secret", lines[1].String())
|
||||
assert.Equal(t, " sauce", lines[2].String())
|
||||
assert.Equal(t, "", lines[3].String())
|
||||
}
|
||||
|
||||
func TestResizeView(t *testing.T) {
|
||||
b := NewBuffer(80, 20)
|
||||
b := NewBuffer(80, 20, CellAttributes{})
|
||||
b.ResizeView(40, 10)
|
||||
}
|
||||
|
|
|
@ -22,11 +22,18 @@ func newCell() Cell {
|
|||
}
|
||||
|
||||
func (cell *Cell) Rune() rune {
|
||||
if cell.r == 0 {
|
||||
return '~'
|
||||
}
|
||||
return cell.r
|
||||
}
|
||||
|
||||
func (cell *Cell) Fg() [3]float32 {
|
||||
return cell.attr.FgColour
|
||||
if cell.r == 0 {
|
||||
return [3]float32{0.5, 0.5, 0.5}
|
||||
}
|
||||
return [3]float32{0.9, 0.9, 0.9} // @todo fix this
|
||||
//return cell.attr.FgColour
|
||||
}
|
||||
|
||||
func (cell *Cell) Bg() [3]float32 {
|
||||
|
|
|
@ -8,6 +8,11 @@ import (
|
|||
type Config struct {
|
||||
DebugMode bool `yaml:"debug"`
|
||||
ColourScheme terminal.ColourScheme
|
||||
Rendering RenderingConfig `yaml:"rendering"`
|
||||
}
|
||||
|
||||
type RenderingConfig struct {
|
||||
AlwaysRepaint bool `yaml:"always_repaint"`
|
||||
}
|
||||
|
||||
var DefaultConfig = Config{
|
||||
|
|
Binary file not shown.
65
gui/gui.go
65
gui/gui.go
|
@ -116,11 +116,6 @@ func (gui *GUI) Render() error {
|
|||
|
||||
gui.logger.Debugf("Starting pty read handling...")
|
||||
|
||||
updateChan := make(chan bool, 1024)
|
||||
|
||||
gui.terminal.OnUpdate(func() {
|
||||
updateChan <- true
|
||||
})
|
||||
go func() {
|
||||
err := gui.terminal.Read()
|
||||
if err != nil {
|
||||
|
@ -129,9 +124,6 @@ func (gui *GUI) Render() error {
|
|||
gui.Close()
|
||||
}()
|
||||
|
||||
ticker := time.NewTicker(time.Millisecond * 100)
|
||||
defer ticker.Stop()
|
||||
|
||||
gui.logger.Debugf("Starting render...")
|
||||
|
||||
gl.UseProgram(program)
|
||||
|
@ -142,7 +134,7 @@ func (gui *GUI) Render() error {
|
|||
//gl.DepthFunc(gl.LESS)
|
||||
gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
|
||||
|
||||
glfw.SwapInterval(1)
|
||||
//glfw.SwapInterval(1)
|
||||
|
||||
gl.ClearColor(
|
||||
gui.config.ColourScheme.DefaultBg[0],
|
||||
|
@ -151,26 +143,61 @@ func (gui *GUI) Render() error {
|
|||
1.0,
|
||||
)
|
||||
|
||||
changeChan := make(chan bool, 1)
|
||||
titleChan := make(chan bool, 1)
|
||||
|
||||
gui.terminal.AttachTitleChangeHandler(titleChan)
|
||||
gui.terminal.AttachDisplayChangeHandler(changeChan)
|
||||
|
||||
frames := 0
|
||||
frameCount := 0
|
||||
fps := 0
|
||||
ticker := time.NewTicker(time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for !gui.window.ShouldClose() {
|
||||
|
||||
select {
|
||||
|
||||
case <-changeChan:
|
||||
frames = 2
|
||||
gui.logger.Sync()
|
||||
case <-titleChan:
|
||||
gui.window.SetTitle(gui.terminal.GetTitle())
|
||||
case <-ticker.C:
|
||||
fps = frameCount
|
||||
frameCount = 0
|
||||
default:
|
||||
}
|
||||
|
||||
gl.UseProgram(program)
|
||||
|
||||
// Render the string.
|
||||
// @todo uncommentbut dont run all of the time... - perhaps use onTitleChange event from terminal?
|
||||
//gui.window.SetTitle(gui.terminal.GetTitle())
|
||||
if gui.config.Rendering.AlwaysRepaint || frames > 0 {
|
||||
|
||||
//gl.ClearColor(0.5, 0.5, 0.5, 1.0)
|
||||
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
|
||||
cols, rows := gui.getTermSize()
|
||||
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)
|
||||
for row := 0; row < rows; row++ {
|
||||
for col := 0; col < cols; col++ {
|
||||
gui.renderer.DrawCell(gui.terminal.GetCell(col, row), col, row)
|
||||
}
|
||||
}
|
||||
|
||||
gui.font.SetColor(1, 0.5, 0.5, 0.5)
|
||||
fpsData := ""
|
||||
if gui.config.Rendering.AlwaysRepaint {
|
||||
fpsData = fmt.Sprintf("%d FPS", fps)
|
||||
}
|
||||
gui.font.Print(10, float32(gui.height-20), 1.5, fmt.Sprintf("%s", fpsData))
|
||||
}
|
||||
|
||||
glfw.PollEvents()
|
||||
gui.window.SwapBuffers()
|
||||
|
||||
if gui.config.Rendering.AlwaysRepaint || frames > 0 {
|
||||
frameCount++
|
||||
gui.window.SwapBuffers()
|
||||
frames--
|
||||
}
|
||||
}
|
||||
|
||||
gui.logger.Debugf("Stopping render...")
|
||||
|
|
3
main.go
3
main.go
|
@ -34,8 +34,7 @@ func loadConfigFile() config.Config {
|
|||
|
||||
places := []string{
|
||||
fmt.Sprintf("%s/.raft.yml", home),
|
||||
fmt.Sprintf("%s/.raft/config.yml", home),
|
||||
fmt.Sprintf("%s/.config/raft/config.yml", home),
|
||||
fmt.Sprintf("%s/.config/raft.yml", home),
|
||||
}
|
||||
|
||||
for _, place := range places {
|
||||
|
|
|
@ -53,11 +53,7 @@ CSI:
|
|||
distance = 1
|
||||
}
|
||||
}
|
||||
if terminal.position.Line-distance >= 0 {
|
||||
terminal.position.Line -= distance
|
||||
} else {
|
||||
terminal.position.Line = 0
|
||||
}
|
||||
terminal.buffer.MovePosition(0, -int16(distance))
|
||||
case 'B':
|
||||
distance := 1
|
||||
if len(params) > 0 {
|
||||
|
@ -68,12 +64,7 @@ CSI:
|
|||
}
|
||||
}
|
||||
|
||||
_, h := terminal.GetSize()
|
||||
if terminal.position.Line+distance >= h {
|
||||
terminal.position.Line = h - 1
|
||||
} else {
|
||||
terminal.position.Line += distance
|
||||
}
|
||||
terminal.buffer.MovePosition(0, int16(distance))
|
||||
case 'C':
|
||||
|
||||
distance := 1
|
||||
|
@ -85,11 +76,7 @@ CSI:
|
|||
}
|
||||
}
|
||||
|
||||
terminal.position.Col += distance
|
||||
w, _ := terminal.GetSize()
|
||||
if terminal.position.Col >= w {
|
||||
terminal.position.Col = w - 1
|
||||
}
|
||||
terminal.buffer.MovePosition(int16(distance), 0)
|
||||
|
||||
case 'D':
|
||||
|
||||
|
@ -102,10 +89,7 @@ CSI:
|
|||
}
|
||||
}
|
||||
|
||||
terminal.position.Col -= distance
|
||||
if terminal.position.Col < 0 {
|
||||
terminal.position.Col = 0
|
||||
}
|
||||
terminal.buffer.MovePosition(-int16(distance), 0)
|
||||
|
||||
case 'E':
|
||||
distance := 1
|
||||
|
@ -117,8 +101,8 @@ CSI:
|
|||
}
|
||||
}
|
||||
|
||||
terminal.position.Line += distance
|
||||
terminal.position.Col = 0
|
||||
terminal.buffer.MovePosition(0, int16(distance))
|
||||
terminal.buffer.SetPosition(0, terminal.buffer.CursorLine())
|
||||
|
||||
case 'F':
|
||||
|
||||
|
@ -130,12 +114,8 @@ CSI:
|
|||
distance = 1
|
||||
}
|
||||
}
|
||||
if terminal.position.Line-distance >= 0 {
|
||||
terminal.position.Line -= distance
|
||||
} else {
|
||||
terminal.position.Line = 0
|
||||
}
|
||||
terminal.position.Col = 0
|
||||
terminal.buffer.MovePosition(0, -int16(distance))
|
||||
terminal.buffer.SetPosition(0, terminal.buffer.CursorLine())
|
||||
|
||||
case 'G':
|
||||
|
||||
|
@ -148,7 +128,7 @@ CSI:
|
|||
}
|
||||
}
|
||||
|
||||
terminal.position.Col = distance - 1 // 1 based to 0 based
|
||||
terminal.buffer.SetPosition(uint16(distance-1), terminal.buffer.CursorLine())
|
||||
|
||||
case 'H', 'f':
|
||||
|
||||
|
@ -168,19 +148,19 @@ CSI:
|
|||
}
|
||||
}
|
||||
}
|
||||
terminal.position.Col = x - 1
|
||||
terminal.position.Line = y - 1
|
||||
|
||||
terminal.buffer.SetPosition(uint16(x-1), uint16(y-1))
|
||||
|
||||
default:
|
||||
switch param + intermediate + string(final) {
|
||||
case "?25h":
|
||||
terminal.showCursor()
|
||||
terminal.buffer.ShowCursor()
|
||||
case "?25l":
|
||||
terminal.hideCursor()
|
||||
terminal.buffer.HideCursor()
|
||||
case "?12h":
|
||||
// todo enable cursor blink
|
||||
terminal.buffer.SetCursorBlink(true)
|
||||
case "?12l":
|
||||
// todo disable cursor blink
|
||||
terminal.buffer.SetCursorBlink(false)
|
||||
default:
|
||||
return fmt.Errorf("Unknown CSI control sequence: 0x%02X (ESC[%s%s%s)", final, param, intermediate, string(final))
|
||||
}
|
||||
|
|
|
@ -38,15 +38,11 @@ func (terminal *Terminal) processInput(ctx context.Context, buffer chan rune) {
|
|||
continue
|
||||
}
|
||||
|
||||
if b != 0x0d {
|
||||
//lineOverflow = false
|
||||
}
|
||||
|
||||
switch b {
|
||||
case 0x0a:
|
||||
terminal.buffer.NewLine()
|
||||
case 0x0d:
|
||||
terminal.buffer.SetPosition(0, terminal.buffer.CursorLine())
|
||||
terminal.buffer.CarriageReturn()
|
||||
case 0x08:
|
||||
// backspace
|
||||
terminal.buffer.MovePosition(-1, 0)
|
||||
|
@ -62,6 +58,5 @@ func (terminal *Terminal) processInput(ctx context.Context, buffer chan rune) {
|
|||
}
|
||||
}
|
||||
|
||||
terminal.triggerOnUpdate()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ func oscHandler(buffer chan rune, terminal *Terminal) error {
|
|||
}
|
||||
title = append(title, b)
|
||||
}
|
||||
terminal.title = string(title)
|
||||
terminal.SetTitle(string(title))
|
||||
} else {
|
||||
return fmt.Errorf("Invalid OSC 0 control sequence: 0x%02X", b)
|
||||
}
|
||||
|
|
|
@ -16,15 +16,13 @@ import (
|
|||
|
||||
type Terminal struct {
|
||||
buffer *buffer.Buffer
|
||||
position Position // line and col
|
||||
lock sync.Mutex
|
||||
pty *os.File
|
||||
logger *zap.SugaredLogger
|
||||
title string
|
||||
onUpdate []func()
|
||||
size Winsize
|
||||
colourScheme ColourScheme
|
||||
cursorVisible bool
|
||||
titleHandlers []chan bool
|
||||
}
|
||||
|
||||
type Line struct {
|
||||
|
@ -53,9 +51,8 @@ func New(pty *os.File, logger *zap.SugaredLogger, colourScheme ColourScheme) *Te
|
|||
}),
|
||||
pty: pty,
|
||||
logger: logger,
|
||||
onUpdate: []func(){},
|
||||
colourScheme: colourScheme,
|
||||
cursorVisible: true,
|
||||
titleHandlers: []chan bool{},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,51 +60,31 @@ func (terminal *Terminal) GetCell(col int, row int) *buffer.Cell {
|
|||
return terminal.buffer.GetCell(col, row)
|
||||
}
|
||||
|
||||
func (terminal *Terminal) OnUpdate(handler func()) {
|
||||
terminal.onUpdate = append(terminal.onUpdate, handler)
|
||||
func (terminal *Terminal) AttachDisplayChangeHandler(handler chan bool) {
|
||||
terminal.buffer.AttachDisplayChangeHandler(handler)
|
||||
}
|
||||
|
||||
func (terminal *Terminal) triggerOnUpdate() {
|
||||
for _, handler := range terminal.onUpdate {
|
||||
go handler()
|
||||
func (terminal *Terminal) AttachTitleChangeHandler(handler chan bool) {
|
||||
terminal.titleHandlers = append(terminal.titleHandlers, handler)
|
||||
}
|
||||
|
||||
func (terminal *Terminal) emitTitleChange() {
|
||||
for _, h := range terminal.titleHandlers {
|
||||
go func(c chan bool) {
|
||||
c <- true
|
||||
}(h)
|
||||
}
|
||||
}
|
||||
|
||||
func (terminal *Terminal) getPosition() Position {
|
||||
return terminal.position
|
||||
}
|
||||
|
||||
func (terminal *Terminal) IsCursorVisible() bool {
|
||||
return terminal.cursorVisible
|
||||
}
|
||||
|
||||
func (terminal *Terminal) showCursor() {
|
||||
terminal.cursorVisible = true
|
||||
}
|
||||
|
||||
func (terminal *Terminal) hideCursor() {
|
||||
terminal.cursorVisible = false
|
||||
}
|
||||
|
||||
func (terminal *Terminal) incrementPosition() {
|
||||
terminal.SetPosition(terminal.position.Col+1, terminal.position.Line)
|
||||
}
|
||||
|
||||
func (terminal *Terminal) SetPosition(col int, line int) {
|
||||
terminal.position = Position{
|
||||
Col: col,
|
||||
Line: line,
|
||||
}
|
||||
}
|
||||
|
||||
func (terminal *Terminal) GetPosition() Position {
|
||||
return terminal.position
|
||||
}
|
||||
|
||||
func (terminal *Terminal) GetTitle() string {
|
||||
return terminal.title
|
||||
}
|
||||
|
||||
func (terminal *Terminal) SetTitle(title string) {
|
||||
terminal.title = title
|
||||
terminal.emitTitleChange()
|
||||
}
|
||||
|
||||
// Write sends data, i.e. locally typed keystrokes to the pty
|
||||
func (terminal *Terminal) Write(data []byte) error {
|
||||
_, err := terminal.pty.Write(data)
|
||||
|
|
Loading…
Reference in New Issue