Compare commits

...

3 Commits

Author SHA1 Message Date
976682d2d2 Don't show text in the cmd entry area 2022-09-24 07:15:16 +04:00
b6c468a23b Rendering from an in-mem glyph grid 2022-09-24 07:05:21 +04:00
b5797c69f9 Rename p *nterm to nt *nterm 2022-09-24 05:09:39 +04:00
4 changed files with 323 additions and 176 deletions

View File

@ -127,7 +127,7 @@ const (
AnsiCodeOptions_LineAbs
AnsiCodeOptions_ScrollOffset
// This is at the bottom so iota starts at 0
// This is at the bottom so the above iota starts at 0
AnsiCodeOptions_Unknown AnsiCodeOptions = 0
)

138
glyph_grid.go Executable file
View File

@ -0,0 +1,138 @@
package main
import (
"fmt"
"unicode/utf8"
"github.com/bloeys/gglm/gglm"
)
type GridTile struct {
Glyph rune
FgColor gglm.Vec4
BgColor gglm.Vec4
}
type GlyphGrid struct {
CursorX uint
CursorY uint
SizeX uint
SizeY uint
Tiles [][]GridTile
}
func (gg *GlyphGrid) Write(rs []rune, fgColor *gglm.Vec4, bgColor *gglm.Vec4) {
for i := 0; i < len(rs); i++ {
r := rs[i]
gg.Tiles[gg.CursorY][gg.CursorX] = GridTile{
Glyph: r,
FgColor: *fgColor,
BgColor: *bgColor,
}
if !gg.TickCursor(r == '\n') {
break
}
}
}
func (gg *GlyphGrid) ClearRow(rowIndex uint) {
if rowIndex >= gg.SizeY {
panic(fmt.Sprintf("passed row index of %d is larger or equal than grid Y size of %d\n", rowIndex, gg.SizeY))
}
row := gg.Tiles[rowIndex]
for x := 0; x < len(row); x++ {
row[x].Glyph = utf8.RuneError
}
}
func (gg *GlyphGrid) ClearAll() {
for y := 0; y < len(gg.Tiles); y++ {
row := gg.Tiles[y]
for x := 0; x < len(row); x++ {
row[x].Glyph = utf8.RuneError
}
}
}
func (gg *GlyphGrid) SetCursor(x, y uint) {
if x > gg.SizeX || y > gg.SizeY {
panic("cursor position can not be larger than grid size")
}
gg.CursorX = x
gg.CursorY = y
}
func (gg *GlyphGrid) TickCursor(forceDown bool) (success bool) {
if gg.CursorX == gg.SizeX-1 && gg.CursorY == gg.SizeY-1 {
// fmt.Println("trying to advance cursor beyond grid which is not allowed. Keeping cursor at position")
return false
}
if forceDown {
if gg.CursorY == gg.SizeY-1 {
// fmt.Println("trying to move cursor to next line but cursor already at last line. Keeping cursor at position")
return false
}
gg.CursorX = 0
gg.CursorY++
return true
}
gg.CursorX++
if gg.CursorX >= gg.SizeX {
gg.CursorX = 0
gg.CursorY++
}
return true
}
func (gg *GlyphGrid) Print() {
fmt.Println("\n---")
for y := 0; y < len(gg.Tiles); y++ {
row := gg.Tiles[y]
for x := 0; x < len(row); x++ {
if row[x].Glyph == utf8.RuneError {
break
}
fmt.Print(string(row[x].Glyph))
}
fmt.Print("\n")
}
fmt.Println("---")
}
func NewGlyphGrid(width, height uint) *GlyphGrid {
if width == 0 || height == 0 {
panic("glyph grid width and height must be larger than zero")
}
tiles := make([][]GridTile, height)
for i := 0; i < len(tiles); i++ {
tiles[i] = make([]GridTile, width)
}
return &GlyphGrid{
CursorX: 0,
CursorY: 0,
SizeX: width,
SizeY: height,
Tiles: tiles,
}
}

359
main.go
View File

@ -91,9 +91,7 @@ type nterm struct {
scrollPosRel int64
scrollSpd int64
CellCountX int64
CellCountY int64
CellCount int64
glyphGrid *GlyphGrid
activeCmd *Cmd
Settings *Settings
@ -129,6 +127,8 @@ var (
yOff float32 = 0
)
// @TODO: We should 'draw' and apply ansi operations on an in-mem grid and send the final grid for rendering
func main() {
err := engine.Init()
@ -150,7 +150,7 @@ func main() {
win: win,
rend: rend,
imguiInfo: nmageimgui.NewImGUI(),
FontSize: 40,
FontSize: 24,
Lines: ring.NewBuffer[Line](defaultLineBufSize),
@ -192,90 +192,97 @@ func main() {
}
}
func (p *nterm) handleSDLEvent(e sdl.Event) {
func (nt *nterm) handleSDLEvent(e sdl.Event) {
switch e := e.(type) {
case *sdl.TextInputEvent:
p.WriteToCmdBuf([]rune(e.GetText()))
nt.WriteToCmdBuf([]rune(e.GetText()))
case *sdl.WindowEvent:
if e.Event == sdl.WINDOWEVENT_SIZE_CHANGED {
p.HandleWindowResize()
nt.HandleWindowResize()
}
}
}
func (p *nterm) Init() {
func (nt *nterm) Init() {
dpi, _, _, err := sdl.GetDisplayDPI(0)
if err != nil {
panic("Failed to get display DPI. Err: " + err.Error())
}
fmt.Printf("DPI: %f, font size: %d\n", dpi, p.FontSize)
fmt.Printf("DPI: %f, font size: %d\n", dpi, nt.FontSize)
w, h := p.win.SDLWin.GetSize()
w, h := nt.win.SDLWin.GetSize()
// p.GlyphRend, err = glyphs.NewGlyphRend("./res/fonts/tajawal-regular-var.ttf", &truetype.Options{Size: float64(p.FontSize), DPI: p.Dpi, SubPixelsX: subPixelX, SubPixelsY: subPixelY, Hinting: hinting}, w, h)
p.GlyphRend, err = glyphs.NewGlyphRend("./res/fonts/alm-fixed.ttf", &truetype.Options{Size: float64(p.FontSize), DPI: p.Dpi, SubPixelsX: subPixelX, SubPixelsY: subPixelY, Hinting: hinting}, w, h)
// nt.GlyphRend, err = glyphs.NewGlyphRend("./res/fonts/alm-fixed.ttf", &truetype.Options{Size: float64(nt.FontSize), DPI: nt.Dpi, SubPixelsX: subPixelX, SubPixelsY: subPixelY, Hinting: hinting}, w, h)
nt.GlyphRend, err = glyphs.NewGlyphRend("./res/fonts/CascadiaMono-Regular.ttf", &truetype.Options{Size: float64(nt.FontSize), DPI: nt.Dpi, SubPixelsX: subPixelX, SubPixelsY: subPixelY, Hinting: hinting}, w, h)
if err != nil {
panic("Failed to create atlas from font file. Err: " + err.Error())
}
p.GlyphRend.OptValues.BgColor = gglm.NewVec4(0, 0, 0, 0)
p.GlyphRend.SetOpts(glyphs.GlyphRendOpt_BgColor)
nt.GlyphRend.OptValues.BgColor = gglm.NewVec4(0, 0, 0, 0)
nt.GlyphRend.SetOpts(glyphs.GlyphRendOpt_BgColor)
// if consts.Mode_Debug {
// glyphs.SaveImgToPNG(p.GlyphRend.Atlas.Img, "./debug-atlas.png")
// }
//Load resources
p.gridMesh, err = meshes.NewMesh("grid", "./res/models/quad.obj", 0)
nt.gridMesh, err = meshes.NewMesh("grid", "./res/models/quad.obj", 0)
if err != nil {
panic(err.Error())
}
p.gridMat = materials.NewMaterial("grid", "./res/shaders/grid.glsl")
p.HandleWindowResize()
nt.gridMat = materials.NewMaterial("grid", "./res/shaders/grid.glsl")
nt.HandleWindowResize()
//Set initial cursor pos
p.lastCmdCharPos.SetY(p.GlyphRend.Atlas.LineHeight)
// Set initial cursor pos
nt.lastCmdCharPos.SetY(nt.GlyphRend.Atlas.LineHeight)
// Init glyph grid
gridWidth, gridHeight := nt.GridSize()
nt.glyphGrid = NewGlyphGrid(uint(gridWidth), uint(gridHeight))
}
func (p *nterm) Update() {
func (nt *nterm) Update() {
p.frameStartTime = time.Now()
nt.frameStartTime = time.Now()
if input.IsQuitClicked() || input.KeyClicked(sdl.K_ESCAPE) {
engine.Quit()
}
if consts.Mode_Debug {
p.DebugUpdate()
nt.DebugUpdate()
}
//Font sizing
oldFont := p.FontSize
oldFontSize := nt.FontSize
fontSizeChanged := false
if input.KeyClicked(sdl.K_KP_PLUS) {
p.FontSize += 2
nt.FontSize += 2
fontSizeChanged = true
} else if input.KeyClicked(sdl.K_KP_MINUS) {
p.FontSize -= 2
nt.FontSize -= 2
fontSizeChanged = true
}
if fontSizeChanged {
err := p.GlyphRend.SetFace(&truetype.Options{Size: float64(p.FontSize), DPI: p.Dpi, SubPixelsX: subPixelX, SubPixelsY: subPixelY, Hinting: hinting})
err := nt.GlyphRend.SetFace(&truetype.Options{Size: float64(nt.FontSize), DPI: nt.Dpi, SubPixelsX: subPixelX, SubPixelsY: subPixelY, Hinting: hinting})
if err != nil {
p.FontSize = oldFont
nt.FontSize = oldFontSize
fmt.Println("Failed to update font face. Err: " + err.Error())
} else {
glyphs.SaveImgToPNG(p.GlyphRend.Atlas.Img, "./debug-atlas.png")
fmt.Println("New font size:", p.FontSize, "; New texture size:", p.GlyphRend.Atlas.Img.Rect.Max.X)
glyphs.SaveImgToPNG(nt.GlyphRend.Atlas.Img, "./debug-atlas.png")
gridWidth, gridHeight := nt.GridSize()
nt.glyphGrid = NewGlyphGrid(uint(gridWidth), uint(gridHeight))
fmt.Println("New font size:", nt.FontSize, "; New texture size:", nt.GlyphRend.Atlas.Img.Rect.Max.X)
}
}
p.MainUpdate()
nt.MainUpdate()
}
func (nt *nterm) MainUpdate() {
@ -317,17 +324,43 @@ func (nt *nterm) MainUpdate() {
nt.SepLinePos.SetY(2 * nt.GlyphRend.Atlas.LineHeight)
// Draw textBuf
nt.glyphGrid.ClearAll()
nt.glyphGrid.SetCursor(0, 0)
gw, gh := nt.GridSize()
v1, v2 := nt.textBuf.ViewsFromToRelIndex(uint64(nt.scrollPosRel), uint64(nt.scrollPosRel)+uint64(gw*gh))
nt.lastCmdCharPos.Data = gglm.NewVec3(0, float32(nt.GlyphRend.ScreenHeight)-nt.GlyphRend.Atlas.LineHeight, 0).Data
nt.lastCmdCharPos.Data = nt.DrawTextAnsiCodes(v1, *nt.lastCmdCharPos).Data
nt.lastCmdCharPos.Data = nt.DrawTextAnsiCodes(v2, *nt.lastCmdCharPos).Data
nt.DrawTextAnsiCodesOnGlyphGrid(v1)
nt.DrawTextAnsiCodesOnGlyphGrid(v2)
nt.glyphGrid.ClearRow(nt.glyphGrid.SizeY - 1)
nt.glyphGrid.ClearRow(nt.glyphGrid.SizeY - 2)
nt.glyphGrid.ClearRow(nt.glyphGrid.SizeY - 3)
for y := 0; y < len(nt.glyphGrid.Tiles); y++ {
row := nt.glyphGrid.Tiles[y]
for x := 0; x < len(row); x++ {
g := row[x]
if g.Glyph == utf8.RuneError {
continue
}
nt.GlyphRend.OptValues.BgColor.Data = g.BgColor.Data
nt.lastCmdCharPos.Data = nt.GlyphRend.DrawTextOpenGLAbsRectWithStartPos([]rune{g.Glyph}, nt.lastCmdCharPos, gglm.NewVec3(0, 0, 0), gglm.NewVec2(float32(nt.GlyphRend.ScreenWidth), 2*nt.GlyphRend.Atlas.LineHeight), &g.FgColor).Data
}
}
nt.GlyphRend.OptValues.BgColor.Data = nt.Settings.DefaultBgColor.Data
// Draw cmd buf
nt.lastCmdCharPos.SetX(0)
nt.lastCmdCharPos.SetY(nt.SepLinePos.Y() - nt.GlyphRend.Atlas.LineHeight)
nt.lastCmdCharPos.Data = nt.SyntaxHighlightAndDraw(nt.cmdBuf[:nt.cmdBufLen], *nt.lastCmdCharPos).Data
if input.KeyClicked(sdl.K_F4) {
nt.glyphGrid.Print()
println(nt.glyphGrid.SizeX, nt.glyphGrid.SizeY)
}
}
func (nt *nterm) ReadInputs() {
@ -389,33 +422,14 @@ func (nt *nterm) ReadInputs() {
}
}
func (p *nterm) DrawTextAnsiCodes(bs []byte, pos gglm.Vec3) gglm.Vec3 {
func (nt *nterm) DrawTextAnsiCodesOnGlyphGrid(bs []byte) {
currFgColor := p.Settings.DefaultFgColor
currBgColor := p.Settings.DefaultBgColor
// @TODO: We should remember color state even if the ansi codes are out of view
currFgColor := nt.Settings.DefaultFgColor
currBgColor := nt.Settings.DefaultBgColor
draw := func(rs []rune) {
p.GlyphRend.OptValues.BgColor.Data = currBgColor.Data
startIndex := 0
for i := 0; i < len(rs); i++ {
r := rs[i]
// @PERF We could probably use bytes.IndexByte here
if r == '\n' {
pos.Data = p.GlyphRend.DrawTextOpenGLAbsRectWithStartPos(rs[startIndex:i], &pos, gglm.NewVec3(0, 0, 0), gglm.NewVec2(float32(p.GlyphRend.ScreenWidth), 2*p.GlyphRend.Atlas.LineHeight), &currFgColor).Data
pos.SetX(0)
pos.AddY(-p.GlyphRend.Atlas.LineHeight)
startIndex = i + 1
continue
}
}
if startIndex < len(rs) {
pos.Data = p.GlyphRend.DrawTextOpenGLAbsRectWithStartPos(rs[startIndex:], &pos, gglm.NewVec3(0, 0, 0), gglm.NewVec2(float32(p.GlyphRend.ScreenWidth), 2*p.GlyphRend.Atlas.LineHeight), &currFgColor).Data
}
nt.glyphGrid.Write(rs, &currFgColor, &currBgColor)
}
for {
@ -435,7 +449,7 @@ func (p *nterm) DrawTextAnsiCodes(bs []byte, pos gglm.Vec3) gglm.Vec3 {
if info.Options.HasOptions(ansi.AnsiCodeOptions_ColorFg) {
if info.Info1.X() == -1 {
currFgColor = p.Settings.DefaultFgColor
currFgColor = nt.Settings.DefaultFgColor
} else {
currFgColor = info.Info1
}
@ -443,7 +457,7 @@ func (p *nterm) DrawTextAnsiCodes(bs []byte, pos gglm.Vec3) gglm.Vec3 {
if info.Options.HasOptions(ansi.AnsiCodeOptions_ColorBg) {
if info.Info1.X() == -1 {
currBgColor = p.Settings.DefaultBgColor
currBgColor = nt.Settings.DefaultBgColor
} else {
currBgColor = info.Info1
}
@ -452,15 +466,13 @@ func (p *nterm) DrawTextAnsiCodes(bs []byte, pos gglm.Vec3) gglm.Vec3 {
// Advance beyond the code chars
bs = bs[index+len(code):]
}
return pos
}
func (p *nterm) SyntaxHighlightAndDraw(text []rune, pos gglm.Vec3) gglm.Vec3 {
func (nt *nterm) SyntaxHighlightAndDraw(text []rune, pos gglm.Vec3) gglm.Vec3 {
startIndex := 0
startPos := pos.Clone()
currColor := &p.Settings.DefaultFgColor
currColor := &nt.Settings.DefaultFgColor
inSingleString := false
inDoubleString := false
@ -474,9 +486,9 @@ func (p *nterm) SyntaxHighlightAndDraw(text []rune, pos gglm.Vec3) gglm.Vec3 {
// to the middle of the text not the start as it uses the start X position of the second half.
// So to get correct new line handling we handle newlines here
case '\n':
pos.Data = p.GlyphRend.DrawTextOpenGLAbs(text[startIndex:i], &pos, currColor).Data
pos.Data = nt.GlyphRend.DrawTextOpenGLAbs(text[startIndex:i], &pos, currColor).Data
pos.SetX(startPos.X())
pos.AddY(-p.GlyphRend.Atlas.LineHeight)
pos.AddY(-nt.GlyphRend.Atlas.LineHeight)
startIndex = i + 1
continue
@ -487,18 +499,18 @@ func (p *nterm) SyntaxHighlightAndDraw(text []rune, pos gglm.Vec3) gglm.Vec3 {
}
if !inDoubleString {
pos.Data = p.GlyphRend.DrawTextOpenGLAbs(text[startIndex:i], &pos, currColor).Data
pos.Data = nt.GlyphRend.DrawTextOpenGLAbs(text[startIndex:i], &pos, currColor).Data
startIndex = i
inDoubleString = true
currColor = &p.Settings.StringColor
currColor = &nt.Settings.StringColor
continue
}
pos.Data = p.GlyphRend.DrawTextOpenGLAbs(text[startIndex:i+1], &pos, currColor).Data
pos.Data = nt.GlyphRend.DrawTextOpenGLAbs(text[startIndex:i+1], &pos, currColor).Data
startIndex = i + 1
inDoubleString = false
currColor = &p.Settings.DefaultFgColor
currColor = &nt.Settings.DefaultFgColor
case '\'':
if inDoubleString {
@ -506,72 +518,72 @@ func (p *nterm) SyntaxHighlightAndDraw(text []rune, pos gglm.Vec3) gglm.Vec3 {
}
if !inSingleString {
pos.Data = p.GlyphRend.DrawTextOpenGLAbs(text[startIndex:i], &pos, currColor).Data
pos.Data = nt.GlyphRend.DrawTextOpenGLAbs(text[startIndex:i], &pos, currColor).Data
startIndex = i
inSingleString = true
currColor = &p.Settings.StringColor
currColor = &nt.Settings.StringColor
continue
}
pos.Data = p.GlyphRend.DrawTextOpenGLAbs(text[startIndex:i+1], &pos, &p.Settings.StringColor).Data
pos.Data = nt.GlyphRend.DrawTextOpenGLAbs(text[startIndex:i+1], &pos, &nt.Settings.StringColor).Data
startIndex = i + 1
inSingleString = false
currColor = &p.Settings.DefaultFgColor
currColor = &nt.Settings.DefaultFgColor
}
}
if startIndex < len(text) {
if inDoubleString || inSingleString {
pos.Data = p.GlyphRend.DrawTextOpenGLAbs(text[startIndex:], &pos, &p.Settings.StringColor).Data
pos.Data = nt.GlyphRend.DrawTextOpenGLAbs(text[startIndex:], &pos, &nt.Settings.StringColor).Data
} else {
pos.Data = p.GlyphRend.DrawTextOpenGLAbs(text[startIndex:], &pos, &p.Settings.DefaultFgColor).Data
pos.Data = nt.GlyphRend.DrawTextOpenGLAbs(text[startIndex:], &pos, &nt.Settings.DefaultFgColor).Data
}
}
return pos
}
func (p *nterm) DeletePrevChar() {
func (nt *nterm) DeletePrevChar() {
if p.cursorCharIndex == 0 || p.cmdBufLen == 0 {
if nt.cursorCharIndex == 0 || nt.cmdBufLen == 0 {
return
}
copy(p.cmdBuf[p.cursorCharIndex-1:], p.cmdBuf[p.cursorCharIndex:])
copy(nt.cmdBuf[nt.cursorCharIndex-1:], nt.cmdBuf[nt.cursorCharIndex:])
p.cmdBufLen--
p.cursorCharIndex--
nt.cmdBufLen--
nt.cursorCharIndex--
}
func (p *nterm) DeleteNextChar() {
func (nt *nterm) DeleteNextChar() {
if p.cmdBufLen == 0 || p.cursorCharIndex == p.cmdBufLen {
if nt.cmdBufLen == 0 || nt.cursorCharIndex == nt.cmdBufLen {
return
}
copy(p.cmdBuf[p.cursorCharIndex:], p.cmdBuf[p.cursorCharIndex+1:])
copy(nt.cmdBuf[nt.cursorCharIndex:], nt.cmdBuf[nt.cursorCharIndex+1:])
p.cmdBufLen--
nt.cmdBufLen--
}
func (p *nterm) HandleReturn() {
func (nt *nterm) HandleReturn() {
cmdRunes := p.cmdBuf[:p.cmdBufLen]
p.cmdBufLen = 0
p.cursorCharIndex = 0
cmdRunes := nt.cmdBuf[:nt.cmdBufLen]
nt.cmdBufLen = 0
nt.cursorCharIndex = 0
cmdStr := string(cmdRunes)
cmdBytes := []byte(cmdStr)
p.WriteToTextBuf(cmdBytes)
nt.WriteToTextBuf(cmdBytes)
if p.activeCmd != nil {
if nt.activeCmd != nil {
// println("Wrote:", string(cmdBytes))
_, err := p.activeCmd.Stdin.Write(cmdBytes)
_, err := nt.activeCmd.Stdin.Write(cmdBytes)
if err != nil {
p.WriteToTextBuf([]byte(fmt.Sprintf("Writing to stdin pipe of '%s' failed. Error: %s\n", p.activeCmd.C.Path, err.Error())))
p.ClearActiveCmd()
nt.WriteToTextBuf([]byte(fmt.Sprintf("Writing to stdin pipe of '%s' failed. Error: %s\n", nt.activeCmd.C.Path, err.Error())))
nt.ClearActiveCmd()
return
}
@ -594,29 +606,29 @@ func (p *nterm) HandleReturn() {
outPipe, err := cmd.StdoutPipe()
if err != nil {
p.WriteToTextBuf([]byte(fmt.Sprintf("Creating stdout pipe of '%s' failed. Error: %s\n", cmdName, err.Error())))
nt.WriteToTextBuf([]byte(fmt.Sprintf("Creating stdout pipe of '%s' failed. Error: %s\n", cmdName, err.Error())))
return
}
inPipe, err := cmd.StdinPipe()
if err != nil {
p.WriteToTextBuf([]byte(fmt.Sprintf("Creating stdin pipe of '%s' failed. Error: %s\n", cmdName, err.Error())))
nt.WriteToTextBuf([]byte(fmt.Sprintf("Creating stdin pipe of '%s' failed. Error: %s\n", cmdName, err.Error())))
return
}
errPipe, err := cmd.StderrPipe()
if err != nil {
p.WriteToTextBuf([]byte(fmt.Sprintf("Creating stderr pipe of '%s' failed. Error: %s\n", cmdName, err.Error())))
nt.WriteToTextBuf([]byte(fmt.Sprintf("Creating stderr pipe of '%s' failed. Error: %s\n", cmdName, err.Error())))
return
}
startTime := time.Now()
err = cmd.Start()
if err != nil {
p.WriteToTextBuf([]byte(fmt.Sprintf("Running '%s' failed. Error: %s\n", cmdName, err.Error())))
nt.WriteToTextBuf([]byte(fmt.Sprintf("Running '%s' failed. Error: %s\n", cmdName, err.Error())))
return
}
p.activeCmd = &Cmd{
nt.activeCmd = &Cmd{
C: cmd,
Stdout: outPipe,
Stdin: inPipe,
@ -630,18 +642,18 @@ func (p *nterm) HandleReturn() {
fmt.Printf("Cmd '%s' took %0.2fs\n", cmdName, time.Since(startTime).Seconds())
}()
defer p.ClearActiveCmd()
defer nt.ClearActiveCmd()
buf := make([]byte, 4*1024)
for p.activeCmd != nil {
for nt.activeCmd != nil {
readBytes, err := p.activeCmd.Stdout.Read(buf)
readBytes, err := nt.activeCmd.Stdout.Read(buf)
if err != nil {
if err == io.EOF {
break
}
p.WriteToTextBuf([]byte("Stdout pipe failed. Error: " + err.Error()))
nt.WriteToTextBuf([]byte("Stdout pipe failed. Error: " + err.Error()))
return
}
@ -651,7 +663,7 @@ func (p *nterm) HandleReturn() {
// @Todo We need to parse ansi codes as data is coming in to update the drawing settings (e.g. color)
b := buf[:readBytes]
p.WriteToTextBuf(b)
nt.WriteToTextBuf(b)
// println("Read:", string(buf[:readBytes]))
}
}()
@ -660,16 +672,16 @@ func (p *nterm) HandleReturn() {
go func() {
buf := make([]byte, 1024)
for p.activeCmd != nil {
for nt.activeCmd != nil {
readBytes, err := p.activeCmd.Stderr.Read(buf)
readBytes, err := nt.activeCmd.Stderr.Read(buf)
if err != nil {
if err == io.EOF {
break
}
p.WriteToTextBuf([]byte("Stderr pipe failed. Error: " + err.Error()))
nt.WriteToTextBuf([]byte("Stderr pipe failed. Error: " + err.Error()))
return
}
@ -677,12 +689,12 @@ func (p *nterm) HandleReturn() {
continue
}
p.WriteToTextBuf(buf[:readBytes])
nt.WriteToTextBuf(buf[:readBytes])
}
}()
}
func (p *nterm) ParseLines(bs []byte) {
func (nt *nterm) ParseLines(bs []byte) {
// @TODO We should virtually break lines when they are too long
checkedBytes := uint64(0)
@ -696,57 +708,57 @@ func (p *nterm) ParseLines(bs []byte) {
bs = bs[index+1:]
checkedBytes += uint64(index + 1)
p.LineBeingParsed.EndIndex_WriteCount = p.textBuf.WrittenElements + checkedBytes
p.WriteLine(&p.LineBeingParsed)
p.LineBeingParsed.StartIndex_WriteCount = p.textBuf.WrittenElements + checkedBytes
nt.LineBeingParsed.EndIndex_WriteCount = nt.textBuf.WrittenElements + checkedBytes
nt.WriteLine(&nt.LineBeingParsed)
nt.LineBeingParsed.StartIndex_WriteCount = nt.textBuf.WrittenElements + checkedBytes
}
}
func (p *nterm) WriteLine(l *Line) {
func (nt *nterm) WriteLine(l *Line) {
assert.T(l.StartIndex_WriteCount <= l.EndIndex_WriteCount, "Invalid line: %+v\n", l)
p.Lines.Write(*l)
nt.Lines.Write(*l)
}
func (p *nterm) ClearActiveCmd() {
func (nt *nterm) ClearActiveCmd() {
if p.activeCmd == nil {
if nt.activeCmd == nil {
return
}
p.activeCmd = nil
nt.activeCmd = nil
}
func (p *nterm) DrawCursor() {
func (nt *nterm) DrawCursor() {
//Position cursor by placing it at the end of the drawn characters then walking backwards
pos := p.lastCmdCharPos.Clone()
pos := nt.lastCmdCharPos.Clone()
pos.AddY(p.GlyphRend.Atlas.LineHeight * 0.5)
for i := clamp(p.cmdBufLen, 0, int64(len(p.cmdBuf))); i > p.cursorCharIndex; i-- {
pos.AddY(nt.GlyphRend.Atlas.LineHeight * 0.5)
for i := clamp(nt.cmdBufLen, 0, int64(len(nt.cmdBuf))); i > nt.cursorCharIndex; i-- {
if p.cmdBuf[i] == '\n' {
pos.AddY(p.GlyphRend.Atlas.LineHeight)
if nt.cmdBuf[i] == '\n' {
pos.AddY(nt.GlyphRend.Atlas.LineHeight)
continue
}
pos.AddX(-p.GlyphRend.Atlas.SpaceAdvance)
pos.AddX(-nt.GlyphRend.Atlas.SpaceAdvance)
}
p.rend.Draw(p.gridMesh, gglm.NewTrMatId().Translate(pos).Scale(gglm.NewVec3(0.1*p.GlyphRend.Atlas.SpaceAdvance, p.GlyphRend.Atlas.LineHeight, 1)), p.gridMat)
nt.rend.Draw(nt.gridMesh, gglm.NewTrMatId().Translate(pos).Scale(gglm.NewVec3(0.1*nt.GlyphRend.Atlas.SpaceAdvance, nt.GlyphRend.Atlas.LineHeight, 1)), nt.gridMat)
}
// GridSize returns how many cells horizontally (aka chars per line) and how many cells vertically (aka lines)
func (p *nterm) GridSize() (w, h int64) {
w = int64(p.GlyphRend.ScreenWidth) / int64(p.GlyphRend.Atlas.SpaceAdvance)
h = int64(p.GlyphRend.ScreenHeight) / int64(p.GlyphRend.Atlas.LineHeight)
func (nt *nterm) GridSize() (w, h int64) {
w = int64(nt.GlyphRend.ScreenWidth) / int64(nt.GlyphRend.Atlas.SpaceAdvance)
h = int64(nt.GlyphRend.ScreenHeight) / int64(nt.GlyphRend.Atlas.LineHeight)
return w, h
}
func (p *nterm) ScreenPosToGridPos(screenPos *gglm.Vec3) {
screenPos.SetX(FloorF32(screenPos.X() / p.GlyphRend.Atlas.SpaceAdvance))
screenPos.SetY(FloorF32(screenPos.Y() / p.GlyphRend.Atlas.LineHeight))
func (nt *nterm) ScreenPosToGridPos(screenPos *gglm.Vec3) {
screenPos.SetX(FloorF32(screenPos.X() / nt.GlyphRend.Atlas.SpaceAdvance))
screenPos.SetY(FloorF32(screenPos.Y() / nt.GlyphRend.Atlas.LineHeight))
}
func (p *nterm) DebugUpdate() {
func (nt *nterm) DebugUpdate() {
//Move text
var speed float32 = 1
@ -768,24 +780,24 @@ func (p *nterm) DebugUpdate() {
}
}
func (p *nterm) Render() {
func (nt *nterm) Render() {
defer p.GlyphRend.Draw()
defer nt.GlyphRend.Draw()
if consts.Mode_Debug {
p.DebugRender()
nt.DebugRender()
sizeX := float32(p.GlyphRend.ScreenWidth)
p.rend.Draw(p.gridMesh, gglm.NewTrMatId().Translate(gglm.NewVec3(sizeX/2, p.SepLinePos.Y(), 0)).Scale(gglm.NewVec3(sizeX, 1, 1)), p.gridMat)
sizeX := float32(nt.GlyphRend.ScreenWidth)
nt.rend.Draw(nt.gridMesh, gglm.NewTrMatId().Translate(gglm.NewVec3(sizeX/2, nt.SepLinePos.Y(), 0)).Scale(gglm.NewVec3(sizeX, 1, 1)), nt.gridMat)
}
p.DrawCursor()
nt.DrawCursor()
}
func (p *nterm) DebugRender() {
func (nt *nterm) DebugRender() {
if drawGrid {
p.DrawGrid()
nt.DrawGrid()
}
fps := int(timing.GetAvgFPS())
@ -795,43 +807,43 @@ func (p *nterm) DebugRender() {
if drawManyLines {
const charsPerFrame = 500_000
for i := 0; i < charsPerFrame/charCount; i++ {
p.GlyphRend.DrawTextOpenGLAbsString(str, gglm.NewVec3(xOff, float32(p.GlyphRend.Atlas.LineHeight)*5+yOff, 0), &p.Settings.DefaultFgColor)
nt.GlyphRend.DrawTextOpenGLAbsString(str, gglm.NewVec3(xOff, float32(nt.GlyphRend.Atlas.LineHeight)*5+yOff, 0), &nt.Settings.DefaultFgColor)
}
p.win.SDLWin.SetTitle(fmt.Sprint("FPS: ", fps, " Draws/f: ", math.Ceil(charsPerFrame/glyphs.DefaultGlyphsPerBatch), " chars/f: ", charsPerFrame, " chars/s: ", fps*charsPerFrame))
nt.win.SDLWin.SetTitle(fmt.Sprint("FPS: ", fps, " Draws/f: ", math.Ceil(charsPerFrame/glyphs.DefaultGlyphsPerBatch), " chars/f: ", charsPerFrame, " chars/s: ", fps*charsPerFrame))
} else {
charsPerFrame := float64(charCount)
p.GlyphRend.DrawTextOpenGLAbsString(str, gglm.NewVec3(xOff, float32(p.GlyphRend.Atlas.LineHeight)*5+yOff, 0), &p.Settings.DefaultFgColor)
p.win.SDLWin.SetTitle(fmt.Sprint("FPS: ", fps, " Draws/f: ", math.Ceil(charsPerFrame/glyphs.DefaultGlyphsPerBatch), " chars/f: ", int(charsPerFrame), " chars/s: ", fps*int(charsPerFrame)))
nt.GlyphRend.DrawTextOpenGLAbsString(str, gglm.NewVec3(xOff, float32(nt.GlyphRend.Atlas.LineHeight)*5+yOff, 0), &nt.Settings.DefaultFgColor)
nt.win.SDLWin.SetTitle(fmt.Sprint("FPS: ", fps, " Draws/f: ", math.Ceil(charsPerFrame/glyphs.DefaultGlyphsPerBatch), " chars/f: ", int(charsPerFrame), " chars/s: ", fps*int(charsPerFrame)))
}
} else {
p.win.SDLWin.SetTitle(fmt.Sprint("FPS: ", fps))
nt.win.SDLWin.SetTitle(fmt.Sprint("FPS: ", fps))
}
}
func (p *nterm) DrawGrid() {
func (nt *nterm) DrawGrid() {
sizeX := float32(p.GlyphRend.ScreenWidth)
sizeY := float32(p.GlyphRend.ScreenHeight)
sizeX := float32(nt.GlyphRend.ScreenWidth)
sizeY := float32(nt.GlyphRend.ScreenHeight)
//columns
adv := p.GlyphRend.Atlas.SpaceAdvance
for i := 0; i < int(p.GlyphRend.ScreenWidth); i++ {
p.rend.Draw(p.gridMesh, gglm.NewTrMatId().Translate(gglm.NewVec3(adv*float32(i), sizeY/2, 0)).Scale(gglm.NewVec3(1, sizeY, 1)), p.gridMat)
adv := nt.GlyphRend.Atlas.SpaceAdvance
for i := 0; i < int(nt.GlyphRend.ScreenWidth); i++ {
nt.rend.Draw(nt.gridMesh, gglm.NewTrMatId().Translate(gglm.NewVec3(adv*float32(i), sizeY/2, 0)).Scale(gglm.NewVec3(1, sizeY, 1)), nt.gridMat)
}
//rows
for i := int32(0); i < p.GlyphRend.ScreenHeight; i += int32(p.GlyphRend.Atlas.LineHeight) {
p.rend.Draw(p.gridMesh, gglm.NewTrMatId().Translate(gglm.NewVec3(sizeX/2, float32(i), 0)).Scale(gglm.NewVec3(sizeX, 1, 1)), p.gridMat)
for i := int32(0); i < nt.GlyphRend.ScreenHeight; i += int32(nt.GlyphRend.Atlas.LineHeight) {
nt.rend.Draw(nt.gridMesh, gglm.NewTrMatId().Translate(gglm.NewVec3(sizeX/2, float32(i), 0)).Scale(gglm.NewVec3(sizeX, 1, 1)), nt.gridMat)
}
}
func (p *nterm) FrameEnd() {
assert.T(p.cursorCharIndex <= p.cmdBufLen, "Cursor char index is larger than cmdBufLen! You probablly forgot to move/reset the cursor index along with the buffer length somewhere. Cursor=%d, cmdBufLen=%d\n", p.cursorCharIndex, p.cmdBufLen)
func (nt *nterm) FrameEnd() {
assert.T(nt.cursorCharIndex <= nt.cmdBufLen, "Cursor char index is larger than cmdBufLen! You probablly forgot to move/reset the cursor index along with the buffer length somewhere. Cursor=%d, cmdBufLen=%d\n", nt.cursorCharIndex, nt.cmdBufLen)
if p.Settings.LimitFps {
if nt.Settings.LimitFps {
elapsed := time.Since(p.frameStartTime)
microSecondsPerFrame := int64(1 / float32(p.Settings.MaxFps) * 1000_000)
elapsed := time.Since(nt.frameStartTime)
microSecondsPerFrame := int64(1 / float32(nt.Settings.MaxFps) * 1000_000)
// Sleep time is reduced by a millisecond to compensate for the (nearly) inevitable over-sleeping that will happen.
timeToSleep := time.Duration((microSecondsPerFrame - elapsed.Microseconds()) * 1000)
@ -843,42 +855,39 @@ func (p *nterm) FrameEnd() {
}
}
func (p *nterm) DeInit() {
func (nt *nterm) DeInit() {
}
func (p *nterm) HandleWindowResize() {
w, h := p.win.SDLWin.GetSize()
p.GlyphRend.SetScreenSize(w, h)
func (nt *nterm) HandleWindowResize() {
w, h := nt.win.SDLWin.GetSize()
nt.GlyphRend.SetScreenSize(w, h)
projMtx := gglm.Ortho(0, float32(w), float32(h), 0, 0.1, 20)
viewMtx := gglm.LookAt(gglm.NewVec3(0, 0, -10), gglm.NewVec3(0, 0, 0), gglm.NewVec3(0, 1, 0))
p.gridMat.SetUnifMat4("projViewMat", &projMtx.Mul(viewMtx).Mat4)
p.CellCountX, p.CellCountY = p.GridSize()
p.CellCount = p.CellCountX * p.CellCountY
nt.gridMat.SetUnifMat4("projViewMat", &projMtx.Mul(viewMtx).Mat4)
}
func (p *nterm) WriteToTextBuf(text []byte) {
func (nt *nterm) WriteToTextBuf(text []byte) {
// This is locked because running cmds are potentially writing to it same time we are
p.textBufMutex.Lock()
nt.textBufMutex.Lock()
p.ParseLines(text)
p.textBuf.Write(text...)
nt.ParseLines(text)
nt.textBuf.Write(text...)
p.textBufMutex.Unlock()
nt.textBufMutex.Unlock()
}
func (p *nterm) WriteToCmdBuf(text []rune) {
func (nt *nterm) WriteToCmdBuf(text []rune) {
delta := int64(len(text))
newHeadPos := p.cmdBufLen + delta
newHeadPos := nt.cmdBufLen + delta
if newHeadPos <= defaultCmdBufSize {
copy(p.cmdBuf[p.cursorCharIndex+delta:], p.cmdBuf[p.cursorCharIndex:])
copy(p.cmdBuf[p.cursorCharIndex:], text)
copy(nt.cmdBuf[nt.cursorCharIndex+delta:], nt.cmdBuf[nt.cursorCharIndex:])
copy(nt.cmdBuf[nt.cursorCharIndex:], text)
p.cursorCharIndex += delta
p.cmdBufLen = newHeadPos
nt.cursorCharIndex += delta
nt.cmdBufLen = newHeadPos
return
}

Binary file not shown.