more efficient text rendering and streamlining of output handling

This commit is contained in:
Liam Galvin 2018-12-01 22:09:20 +00:00
parent 1a932fa2c3
commit 177e928b71
13 changed files with 323 additions and 269 deletions

View File

@ -22,12 +22,6 @@ install-tools:
which dep || curl -L https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
which packr || go get -u github.com/gobuffalo/packr/packr
.PHONY: update-fonts
update-fonts: install-tools
curl -L https://github.com/ryanoasis/nerd-fonts/raw/master/patched-fonts/Hack/Regular/complete/Hack%20Regular%20Nerd%20Font%20Complete.ttf -o "${FONTPATH}/Hack Regular Nerd Font Complete.ttf"
curl -L https://github.com/ryanoasis/nerd-fonts/raw/master/patched-fonts/Hack/Bold/complete/Hack%20Bold%20Nerd%20Font%20Complete.ttf -o "${FONTPATH}/Hack Bold Nerd Font Complete.ttf"
packr -v
.PHONY: build-linux
build-linux:
mkdir -p bin/linux

View File

@ -610,20 +610,10 @@ func (buffer *Buffer) ReverseIndex() {
func (buffer *Buffer) Write(runes ...rune) {
// scroll to bottom on input
inc := true
buffer.scrollLinesFromBottom = 0
for _, r := range runes {
if r == 0x0a {
buffer.NewLine()
continue
} else if r == 0x0d {
buffer.CarriageReturn()
continue
} else if r == 0x9 {
buffer.Tab()
continue
}
line := buffer.getCurrentLine()
if buffer.replaceMode {
@ -634,7 +624,7 @@ func (buffer *Buffer) Write(runes ...rune) {
}
for int(buffer.CursorColumn()) >= len(line.cells) {
line.cells = append(line.cells, NewBackgroundCell(buffer.cursorAttr.BgColour))
line.cells = append(line.cells, Cell{})
}
line.cells[buffer.cursorX].attr = buffer.cursorAttr
line.cells[buffer.cursorX].setRune(r)
@ -665,31 +655,23 @@ func (buffer *Buffer) Write(runes ...rune) {
} else {
for int(buffer.CursorColumn()) >= len(line.cells) {
line.cells = append(line.cells, NewBackgroundCell(buffer.cursorAttr.BgColour))
line.cells = append(line.cells, Cell{})
}
cell := &line.cells[buffer.CursorColumn()]
cell.setRune(r)
cell.attr = buffer.cursorAttr
}
if inc {
buffer.incrementCursorPosition()
}
buffer.incrementCursorPosition()
}
}
func (buffer *Buffer) incrementCursorPosition() {
defer buffer.emitDisplayChange()
// we can increment one column past the end of the line.
// this is effectively the beginning of the next line, except when we \r etc.
if buffer.CursorColumn() < buffer.Width() { // if not at end of line
if buffer.CursorColumn() < buffer.Width() {
buffer.cursorX++
}
}
@ -708,7 +690,6 @@ func (buffer *Buffer) Backspace() {
}
func (buffer *Buffer) CarriageReturn() {
defer buffer.emitDisplayChange()
for {
line := buffer.getCurrentLine()
@ -726,7 +707,6 @@ func (buffer *Buffer) CarriageReturn() {
}
func (buffer *Buffer) Tab() {
defer buffer.emitDisplayChange()
tabSize := 4
shift := int(buffer.cursorX-1) % tabSize
if shift == 0 {
@ -738,7 +718,6 @@ func (buffer *Buffer) Tab() {
}
func (buffer *Buffer) NewLine() {
defer buffer.emitDisplayChange()
buffer.cursorX = 0
buffer.Index()

View File

@ -11,10 +11,18 @@ import (
func TestOffsets(t *testing.T) {
b := NewBuffer(10, 3, CellAttributes{})
b.Write([]rune("hello\r\n")...)
b.Write([]rune("hello\r\n")...)
b.Write([]rune("hello\r\n")...)
b.Write([]rune("hello\r\n")...)
b.Write([]rune("hello")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello")...)
assert.Equal(t, uint16(10), b.ViewWidth())
assert.Equal(t, uint16(10), b.Width())
@ -84,14 +92,14 @@ func TestWritingNewLineAsFirstRuneOnWrappedLine(t *testing.T) {
b.Write('a', 'b', 'c')
assert.Equal(t, uint16(3), b.cursorX)
assert.Equal(t, uint16(0), b.cursorY)
b.Write(0x0a)
b.NewLine()
assert.Equal(t, uint16(0), b.cursorX)
assert.Equal(t, uint16(1), b.cursorY)
b.Write('d', 'e', 'f')
assert.Equal(t, uint16(3), b.cursorX)
assert.Equal(t, uint16(1), b.cursorY)
b.Write(0x0a)
b.NewLine()
assert.Equal(t, uint16(0), b.cursorX)
assert.Equal(t, uint16(2), b.cursorY)
@ -114,11 +122,11 @@ func TestWritingNewLineAsSecondRuneOnWrappedLine(t *testing.T) {
*/
b.Write('a', 'b', 'c', 'd')
b.Write(0x0a)
b.NewLine()
b.Write('e', 'f')
b.Write(0x0a)
b.Write(0x0a)
b.Write(0x0a)
b.NewLine()
b.NewLine()
b.NewLine()
b.Write('z')
assert.Equal(t, "abc", b.lines[0].String())
@ -170,19 +178,45 @@ func TestMovePosition(t *testing.T) {
func TestVisibleLines(t *testing.T) {
b := NewBuffer(80, 10, CellAttributes{})
b.Write([]rune("hello 1\r\n")...)
b.Write([]rune("hello 2\r\n")...)
b.Write([]rune("hello 3\r\n")...)
b.Write([]rune("hello 4\r\n")...)
b.Write([]rune("hello 5\r\n")...)
b.Write([]rune("hello 6\r\n")...)
b.Write([]rune("hello 7\r\n")...)
b.Write([]rune("hello 8\r\n")...)
b.Write([]rune("hello 9\r\n")...)
b.Write([]rune("hello 10\r\n")...)
b.Write([]rune("hello 11\r\n")...)
b.Write([]rune("hello 12\r\n")...)
b.Write([]rune("hello 13\r\n")...)
b.Write([]rune("hello 1")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 2")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 3")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 4")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 5")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 6")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 7")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 8")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 9")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 10")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 11")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 12")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 13")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 14")...)
lines := b.GetVisibleLines()
@ -194,9 +228,13 @@ func TestVisibleLines(t *testing.T) {
func TestClearWithoutFullView(t *testing.T) {
b := NewBuffer(80, 10, CellAttributes{})
b.Write([]rune("hello 1\r\n")...)
b.Write([]rune("hello 2\r\n")...)
b.Write([]rune("hello 3")...)
b.Write([]rune("hello 1")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 1")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 1")...)
b.Clear()
lines := b.GetVisibleLines()
for _, line := range lines {
@ -206,14 +244,28 @@ func TestClearWithoutFullView(t *testing.T) {
func TestClearWithFullView(t *testing.T) {
b := NewBuffer(80, 5, CellAttributes{})
b.Write([]rune("hello 1\r\n")...)
b.Write([]rune("hello 2\r\n")...)
b.Write([]rune("hello 3\r\n")...)
b.Write([]rune("hello 4\r\n")...)
b.Write([]rune("hello 5\r\n")...)
b.Write([]rune("hello 6\r\n")...)
b.Write([]rune("hello 7\r\n")...)
b.Write([]rune("hello 8\r\n")...)
b.Write([]rune("hello 1")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 1")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 1")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 1")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 1")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 1")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 1")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("hello 1")...)
b.Clear()
lines := b.GetVisibleLines()
for _, line := range lines {
@ -241,7 +293,10 @@ func TestCarriageReturnOnFullLine(t *testing.T) {
func TestCarriageReturnOnFullLastLine(t *testing.T) {
b := NewBuffer(20, 2, CellAttributes{})
b.Write([]rune("\nabcdeabcdeabcdeabcde\rxxxxxxxxxxxxxxxxxxxx")...)
b.NewLine()
b.Write([]rune("abcdeabcdeabcdeabcde")...)
b.CarriageReturn()
b.Write([]rune("xxxxxxxxxxxxxxxxxxxx")...)
lines := b.GetVisibleLines()
assert.Equal(t, "", lines[0].String())
assert.Equal(t, "xxxxxxxxxxxxxxxxxxxx", lines[1].String())
@ -249,7 +304,10 @@ func TestCarriageReturnOnFullLastLine(t *testing.T) {
func TestCarriageReturnOnWrappedLine(t *testing.T) {
b := NewBuffer(80, 6, CellAttributes{})
b.Write([]rune("hello!\rsecret")...)
b.Write([]rune("hello!")...)
b.CarriageReturn()
b.Write([]rune("secret")...)
lines := b.GetVisibleLines()
assert.Equal(t, "secret", lines[0].String())
}
@ -257,14 +315,22 @@ func TestCarriageReturnOnWrappedLine(t *testing.T) {
func TestCarriageReturnOnLineThatDoesntExist(t *testing.T) {
b := NewBuffer(6, 10, CellAttributes{})
b.cursorY = 3
b.Write('\r')
b.CarriageReturn()
assert.Equal(t, uint16(0), b.cursorX)
assert.Equal(t, uint16(3), b.cursorY)
}
func TestGetCell(t *testing.T) {
b := NewBuffer(80, 20, CellAttributes{})
b.Write([]rune("Hello\r\nthere\r\nsomething...")...)
b.Write([]rune("Hello")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("there")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("something...")...)
cell := b.GetCell(8, 2)
require.NotNil(t, cell)
assert.Equal(t, 'g', cell.Rune())
@ -272,7 +338,17 @@ func TestGetCell(t *testing.T) {
func TestGetCellWithHistory(t *testing.T) {
b := NewBuffer(80, 2, CellAttributes{})
b.Write([]rune("Hello\r\nthere\r\nsomething...")...)
b.Write([]rune("Hello")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("there")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("something...")...)
cell := b.GetCell(8, 1)
require.NotNil(t, cell)
assert.Equal(t, 'g', cell.Rune())
@ -301,7 +377,35 @@ func TestCursorPositionQuerying(t *testing.T) {
func TestRawPositionQuerying(t *testing.T) {
b := NewBuffer(80, 5, CellAttributes{})
b.Write([]rune("a\r\na\r\na\r\na\r\na\r\na\r\na\r\na\r\na\r\na")...)
b.Write([]rune("a")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("a")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("a")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("a")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("a")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("a")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("a")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("a")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("a")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("a")...)
b.cursorX = 3
b.cursorY = 4
assert.Equal(t, uint64(9), b.RawLine())
@ -310,7 +414,10 @@ func TestRawPositionQuerying(t *testing.T) {
// CSI 2 K
func TestEraseLine(t *testing.T) {
b := NewBuffer(80, 5, CellAttributes{})
b.Write([]rune("hello, this is a test\r\nthis line should be deleted")...)
b.Write([]rune("hello, this is a test")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("this line should be deleted")...)
b.EraseLine()
assert.Equal(t, "hello, this is a test", b.lines[0].String())
assert.Equal(t, "", b.lines[1].String())
@ -319,7 +426,11 @@ func TestEraseLine(t *testing.T) {
// CSI 1 K
func TestEraseLineToCursor(t *testing.T) {
b := NewBuffer(80, 5, CellAttributes{})
b.Write([]rune("hello, this is a test\r\ndeleted")...)
b.Write([]rune("hello, this is a test")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("deleted")...)
b.MovePosition(-3, 0)
b.EraseLineToCursor()
assert.Equal(t, "hello, this is a test", b.lines[0].String())
@ -329,7 +440,10 @@ func TestEraseLineToCursor(t *testing.T) {
// CSI 0 K
func TestEraseLineAfterCursor(t *testing.T) {
b := NewBuffer(80, 5, CellAttributes{})
b.Write([]rune("hello, this is a test\r\ndeleted")...)
b.Write([]rune("hello, this is a test")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("deleted")...)
b.MovePosition(-3, 0)
b.EraseLineFromCursor()
assert.Equal(t, "hello, this is a test", b.lines[0].String())
@ -337,7 +451,13 @@ func TestEraseLineAfterCursor(t *testing.T) {
}
func TestEraseDisplay(t *testing.T) {
b := NewBuffer(80, 5, CellAttributes{})
b.Write([]rune("hello\r\nasdasd\r\nthing")...)
b.Write([]rune("hello")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("asdasd")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("thing")...)
b.MovePosition(2, 1)
b.EraseDisplay()
lines := b.GetVisibleLines()
@ -347,7 +467,13 @@ func TestEraseDisplay(t *testing.T) {
}
func TestEraseDisplayToCursor(t *testing.T) {
b := NewBuffer(80, 5, CellAttributes{})
b.Write([]rune("hello\r\nasdasd\r\nthing")...)
b.Write([]rune("hello")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("asdasd")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("thing")...)
b.MovePosition(-2, 0)
b.EraseDisplayToCursor()
lines := b.GetVisibleLines()
@ -359,7 +485,13 @@ func TestEraseDisplayToCursor(t *testing.T) {
func TestEraseDisplayFromCursor(t *testing.T) {
b := NewBuffer(80, 5, CellAttributes{})
b.Write([]rune("hello\r\nasdasd\r\nthings")...)
b.Write([]rune("hello")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("asdasd")...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune("things")...)
b.MovePosition(-3, -1)
b.EraseDisplayFromCursor()
lines := b.GetVisibleLines()
@ -381,9 +513,12 @@ func TestHorizontalResizeView(t *testing.T) {
b := NewBuffer(80, 10, CellAttributes{})
// 60 characters
b.Write([]rune(
`hellohellohellohellohellohellohellohellohellohellohellohello
goodbyegoodbye`)...)
b.Write([]rune(`hellohellohellohellohellohellohellohellohellohellohellohello`)...)
b.CarriageReturn()
b.NewLine()
b.Write([]rune(`goodbyegoodbye`)...)
require.Equal(t, uint16(14), b.cursorX)
require.Equal(t, uint16(1), b.cursorY)

View File

@ -40,10 +40,16 @@ func (cell *Cell) Rune() rune {
}
func (cell *Cell) Fg() [3]float32 {
if cell.Attr().Reverse {
return cell.attr.BgColour
}
return cell.attr.FgColour
}
func (cell *Cell) Bg() [3]float32 {
if cell.Attr().Reverse {
return cell.attr.FgColour
}
return cell.attr.BgColour
}

View File

@ -2,14 +2,15 @@ package glfont
import (
"fmt"
"image"
"image/draw"
"io"
"github.com/go-gl/gl/all-core/gl"
"github.com/golang/freetype"
"github.com/golang/freetype/truetype"
"golang.org/x/image/font"
"golang.org/x/image/math/fixed"
"image"
"image/draw"
"io"
)
const DPI = 72

File diff suppressed because one or more lines are too long

View File

@ -28,16 +28,16 @@ func (a *annotation) render(gui *GUI) {
}
cell := cells[x]
var colour *[3]float32
var colour [3]float32 = cell.Fg()
var alpha float32 = 0.6
if y == int(a.hint.StartY) {
if x >= int(a.hint.StartX) && x <= int(a.hint.StartX+uint16(len(a.hint.Word))) {
colour = &[3]float32{0.2, 1.0, 0.2}
colour = [3]float32{0.2, 1.0, 0.2}
alpha = 1.0
}
}
gui.renderer.DrawCellText(cell, uint(x), uint(y), alpha, colour)
gui.renderer.DrawCellText(string(cell.Rune()), uint(x), uint(y), alpha, colour, cell.Attr().Bold)
}
}

View File

@ -5,73 +5,25 @@ import "github.com/liamg/aminal/glfont"
type FontMap struct {
defaultFont *glfont.Font
defaultBoldFont *glfont.Font
runeMap map[rune]*glfont.Font
ranges map[runeRange]*glfont.Font
}
type runeRange struct {
start rune
end rune // inclusive
}
func NewFontMap(defaultFont *glfont.Font, defaultBoldFont *glfont.Font) *FontMap {
return &FontMap{
defaultFont: defaultFont,
defaultBoldFont: defaultBoldFont,
runeMap: map[rune]*glfont.Font{},
ranges: map[runeRange]*glfont.Font{},
}
}
func (fm *FontMap) UpdateResolution(w int, h int) {
fm.defaultFont.UpdateResolution(w, h)
fm.defaultBoldFont.UpdateResolution(w, h)
for _, f := range fm.ranges {
f.UpdateResolution(w, h)
}
}
func (fm *FontMap) findOverride(r rune) *glfont.Font {
override, ok := fm.runeMap[r]
if ok {
return override
}
for rr, f := range fm.ranges {
if r >= rr.start && r <= rr.end {
fm.runeMap[r] = f
return f
}
}
return nil
}
func (fm *FontMap) setOverrideRange(start rune, end rune, font *glfont.Font) {
fm.ranges[runeRange{start: start, end: end}] = font
}
func (fm *FontMap) GetFont(r rune) *glfont.Font {
if r <= 0xff {
return fm.defaultFont
}
if f := fm.findOverride(r); f != nil {
return f
}
func (fm *FontMap) DefaultFont() *glfont.Font {
return fm.defaultFont
}
func (fm *FontMap) GetBoldFont(r rune) *glfont.Font {
if r <= 0xff {
return fm.defaultBoldFont
}
if f := fm.findOverride(r); f != nil {
return f
}
func (fm *FontMap) BoldFont() *glfont.Font {
return fm.defaultBoldFont
}

View File

@ -205,6 +205,7 @@ func (gui *GUI) Render() error {
}()
startTime := time.Now()
showMessage := true
for !gui.window.ShouldClose() {
@ -223,58 +224,86 @@ func (gui *GUI) Render() error {
lines := gui.terminal.GetVisibleLines()
lineCount := int(gui.terminal.ActiveBuffer().ViewHeight())
colCount := int(gui.terminal.ActiveBuffer().ViewWidth())
cx := uint(gui.terminal.GetLogicalCursorX())
cy := uint(gui.terminal.GetLogicalCursorY()) + uint(gui.terminal.GetScrollOffset())
var colour *config.Colour
for y := 0; y < lineCount; y++ {
for x := 0; x < colCount; x++ {
if y < len(lines) {
cells := lines[y].Cells()
for x := 0; x < colCount; x++ {
cell := defaultCell
if y < len(lines) {
cells := lines[y].Cells()
if x < len(cells) {
cell = cells[x]
cursor := false
if gui.terminal.Modes().ShowCursor {
cursor = cx == uint(x) && cy == uint(y)
}
}
cursor := false
if gui.terminal.Modes().ShowCursor {
cx := uint(gui.terminal.GetLogicalCursorX())
cy := uint(gui.terminal.GetLogicalCursorY())
cy = cy + uint(gui.terminal.GetScrollOffset())
cursor = cx == uint(x) && cy == uint(y)
}
if gui.terminal.ActiveBuffer().InSelection(uint16(x), uint16(y)) {
colour = &gui.config.ColourScheme.Selection
} else {
colour = nil
}
var colour *config.Colour
cell := defaultCell
if colour != nil || cursor || x < len(cells) {
if x < len(cells) {
cell = cells[x]
if cell.Image() != nil {
gui.renderer.DrawCellImage(cell, uint(x), uint(y))
continue
}
}
gui.renderer.DrawCellBg(cell, uint(x), uint(y), cursor, colour, false)
}
if gui.terminal.ActiveBuffer().InSelection(uint16(x), uint16(y)) {
colour = &gui.config.ColourScheme.Selection
}
if cell.Image() != nil {
gui.renderer.DrawCellImage(cell, uint(x), uint(y))
} else {
gui.renderer.DrawCellBg(cell, uint(x), uint(y), cursor, colour, false)
}
}
}
for y := 0; y < lineCount; y++ {
for x := 0; x < colCount; x++ {
cell := defaultCell
hasText := false
if y < len(lines) {
if y < len(lines) {
cells := lines[y].Cells()
bufStr := ""
bold := false
dim := false
col := 0
colour := [3]float32{0, 0, 0}
cells := lines[y].Cells()
for x := 0; x < colCount; x++ {
if x < len(cells) {
cell = cells[x]
if cell.Rune() != 0 && cell.Rune() != 32 {
hasText = true
cell := cells[x]
if bufStr != "" && (cell.Attr().Dim != dim || cell.Attr().Bold != bold || colour != cell.Fg()) {
var alpha float32 = 1.0
if dim {
alpha = 0.5
}
gui.renderer.DrawCellText(bufStr, uint(col), uint(y), alpha, colour, bold)
col = x
bufStr = ""
}
dim = cell.Attr().Dim
colour = cell.Fg()
bold = cell.Attr().Bold
r := cell.Rune()
if r == 0 {
r = ' '
}
bufStr += string(r)
}
}
if hasText {
gui.renderer.DrawCellText(cell, uint(x), uint(y), 1.0, nil)
if bufStr != "" {
var alpha float32 = 1.0
if dim {
alpha = 0.5
}
gui.renderer.DrawCellText(bufStr, uint(col), uint(y), alpha, colour, bold)
}
}
}
gui.renderOverlay()
@ -295,22 +324,26 @@ Buffer Size: %d lines
)
}
if latestVersion != "" && time.Since(startTime) < time.Second*10 && gui.terminal.ActiveBuffer().RawLine() == 0 {
time.AfterFunc(time.Second, gui.terminal.SetDirty)
_, h := gui.terminal.GetSize()
var msg string
if version.Version == "" {
msg = "You are using a development build of Aminal."
if showMessage {
if latestVersion != "" && time.Since(startTime) < time.Second*10 && gui.terminal.ActiveBuffer().RawLine() == 0 {
time.AfterFunc(time.Second, gui.terminal.SetDirty)
_, h := gui.terminal.GetSize()
var msg string
if version.Version == "" {
msg = "You are using a development build of Aminal."
} else {
msg = fmt.Sprintf("Version %s of Aminal is now available.", strings.Replace(latestVersion, "v", "", -1))
}
gui.textbox(
2,
uint16(h-3),
fmt.Sprintf("%s (%d)", msg, 10-int(time.Since(startTime).Seconds())),
[3]float32{1, 1, 1},
[3]float32{0, 0.5, 0},
)
} else {
msg = fmt.Sprintf("Version %s of Aminal is now available.", strings.Replace(latestVersion, "v", "", -1))
showMessage = false
}
gui.textbox(
2,
uint16(h-3),
fmt.Sprintf("%s (%d)", msg, 10-int(time.Since(startTime).Seconds())),
[3]float32{1, 1, 1},
[3]float32{0, 0.5, 0},
)
}
gui.window.SwapBuffers()

View File

@ -161,7 +161,7 @@ func (r *OpenGLRenderer) SetArea(areaX int, areaY int, areaWidth int, areaHeight
r.areaHeight = areaHeight
r.areaX = areaX
r.areaY = areaY
f := r.fontMap.GetFont('X')
f := r.fontMap.DefaultFont()
_, r.cellHeight = f.MaxSize()
r.cellWidth, _ = f.Size("X")
//= f.LineHeight() // includes vertical padding
@ -200,8 +200,6 @@ func (r *OpenGLRenderer) DrawCellBg(cell buffer.Cell, col uint, row uint, cursor
if cursor {
bg = r.config.ColourScheme.Cursor
} else if cell.Attr().Reverse {
bg = cell.Fg()
} else {
bg = cell.Bg()
}
@ -215,32 +213,21 @@ func (r *OpenGLRenderer) DrawCellBg(cell buffer.Cell, col uint, row uint, cursor
}
func (r *OpenGLRenderer) DrawCellText(cell buffer.Cell, col uint, row uint, alpha float32, colour *[3]float32) {
func (r *OpenGLRenderer) DrawCellText(text string, col uint, row uint, alpha float32, colour [3]float32, bold bool) {
var fg [3]float32
if colour != nil {
fg = *colour
} else if cell.Attr().Reverse {
fg = cell.Bg()
var f *glfont.Font
if bold {
f = r.fontMap.BoldFont()
} else {
fg = cell.Fg()
f = r.fontMap.DefaultFont()
}
f := r.fontMap.GetFont(cell.Rune())
if cell.Attr().Bold {
f = r.fontMap.GetBoldFont(cell.Rune())
}
if cell.Attr().Dim {
alpha = 0.5 * alpha
}
f.SetColor(fg[0], fg[1], fg[2], alpha)
f.SetColor(colour[0], colour[1], colour[2], alpha)
x := float32(r.areaX) + float32(col)*r.cellWidth
y := float32(r.areaY) + (float32(row+1) * r.cellHeight) + f.MinY()
f.Print(x, y, string(cell.Rune()))
f.Print(x, y, text)
}
func (r *OpenGLRenderer) DrawCellImage(cell buffer.Cell, col uint, row uint) {

View File

@ -89,7 +89,7 @@ DONE:
x := float32(col) * gui.renderer.cellWidth
f := gui.fontMap.GetFont('X')
f := gui.fontMap.DefaultFont()
f.SetColor(fg[0], fg[1], fg[2], 1)
for i, line := range lines {

View File

@ -1,7 +1,6 @@
package terminal
import (
"context"
"time"
)
@ -27,21 +26,25 @@ var escapeSequenceMap = map[rune]escapeSequenceHandler{
func newLineSequenceHandler(pty chan rune, terminal *Terminal) error {
terminal.ActiveBuffer().NewLine()
terminal.isDirty = true
return nil
}
func tabSequenceHandler(pty chan rune, terminal *Terminal) error {
terminal.ActiveBuffer().Tab()
terminal.isDirty = true
return nil
}
func carriageReturnSequenceHandler(pty chan rune, terminal *Terminal) error {
terminal.ActiveBuffer().CarriageReturn()
terminal.isDirty = true
return nil
}
func backspaceSequenceHandler(pty chan rune, terminal *Terminal) error {
terminal.ActiveBuffer().Backspace()
terminal.isDirty = true
return nil
}
@ -65,47 +68,33 @@ func shiftInSequenceHandler(pty chan rune, terminal *Terminal) error {
return nil
}
func (terminal *Terminal) processInput(ctx context.Context, pty chan rune) {
func (terminal *Terminal) processInput(pty chan rune) {
// https://en.wikipedia.org/wiki/ANSI_escape_code
for {
var b rune
select {
case <-terminal.pauseChan:
// @todo alert user when terminal is suspended
terminal.logger.Debugf("Terminal suspended")
<-terminal.resumeChan
case <-ctx.Done():
break
default:
}
for {
if terminal.config.Slomo {
time.Sleep(time.Millisecond * 100)
}
b := <-pty
b = <-pty
terminal.logger.Debugf("0x%q", string(b))
handler, ok := escapeSequenceMap[b]
if ok {
//terminal.logger.Debugf("Handling escape sequence: 0x%x", b)
if err := handler(pty, terminal); err != nil {
terminal.logger.Errorf("Error handling escape sequence: %s", err)
}
} else {
//terminal.logger.Debugf("Received character 0x%X: %q", b, string(b))
if b >= 0x20 {
//terminal.logger.Debugf("%c", b)
terminal.ActiveBuffer().Write(b)
} else {
terminal.logger.Error("Non-readable rune received: 0x%X", b)
if b < 0x20 {
if handler, ok := escapeSequenceMap[b]; ok {
//terminal.logger.Debugf("Handling escape sequence: 0x%x", b)
if err := handler(pty, terminal); err != nil {
terminal.logger.Errorf("Error handling escape sequence: %s", err)
}
terminal.isDirty = true
continue
}
}
//terminal.logger.Debugf("Received character 0x%X: %q", b, string(b))
terminal.ActiveBuffer().Write(b)
terminal.isDirty = true
}
}

View File

@ -2,7 +2,6 @@ package terminal
import (
"bufio"
"context"
"fmt"
"io"
"os"
@ -35,7 +34,7 @@ const (
type Terminal struct {
program uint32
buffers []*buffer.Buffer
activeBufferIndex uint8
activeBuffer *buffer.Buffer
lock sync.Mutex
pty *os.File
logger *zap.SugaredLogger
@ -43,8 +42,6 @@ type Terminal struct {
size Winsize
config *config.Config
titleHandlers []chan bool
pauseChan chan bool
resumeChan chan bool
modes Modes
mouseMode MouseMode
bracketedPasteMode bool
@ -87,13 +84,11 @@ func New(pty *os.File, logger *zap.SugaredLogger, config *config.Config) *Termin
logger: logger,
config: config,
titleHandlers: []chan bool{},
pauseChan: make(chan bool, 1),
resumeChan: make(chan bool, 1),
modes: Modes{
ShowCursor: true,
},
}
t.activeBuffer = t.buffers[0]
return t
}
@ -129,32 +124,30 @@ func (terminal *Terminal) GetMouseMode() MouseMode {
}
func (terminal *Terminal) UseMainBuffer() {
terminal.activeBufferIndex = MainBuffer
terminal.activeBuffer = terminal.buffers[MainBuffer]
terminal.SetSize(uint(terminal.size.Width), uint(terminal.size.Height))
}
func (terminal *Terminal) UseAltBuffer() {
terminal.activeBufferIndex = AltBuffer
terminal.activeBuffer = terminal.buffers[AltBuffer]
terminal.SetSize(uint(terminal.size.Width), uint(terminal.size.Height))
}
func (terminal *Terminal) UseInternalBuffer() {
terminal.pauseChan <- true
terminal.activeBufferIndex = InternalBuffer
terminal.activeBuffer = terminal.buffers[InternalBuffer]
terminal.SetSize(uint(terminal.size.Width), uint(terminal.size.Height))
}
func (terminal *Terminal) ExitInternalBuffer() {
terminal.activeBufferIndex = terminal.lastBuffer
terminal.resumeChan <- true
terminal.activeBuffer = terminal.buffers[terminal.lastBuffer]
}
func (terminal *Terminal) ActiveBuffer() *buffer.Buffer {
return terminal.buffers[terminal.activeBufferIndex]
return terminal.activeBuffer
}
func (terminal *Terminal) UsingMainBuffer() bool {
return terminal.activeBufferIndex == MainBuffer
return terminal.activeBuffer == terminal.buffers[MainBuffer]
}
func (terminal *Terminal) GetScrollOffset() uint {
@ -255,20 +248,17 @@ func (terminal *Terminal) Read() error {
buffer := make(chan rune, 0xffff)
reader := bufio.NewReader(terminal.pty)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go terminal.processInput(ctx, buffer)
go terminal.processInput(buffer)
for {
r, size, err := reader.ReadRune()
r, _, err := reader.ReadRune()
if err != nil {
if err == io.EOF {
break
}
return err
} else if size > 0 {
buffer <- r
}
buffer <- r
}
//clean exit