mirror of https://github.com/liamg/aminal.git
efficiency, introduced resize bug though :/
This commit is contained in:
parent
bb38d22a60
commit
88528fc6b0
|
@ -20,7 +20,7 @@ Ensure you have your latest graphics card drivers installed before use.
|
|||
|
||||
## What isn't supported?
|
||||
|
||||
- Suspend/Continue (^S, ^Q). This is archaic bullshit that annoys more people than it helps. Basically:
|
||||
- Suspend/Continue (\^S, \^Q). This is archaic bullshit that annoys more people than it helps. Basically:q
|
||||
|
||||
<span style="display:block;text-align:center">
|
||||

|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
package buffer
|
||||
|
||||
type Cell struct {
|
||||
r rune
|
||||
attr CellAttributes
|
||||
hasContent bool
|
||||
r rune
|
||||
attr CellAttributes
|
||||
}
|
||||
|
||||
type CellAttributes struct {
|
||||
|
@ -43,5 +42,12 @@ func (cell *Cell) erase() {
|
|||
|
||||
func (cell *Cell) setRune(r rune) {
|
||||
cell.r = r
|
||||
cell.hasContent = r > 0
|
||||
}
|
||||
|
||||
func NewBackgroundCell(colour [3]float32) Cell {
|
||||
return Cell{
|
||||
attr: CellAttributes{
|
||||
BgColour: colour,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
package buffer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSetRune(t *testing.T) {
|
||||
cell := newCell()
|
||||
assert.False(t, cell.hasContent)
|
||||
cell.setRune('X')
|
||||
assert.True(t, cell.hasContent)
|
||||
assert.Equal(t, 'X', cell.r)
|
||||
cell.setRune('Y')
|
||||
assert.True(t, cell.hasContent)
|
||||
assert.Equal(t, 'Y', cell.r)
|
||||
}
|
150
gui/gui.go
150
gui/gui.go
|
@ -9,34 +9,37 @@ import (
|
|||
|
||||
"github.com/go-gl/gl/all-core/gl"
|
||||
"github.com/go-gl/glfw/v3.2/glfw"
|
||||
"gitlab.com/liamg/raft/buffer"
|
||||
"gitlab.com/liamg/raft/config"
|
||||
"gitlab.com/liamg/raft/terminal"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type GUI struct {
|
||||
window *glfw.Window
|
||||
logger *zap.SugaredLogger
|
||||
config *config.Config
|
||||
terminal *terminal.Terminal
|
||||
width int //window width in pixels
|
||||
height int //window height in pixels
|
||||
font *glfont.Font
|
||||
fontScale int32
|
||||
renderer Renderer
|
||||
colourAttr uint32
|
||||
window *glfw.Window
|
||||
logger *zap.SugaredLogger
|
||||
config *config.Config
|
||||
terminal *terminal.Terminal
|
||||
width int //window width in pixels
|
||||
height int //window height in pixels
|
||||
font *glfont.Font
|
||||
fontScale int32
|
||||
renderer Renderer
|
||||
colourAttr uint32
|
||||
renderState *RenderState
|
||||
}
|
||||
|
||||
func New(config *config.Config, terminal *terminal.Terminal, logger *zap.SugaredLogger) *GUI {
|
||||
|
||||
//logger.
|
||||
return &GUI{
|
||||
config: config,
|
||||
logger: logger,
|
||||
width: 600,
|
||||
height: 300,
|
||||
terminal: terminal,
|
||||
fontScale: 15.0,
|
||||
config: config,
|
||||
logger: logger,
|
||||
width: 600,
|
||||
height: 300,
|
||||
terminal: terminal,
|
||||
fontScale: 15.0,
|
||||
renderState: NewRenderState(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,25 +48,39 @@ func New(config *config.Config, terminal *terminal.Terminal, logger *zap.Sugared
|
|||
// can only be called on OS thread
|
||||
func (gui *GUI) resize(w *glfw.Window, width int, height int) {
|
||||
|
||||
gui.logger.Debugf("GUI resize to %dx%d", width, height)
|
||||
if width == gui.width && height == gui.height {
|
||||
//return
|
||||
}
|
||||
|
||||
gui.logger.Debugf("Initiating GUI resize to %dx%d", width, height)
|
||||
|
||||
gui.width = width
|
||||
gui.height = height
|
||||
|
||||
gui.logger.Debugf("Updating font resolution...")
|
||||
if gui.font != nil {
|
||||
gui.font.UpdateResolution((width), (height))
|
||||
}
|
||||
|
||||
gl.Viewport(0, 0, int32(gui.width), int32(gui.height))
|
||||
|
||||
gui.logger.Debugf("Setting renderer area...")
|
||||
gui.renderer.SetArea(0, 0, gui.width, gui.height)
|
||||
|
||||
gui.logger.Debugf("Calculating size in cols/rows...")
|
||||
cols, rows := gui.renderer.GetTermSize()
|
||||
|
||||
gui.logger.Debugf("Resizing internal terminal...")
|
||||
if err := gui.terminal.SetSize(cols, rows); err != nil {
|
||||
gui.logger.Errorf("Failed to resize terminal to %d cols, %d rows: %s", cols, rows, err)
|
||||
}
|
||||
|
||||
gui.logger.Debugf("Resetting render state...")
|
||||
gui.renderState.Reset()
|
||||
|
||||
gui.logger.Debugf("Setting viewport size...")
|
||||
gl.Viewport(0, 0, int32(gui.width), int32(gui.height))
|
||||
|
||||
gui.logger.Debugf("Resize complete!")
|
||||
|
||||
}
|
||||
|
||||
func (gui *GUI) glfwScrollCallback(w *glfw.Window, xoff float64, yoff float64) {
|
||||
|
@ -93,7 +110,7 @@ func (gui *GUI) Render() error {
|
|||
|
||||
gui.logger.Debugf("Creating window...")
|
||||
var err error
|
||||
gui.window, err = gui.createWindow(gui.width, gui.height)
|
||||
gui.window, err = gui.createWindow(500, 300)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to create window: %s", err)
|
||||
}
|
||||
|
@ -172,37 +189,79 @@ func (gui *GUI) Render() error {
|
|||
gui.terminal.AttachTitleChangeHandler(titleChan)
|
||||
gui.terminal.AttachDisplayChangeHandler(changeChan)
|
||||
|
||||
frames := 0
|
||||
frameCount := 0
|
||||
fps := 0
|
||||
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
|
||||
|
||||
ticker := time.NewTicker(time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
dirty := true
|
||||
defaultCell := buffer.NewBackgroundCell(gui.config.ColourScheme.Background)
|
||||
|
||||
var lastCursorX uint
|
||||
var lastCursorY uint
|
||||
|
||||
for !gui.window.ShouldClose() {
|
||||
|
||||
select {
|
||||
|
||||
case <-changeChan:
|
||||
frames = 2
|
||||
case <-ticker.C:
|
||||
gui.logger.Sync()
|
||||
case <-changeChan:
|
||||
dirty = true
|
||||
case <-titleChan:
|
||||
gui.window.SetTitle(gui.terminal.GetTitle())
|
||||
case <-ticker.C:
|
||||
fps = frameCount
|
||||
frameCount = 0
|
||||
default:
|
||||
}
|
||||
|
||||
gl.UseProgram(program)
|
||||
|
||||
if gui.config.Rendering.AlwaysRepaint || frames > 0 {
|
||||
if dirty {
|
||||
gui.window.SwapBuffers()
|
||||
dirty = false
|
||||
}
|
||||
|
||||
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
|
||||
if gui.terminal.CheckDirty() {
|
||||
|
||||
if gui.terminal.Modes().ShowCursor {
|
||||
cx := uint(gui.terminal.GetLogicalCursorX())
|
||||
cy := uint(gui.terminal.GetLogicalCursorY())
|
||||
cy = cy + uint(gui.terminal.GetScrollOffset())
|
||||
|
||||
if lastCursorX != cx || lastCursorY != cy {
|
||||
gui.renderState.SetDirty(lastCursorX, lastCursorY)
|
||||
dirty = true
|
||||
}
|
||||
} else {
|
||||
gui.renderState.SetDirty(lastCursorX, lastCursorY)
|
||||
dirty = true
|
||||
}
|
||||
|
||||
lines := gui.terminal.GetVisibleLines()
|
||||
for y := 0; y < len(lines); y++ {
|
||||
for x, cell := range lines[y].Cells() {
|
||||
gui.renderer.DrawCell(cell, uint(x), uint(y))
|
||||
lineCount := gui.terminal.ActiveBuffer().ViewHeight()
|
||||
colCount := gui.terminal.ActiveBuffer().ViewWidth()
|
||||
for y := 0; y < int(lineCount); y++ {
|
||||
|
||||
for x := 0; x < int(colCount); x++ {
|
||||
|
||||
cell := defaultCell
|
||||
empty := true
|
||||
|
||||
if y < len(lines) {
|
||||
cells := lines[y].Cells()
|
||||
if x < len(cells) {
|
||||
cell = cells[x]
|
||||
if cell.Rune() == 0 {
|
||||
cell = defaultCell
|
||||
}
|
||||
empty = false
|
||||
}
|
||||
}
|
||||
|
||||
if gui.renderState.RequiresRender(uint(x), uint(y), cell.Bg(), cell.Fg(), cell.Rune(), empty) {
|
||||
gui.renderer.DrawCell(cell, uint(x), uint(y))
|
||||
dirty = true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,26 +269,19 @@ func (gui *GUI) Render() error {
|
|||
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)
|
||||
|
||||
if lastCursorX != cx || lastCursorY != cy {
|
||||
gui.renderer.DrawCursor(cx, cy, gui.config.ColourScheme.Cursor)
|
||||
gui.renderState.SetDirty(lastCursorX, lastCursorY)
|
||||
lastCursorX = cx
|
||||
lastCursorY = cy
|
||||
dirty = true
|
||||
}
|
||||
}
|
||||
|
||||
_ = fps
|
||||
/*
|
||||
gui.font.SetColor(1, 0.5, 0.5, 0.5)
|
||||
fpsData := ""
|
||||
if gui.config.Rendering.AlwaysRepaint {
|
||||
fpsData = fmt.Sprintf("%d FPS | %d,%d", fps, gui.terminal.GetLogicalCursorX(), gui.terminal.GetLogicalCursorY())
|
||||
}
|
||||
gui.font.Print(10, float32(gui.height-20), 1.5, fmt.Sprintf("%s", fpsData))
|
||||
*/
|
||||
}
|
||||
|
||||
if gui.config.Rendering.AlwaysRepaint || frames > 0 {
|
||||
gui.window.SwapBuffers()
|
||||
frameCount++
|
||||
frames--
|
||||
}
|
||||
|
||||
//glfw.PollEvents()
|
||||
glfw.WaitEventsTimeout(0.02) // up to 50fps on no input, otherwise higher
|
||||
}
|
||||
|
||||
|
|
103
gui/renderer.go
103
gui/renderer.go
|
@ -43,6 +43,7 @@ type rectangle struct {
|
|||
colourAttr uint32
|
||||
colour [3]float32
|
||||
points []float32
|
||||
prog uint32
|
||||
}
|
||||
|
||||
func (r *OpenGLRenderer) newRectangle(x float32, y float32, colourAttr uint32) *rectangle {
|
||||
|
@ -53,7 +54,6 @@ func (r *OpenGLRenderer) newRectangle(x float32, y float32, colourAttr uint32) *
|
|||
h := r.cellHeight / float32(r.areaHeight/2)
|
||||
|
||||
rect := &rectangle{
|
||||
colour: [3]float32{0, 0, 1},
|
||||
points: []float32{
|
||||
x, y, 0,
|
||||
x, y + h, 0,
|
||||
|
@ -64,24 +64,8 @@ func (r *OpenGLRenderer) newRectangle(x float32, y float32, colourAttr uint32) *
|
|||
x + w, y + h, 0,
|
||||
},
|
||||
colourAttr: colourAttr,
|
||||
prog: r.program,
|
||||
}
|
||||
|
||||
rect.gen()
|
||||
|
||||
return rect
|
||||
}
|
||||
|
||||
func (rect *rectangle) gen() {
|
||||
|
||||
colour := []float32{
|
||||
rect.colour[0], rect.colour[1], rect.colour[2],
|
||||
rect.colour[0], rect.colour[1], rect.colour[2],
|
||||
rect.colour[0], rect.colour[1], rect.colour[2],
|
||||
rect.colour[0], rect.colour[1], rect.colour[2],
|
||||
rect.colour[0], rect.colour[1], rect.colour[2],
|
||||
rect.colour[0], rect.colour[1], rect.colour[2],
|
||||
}
|
||||
|
||||
// SHAPE
|
||||
gl.GenBuffers(1, &rect.vbo)
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, rect.vbo)
|
||||
|
@ -96,22 +80,37 @@ func (rect *rectangle) gen() {
|
|||
|
||||
// colour
|
||||
gl.GenBuffers(1, &rect.cv)
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, rect.cv)
|
||||
gl.BufferData(gl.ARRAY_BUFFER, len(colour)*4, gl.Ptr(colour), gl.STATIC_DRAW)
|
||||
gl.EnableVertexAttribArray(rect.colourAttr)
|
||||
gl.VertexAttribPointer(rect.colourAttr, 3, gl.FLOAT, false, 0, gl.PtrOffset(0))
|
||||
|
||||
rect.setColour([3]float32{0, 1, 0})
|
||||
|
||||
return rect
|
||||
}
|
||||
|
||||
func (rect *rectangle) setColour(colour [3]float32) {
|
||||
if rect.colour == colour {
|
||||
return
|
||||
}
|
||||
rect.Free()
|
||||
|
||||
c := []float32{
|
||||
colour[0], colour[1], colour[2],
|
||||
colour[0], colour[1], colour[2],
|
||||
colour[0], colour[1], colour[2],
|
||||
colour[0], colour[1], colour[2],
|
||||
colour[0], colour[1], colour[2],
|
||||
colour[0], colour[1], colour[2],
|
||||
}
|
||||
|
||||
gl.UseProgram(rect.prog)
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, rect.cv)
|
||||
gl.BufferData(gl.ARRAY_BUFFER, len(c)*4, gl.Ptr(c), gl.STATIC_DRAW)
|
||||
gl.EnableVertexAttribArray(rect.colourAttr)
|
||||
gl.VertexAttribPointer(rect.colourAttr, 3, gl.FLOAT, false, 0, gl.PtrOffset(0))
|
||||
|
||||
rect.colour = colour
|
||||
rect.gen()
|
||||
}
|
||||
|
||||
func (rect *rectangle) Free() {
|
||||
gl.UseProgram(rect.prog)
|
||||
gl.DeleteVertexArrays(1, &rect.vao)
|
||||
gl.DeleteBuffers(1, &rect.vbo)
|
||||
gl.DeleteBuffers(1, &rect.cv)
|
||||
|
@ -159,7 +158,6 @@ func (r *OpenGLRenderer) SetFont(font *glfont.Font) { // @todo check for monospa
|
|||
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() {
|
||||
|
@ -175,22 +173,27 @@ func (r *OpenGLRenderer) calculatePositions() {
|
|||
}
|
||||
}
|
||||
|
||||
func (r *OpenGLRenderer) generateRectangles() {
|
||||
gl.UseProgram(r.program)
|
||||
for line := uint(0); line < r.termRows; line++ {
|
||||
for col := uint(0); col < r.termCols; col++ {
|
||||
|
||||
rect, ok := r.rectangles[[2]uint{col, line}]
|
||||
if ok {
|
||||
rect.Free()
|
||||
}
|
||||
|
||||
// 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]uint{col, line}] = r.newRectangle(x, y, r.colourAttr)
|
||||
}
|
||||
func (r *OpenGLRenderer) getRectangle(col uint, row uint) *rectangle {
|
||||
if rect, ok := r.rectangles[[2]uint{col, row}]; ok {
|
||||
return rect
|
||||
}
|
||||
return r.generateRectangle(col, row)
|
||||
}
|
||||
|
||||
func (r *OpenGLRenderer) generateRectangle(col uint, line uint) *rectangle {
|
||||
|
||||
gl.UseProgram(r.program)
|
||||
|
||||
rect, ok := r.rectangles[[2]uint{col, line}]
|
||||
if ok {
|
||||
rect.Free()
|
||||
}
|
||||
|
||||
// rounding to whole pixels makes everything nice
|
||||
x := float32(float32(col) * r.cellWidth)
|
||||
y := float32((float32(line) * r.cellHeight) + (r.cellHeight))
|
||||
r.rectangles[[2]uint{col, line}] = r.newRectangle(x, y, r.colourAttr)
|
||||
return r.rectangles[[2]uint{col, line}]
|
||||
}
|
||||
|
||||
func (r *OpenGLRenderer) DrawCursor(col uint, row uint, colour config.Colour) {
|
||||
|
@ -220,10 +223,6 @@ func (r *OpenGLRenderer) DrawCursor(col uint, row uint, colour config.Colour) {
|
|||
|
||||
func (r *OpenGLRenderer) DrawCell(cell buffer.Cell, col uint, row uint) {
|
||||
|
||||
if cell.Attr().Hidden || (cell.Rune() == 0x00) {
|
||||
return
|
||||
}
|
||||
|
||||
var fg [3]float32
|
||||
var bg [3]float32
|
||||
|
||||
|
@ -241,18 +240,10 @@ func (r *OpenGLRenderer) DrawCell(cell buffer.Cell, col uint, row uint) {
|
|||
}
|
||||
|
||||
gl.UseProgram(r.program)
|
||||
|
||||
// don't bother rendering rectangles that are the same colour as the background
|
||||
if bg != r.config.ColourScheme.Background {
|
||||
|
||||
rect, ok := r.rectangles[[2]uint{col, row}]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Missing rectangle data for cell at %d,%d", col, row))
|
||||
}
|
||||
rect.setColour(bg)
|
||||
gl.BindVertexArray(rect.vao)
|
||||
gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 6)
|
||||
}
|
||||
rect := r.getRectangle(col, row)
|
||||
rect.setColour(bg)
|
||||
gl.BindVertexArray(rect.vao)
|
||||
gl.DrawArrays(gl.TRIANGLES, 0, 6)
|
||||
|
||||
var alpha float32 = 1
|
||||
if cell.Attr().Dim {
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
package gui
|
||||
|
||||
type RenderState struct {
|
||||
cells map[[2]uint]RenderedCell
|
||||
}
|
||||
|
||||
func NewRenderState() *RenderState {
|
||||
return &RenderState{
|
||||
cells: map[[2]uint]RenderedCell{},
|
||||
}
|
||||
}
|
||||
|
||||
type RenderedCell struct {
|
||||
bg [3]float32
|
||||
fg [3]float32
|
||||
contents rune
|
||||
dirty bool
|
||||
}
|
||||
|
||||
func (rs *RenderState) Reset() {
|
||||
rs.cells = map[[2]uint]RenderedCell{}
|
||||
}
|
||||
|
||||
func (rs *RenderState) SetDirty(x uint, y uint) {
|
||||
rs.cells[[2]uint{x, y}] = RenderedCell{
|
||||
bg: [3]float32{0, 0, 0},
|
||||
fg: [3]float32{0, 0, 0},
|
||||
contents: 0,
|
||||
dirty: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (rs *RenderState) RequiresRender(x uint, y uint, bg [3]float32, fg [3]float32, contents rune, empty bool) bool {
|
||||
|
||||
state, found := rs.cells[[2]uint{x, y}]
|
||||
if !found {
|
||||
if empty {
|
||||
//return false
|
||||
}
|
||||
rs.cells[[2]uint{x, y}] = RenderedCell{
|
||||
bg: bg,
|
||||
fg: fg,
|
||||
contents: contents,
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if state.bg != bg || state.fg != fg || state.contents != contents || state.dirty {
|
||||
rs.cells[[2]uint{x, y}] = RenderedCell{
|
||||
bg: bg,
|
||||
fg: fg,
|
||||
contents: contents,
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
102
terminal/ansi.go
102
terminal/ansi.go
|
@ -1,18 +1,36 @@
|
|||
package terminal
|
||||
|
||||
import "fmt"
|
||||
|
||||
// https://www.xfree86.org/4.8.0/ctlseqs.html
|
||||
// https://vt100.net/docs/vt100-ug/chapter3.html
|
||||
|
||||
var ansiSequenceMap = map[rune]escapeSequenceHandler{
|
||||
'[': csiHandler,
|
||||
0x5d: oscHandler,
|
||||
'7': saveCursorHandler,
|
||||
'8': restoreCursorHandler,
|
||||
'D': indexHandler,
|
||||
'M': reverseIndexHandler,
|
||||
'[': csiHandler,
|
||||
']': oscHandler,
|
||||
'7': saveCursorHandler,
|
||||
'8': restoreCursorHandler,
|
||||
'D': indexHandler,
|
||||
'M': reverseIndexHandler,
|
||||
'c': swallowHandler(0), //RIS
|
||||
'(': swallowHandler(1), // character set bullshit
|
||||
')': swallowHandler(1), // character set bullshit
|
||||
'*': swallowHandler(1), // character set bullshit
|
||||
'+': swallowHandler(1), // character set bullshit
|
||||
'>': swallowHandler(0), // numeric char selection //@todo
|
||||
'=': swallowHandler(0), // alt char selection //@todo
|
||||
}
|
||||
|
||||
func indexHandler(buffer chan rune, terminal *Terminal) error {
|
||||
func swallowHandler(n int) func(pty chan rune, terminal *Terminal) error {
|
||||
return func(pty chan rune, terminal *Terminal) error {
|
||||
for i := 0; i < n; i++ {
|
||||
<-pty
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func indexHandler(pty 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.ActiveBuffer().CursorLine() == terminal.ActiveBuffer().ViewHeight()-1 {
|
||||
|
@ -23,17 +41,17 @@ func indexHandler(buffer chan rune, terminal *Terminal) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func reverseIndexHandler(buffer chan rune, terminal *Terminal) error {
|
||||
func reverseIndexHandler(pty chan rune, terminal *Terminal) error {
|
||||
terminal.ActiveBuffer().MovePosition(0, -1)
|
||||
return nil
|
||||
}
|
||||
|
||||
func saveCursorHandler(buffer chan rune, terminal *Terminal) error {
|
||||
func saveCursorHandler(pty chan rune, terminal *Terminal) error {
|
||||
terminal.ActiveBuffer().SaveCursor()
|
||||
return nil
|
||||
}
|
||||
|
||||
func restoreCursorHandler(buffer chan rune, terminal *Terminal) error {
|
||||
func restoreCursorHandler(pty chan rune, terminal *Terminal) error {
|
||||
terminal.ActiveBuffer().RestoreCursor()
|
||||
return nil
|
||||
}
|
||||
|
@ -47,67 +65,5 @@ func ansiHandler(pty chan rune, terminal *Terminal) error {
|
|||
return handler(pty, terminal)
|
||||
}
|
||||
|
||||
switch b {
|
||||
|
||||
case 'c':
|
||||
terminal.logger.Errorf("RIS not yet supported")
|
||||
case '(':
|
||||
b = <-pty
|
||||
switch b {
|
||||
case 'A': //uk @todo handle these?
|
||||
//terminal.charSet = C0
|
||||
case 'B': //us
|
||||
//terminal.charSet = C0
|
||||
}
|
||||
case ')':
|
||||
b = <-pty
|
||||
switch b {
|
||||
case 'A': //uk @todo handle these?
|
||||
//terminal.charSet = C1
|
||||
case 'B': //us
|
||||
//terminal.charSet = C1
|
||||
}
|
||||
case '*':
|
||||
b = <-pty
|
||||
switch b {
|
||||
case 'A': //uk @todo handle these?
|
||||
//terminal.charSet = C2
|
||||
case 'B': //us
|
||||
//terminal.charSet = C2
|
||||
}
|
||||
case '+':
|
||||
b = <-pty
|
||||
switch b {
|
||||
case 'A': //uk @todo handle these?
|
||||
//terminal.charSet = C3
|
||||
case 'B': //us
|
||||
//terminal.charSet = C3
|
||||
}
|
||||
case '>':
|
||||
// numeric char selection @todo
|
||||
case '=':
|
||||
//alternate char selection @todo
|
||||
case '?':
|
||||
pm := ""
|
||||
for {
|
||||
b = <-pty
|
||||
switch b {
|
||||
case 'h':
|
||||
switch pm {
|
||||
default:
|
||||
terminal.logger.Errorf("Unknown private code ESC?%sh", pm)
|
||||
}
|
||||
case 'l':
|
||||
switch pm {
|
||||
default:
|
||||
terminal.logger.Errorf("Unknown private code ESC?%sl", pm)
|
||||
}
|
||||
default:
|
||||
pm += string(b)
|
||||
}
|
||||
}
|
||||
default:
|
||||
terminal.logger.Errorf("Unknown control sequence: 0x%02X [%s]", b, string(b))
|
||||
}
|
||||
return nil
|
||||
return fmt.Errorf("Unknown ANSI control sequence byte: 0x%02X [%v]", b, string(b))
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ func csiSetMode(modeStr string, enabled bool, terminal *Terminal) error {
|
|||
} else {
|
||||
terminal.UseMainBuffer()
|
||||
}
|
||||
case "?1000", "?10061000": // ?10061000 seen from htop
|
||||
case "?1000", "?1006;1000", "?10061000": // ?10061000 seen from htop
|
||||
// enable mouse tracking
|
||||
if enabled {
|
||||
terminal.SetMouseMode(MouseModeVT200)
|
||||
|
@ -153,16 +153,16 @@ func csiWindowManipulation(params []string, intermediate string, terminal *Termi
|
|||
}
|
||||
|
||||
func csiLinePositionAbsolute(params []string, intermediate string, terminal *Terminal) error {
|
||||
col := 1
|
||||
row := 1
|
||||
if len(params) > 0 {
|
||||
var err error
|
||||
col, err = strconv.Atoi(params[0])
|
||||
row, err = strconv.Atoi(params[0])
|
||||
if err != nil {
|
||||
col = 1
|
||||
row = 1
|
||||
}
|
||||
}
|
||||
|
||||
terminal.ActiveBuffer().SetPosition(uint16(col), terminal.ActiveBuffer().CursorLine())
|
||||
terminal.ActiveBuffer().SetPosition(terminal.ActiveBuffer().CursorColumn(), uint16(row-1))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -12,9 +12,59 @@ type TerminalCharSet int
|
|||
type escapeSequenceHandler func(pty chan rune, terminal *Terminal) error
|
||||
|
||||
var escapeSequenceMap = map[rune]escapeSequenceHandler{
|
||||
0x05: enqSequenceHandler,
|
||||
0x07: bellSequenceHandler,
|
||||
0x08: backspaceSequenceHandler,
|
||||
0x09: tabSequenceHandler,
|
||||
0x0a: newLineSequenceHandler,
|
||||
0x0b: newLineSequenceHandler,
|
||||
0x0c: newLineSequenceHandler,
|
||||
0x0d: carriageReturnSequenceHandler,
|
||||
0x0e: shiftOutSequenceHandler,
|
||||
0x0f: shiftInSequenceHandler,
|
||||
0x1b: ansiHandler,
|
||||
}
|
||||
|
||||
func newLineSequenceHandler(pty chan rune, terminal *Terminal) error {
|
||||
terminal.ActiveBuffer().NewLine()
|
||||
return nil
|
||||
}
|
||||
|
||||
func carriageReturnSequenceHandler(pty chan rune, terminal *Terminal) error {
|
||||
terminal.ActiveBuffer().CarriageReturn()
|
||||
return nil
|
||||
}
|
||||
|
||||
func backspaceSequenceHandler(pty chan rune, terminal *Terminal) error {
|
||||
terminal.ActiveBuffer().Backspace()
|
||||
return nil
|
||||
}
|
||||
|
||||
func bellSequenceHandler(pty chan rune, terminal *Terminal) error {
|
||||
// @todo ring bell - flash red or some shit?
|
||||
return nil
|
||||
}
|
||||
|
||||
func enqSequenceHandler(pty chan rune, terminal *Terminal) error {
|
||||
terminal.logger.Errorf("Received ENQ!")
|
||||
return nil
|
||||
}
|
||||
|
||||
func shiftOutSequenceHandler(pty chan rune, terminal *Terminal) error {
|
||||
terminal.logger.Errorf("Received shift out")
|
||||
return nil
|
||||
}
|
||||
|
||||
func shiftInSequenceHandler(pty chan rune, terminal *Terminal) error {
|
||||
terminal.logger.Errorf("Received shift in")
|
||||
return nil
|
||||
}
|
||||
|
||||
func tabSequenceHandler(pty chan rune, terminal *Terminal) error {
|
||||
terminal.logger.Errorf("Received tab")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (terminal *Terminal) processInput(ctx context.Context, pty chan rune) {
|
||||
|
||||
// https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
|
@ -43,28 +93,8 @@ func (terminal *Terminal) processInput(ctx context.Context, pty chan rune) {
|
|||
if err := handler(pty, terminal); err != nil {
|
||||
terminal.logger.Errorf("Error handling escape sequence: %s", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
terminal.logger.Debugf("Received character 0x%X: %q", b, string(b))
|
||||
|
||||
switch b {
|
||||
case 0x0a, 0x0c, 0x0b: // LF, FF, VT
|
||||
terminal.ActiveBuffer().NewLine()
|
||||
case 0x0d: // CR
|
||||
terminal.ActiveBuffer().CarriageReturn()
|
||||
case 0x08: // BS
|
||||
// backspace
|
||||
terminal.ActiveBuffer().Backspace()
|
||||
case 0x07: // BEL
|
||||
// @todo ring bell - flash red or some shit?
|
||||
case 0x05: // ENQ
|
||||
terminal.logger.Errorf("Received ENQ!")
|
||||
case 0xe, 0xf:
|
||||
terminal.logger.Errorf("Received SI/SO")
|
||||
case 0x09:
|
||||
terminal.logger.Errorf("Received TAB")
|
||||
default:
|
||||
} else {
|
||||
terminal.logger.Debugf("Received character 0x%X: %q", b, string(b))
|
||||
if b >= 0x20 {
|
||||
terminal.ActiveBuffer().Write(b)
|
||||
} else {
|
||||
|
@ -72,5 +102,6 @@ func (terminal *Terminal) processInput(ctx context.Context, pty chan rune) {
|
|||
}
|
||||
}
|
||||
|
||||
terminal.isDirty = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ type Terminal struct {
|
|||
resumeChan chan bool
|
||||
modes Modes
|
||||
mouseMode MouseMode
|
||||
isDirty bool
|
||||
}
|
||||
|
||||
type Modes struct {
|
||||
|
@ -90,6 +91,12 @@ func New(pty *os.File, logger *zap.SugaredLogger, config *config.Config) *Termin
|
|||
}
|
||||
}
|
||||
|
||||
func (terminal *Terminal) CheckDirty() bool {
|
||||
d := terminal.isDirty
|
||||
terminal.isDirty = false
|
||||
return d
|
||||
}
|
||||
|
||||
func (terminal *Terminal) IsApplicationCursorKeysModeEnabled() bool {
|
||||
return terminal.modes.ApplicationCursorKeys
|
||||
}
|
||||
|
|
|
@ -219,7 +219,7 @@ func (f *Font) Height(scale float32, text string) float32 {
|
|||
baseHeight = height
|
||||
height = 0
|
||||
}
|
||||
|
||||
|
||||
//skip runes that are not in font chacter range
|
||||
if int(runeIndex)-int(lowChar) > len(f.fontChar) || runeIndex < lowChar {
|
||||
continue
|
||||
|
@ -229,7 +229,7 @@ func (f *Font) Height(scale float32, text string) float32 {
|
|||
ch := f.fontChar[runeIndex-lowChar]
|
||||
|
||||
// Now advance cursors for next glyph (note that advance is number of 1/64 pixels)
|
||||
if float32(ch.height) * scale > height {
|
||||
if float32(ch.height)*scale > height {
|
||||
height = float32(ch.height) * scale
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue