mirror of
https://github.com/bloeys/nterm.git
synced 2025-12-29 14:38:19 +00:00
Scroll by lines+mutex when writing to text buffer
This commit is contained in:
@ -17,7 +17,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MaxGlyphsPerBatch = 16384
|
DefaultGlyphsPerBatch = 16384
|
||||||
|
|
||||||
floatsPerGlyph = 13
|
floatsPerGlyph = 13
|
||||||
invalidRune = unicode.ReplacementChar
|
invalidRune = unicode.ReplacementChar
|
||||||
@ -210,7 +210,7 @@ func (gr *GlyphRend) drawRune(run *TextRun, i int, prevRune rune, screenPos, pos
|
|||||||
|
|
||||||
//If we fill the buffer we issue a draw call
|
//If we fill the buffer we issue a draw call
|
||||||
gr.GlyphCount++
|
gr.GlyphCount++
|
||||||
if gr.GlyphCount == MaxGlyphsPerBatch {
|
if gr.GlyphCount == DefaultGlyphsPerBatch {
|
||||||
gr.Draw()
|
gr.Draw()
|
||||||
*bufIndex = 0
|
*bufIndex = 0
|
||||||
}
|
}
|
||||||
@ -487,7 +487,7 @@ func NewGlyphRend(fontFile string, fontOptions *truetype.Options, screenWidth, s
|
|||||||
|
|
||||||
gr := &GlyphRend{
|
gr := &GlyphRend{
|
||||||
GlyphCount: 0,
|
GlyphCount: 0,
|
||||||
GlyphVBO: make([]float32, floatsPerGlyph*MaxGlyphsPerBatch),
|
GlyphVBO: make([]float32, floatsPerGlyph*DefaultGlyphsPerBatch),
|
||||||
TextRunsBuf: make([]TextRun, 0, 20),
|
TextRunsBuf: make([]TextRun, 0, 20),
|
||||||
SpacesPerTab: 4,
|
SpacesPerTab: 4,
|
||||||
}
|
}
|
||||||
|
|||||||
139
main.go
139
main.go
@ -8,6 +8,7 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
@ -55,7 +56,8 @@ type program struct {
|
|||||||
gridMesh *meshes.Mesh
|
gridMesh *meshes.Mesh
|
||||||
gridMat *materials.Material
|
gridMat *materials.Material
|
||||||
|
|
||||||
textBuf *ring.Buffer[byte]
|
textBuf *ring.Buffer[byte]
|
||||||
|
textBufMutex sync.Mutex
|
||||||
|
|
||||||
cmdBuf []rune
|
cmdBuf []rune
|
||||||
cmdBufLen int64
|
cmdBufLen int64
|
||||||
@ -69,8 +71,7 @@ type program struct {
|
|||||||
maxLinesToShow int64
|
maxLinesToShow int64
|
||||||
|
|
||||||
activeCmd *Cmd
|
activeCmd *Cmd
|
||||||
|
Settings *Settings
|
||||||
Settings *Settings
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -230,10 +231,12 @@ func (p *program) Update() {
|
|||||||
p.MainUpdate()
|
p.MainUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
// @TODO: These probably need a mutex
|
|
||||||
func (p *program) WriteToTextBuf(text []byte) {
|
func (p *program) WriteToTextBuf(text []byte) {
|
||||||
|
// This is locked because running cmds are potentially writing to it same time we are
|
||||||
|
p.textBufMutex.Lock()
|
||||||
p.textBuf.Write(text...)
|
p.textBuf.Write(text...)
|
||||||
p.scrollPos = clamp(p.textBuf.Len-p.maxCharsToShow, 0, p.textBuf.Len-1)
|
p.scrollPos = clamp(p.textBuf.Len-p.maxCharsToShow, 0, p.textBuf.Len-1)
|
||||||
|
p.textBufMutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *program) WriteToCmdBuf(text []rune) {
|
func (p *program) WriteToCmdBuf(text []rune) {
|
||||||
@ -277,7 +280,16 @@ func (p *program) MainUpdate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if mouseWheelYNorm := -int64(input.GetMouseWheelYNorm()); mouseWheelYNorm != 0 {
|
if mouseWheelYNorm := -int64(input.GetMouseWheelYNorm()); mouseWheelYNorm != 0 {
|
||||||
p.scrollPos = clamp(p.scrollPos+p.scrollSpd*mouseWheelYNorm, 0, p.textBuf.Len)
|
|
||||||
|
var newPosNewLines int64
|
||||||
|
w, _ := p.GridSize()
|
||||||
|
if mouseWheelYNorm < 0 {
|
||||||
|
newPosNewLines, _ = find_n_lines_index(p.textBuf.Data, p.scrollPos, p.scrollSpd*mouseWheelYNorm-1, int64(w))
|
||||||
|
} else {
|
||||||
|
newPosNewLines, _ = find_n_lines_index(p.textBuf.Data, p.scrollPos, p.scrollSpd*mouseWheelYNorm, int64(w))
|
||||||
|
}
|
||||||
|
|
||||||
|
p.scrollPos = clamp(newPosNewLines, 0, p.textBuf.Len)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete inputs
|
// Delete inputs
|
||||||
@ -290,7 +302,6 @@ func (p *program) MainUpdate() {
|
|||||||
p.DeleteNextChar()
|
p.DeleteNextChar()
|
||||||
}
|
}
|
||||||
|
|
||||||
// @TODO cmds should be printed with only syntax highlighting
|
|
||||||
// Draw textBuf
|
// Draw textBuf
|
||||||
v1, v2 := p.textBuf.Views()
|
v1, v2 := p.textBuf.Views()
|
||||||
|
|
||||||
@ -643,6 +654,10 @@ func (p *program) DrawCursor() {
|
|||||||
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)
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *program) GridSize() (w, h int32) {
|
||||||
|
return p.GlyphRend.ScreenWidth / int32(p.GlyphRend.Atlas.SpaceAdvance), p.GlyphRend.ScreenHeight / int32(p.GlyphRend.Atlas.LineHeight)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *program) ScreenPosToGridPos(screenPos *gglm.Vec3) {
|
func (p *program) ScreenPosToGridPos(screenPos *gglm.Vec3) {
|
||||||
screenPos.SetX(screenPos.X() / p.GlyphRend.Atlas.SpaceAdvance * p.GlyphRend.Atlas.SpaceAdvance)
|
screenPos.SetX(screenPos.X() / p.GlyphRend.Atlas.SpaceAdvance * p.GlyphRend.Atlas.SpaceAdvance)
|
||||||
screenPos.SetY(screenPos.Y() / p.GlyphRend.Atlas.LineHeight * p.GlyphRend.Atlas.LineHeight)
|
screenPos.SetY(screenPos.Y() / p.GlyphRend.Atlas.LineHeight * p.GlyphRend.Atlas.LineHeight)
|
||||||
@ -699,11 +714,11 @@ func (p *program) DebugRender() {
|
|||||||
for i := 0; i < charsPerFrame/charCount; i++ {
|
for i := 0; i < charsPerFrame/charCount; i++ {
|
||||||
p.GlyphRend.DrawTextOpenGLAbsString(str, gglm.NewVec3(xOff, float32(p.GlyphRend.Atlas.LineHeight)*5+yOff, 0), &p.Settings.DefaultColor)
|
p.GlyphRend.DrawTextOpenGLAbsString(str, gglm.NewVec3(xOff, float32(p.GlyphRend.Atlas.LineHeight)*5+yOff, 0), &p.Settings.DefaultColor)
|
||||||
}
|
}
|
||||||
p.win.SDLWin.SetTitle(fmt.Sprint("FPS: ", fps, " Draws/f: ", math.Ceil(charsPerFrame/glyphs.MaxGlyphsPerBatch), " chars/f: ", charsPerFrame, " chars/s: ", fps*charsPerFrame))
|
p.win.SDLWin.SetTitle(fmt.Sprint("FPS: ", fps, " Draws/f: ", math.Ceil(charsPerFrame/glyphs.DefaultGlyphsPerBatch), " chars/f: ", charsPerFrame, " chars/s: ", fps*charsPerFrame))
|
||||||
} else {
|
} else {
|
||||||
charsPerFrame := float64(charCount)
|
charsPerFrame := float64(charCount)
|
||||||
p.GlyphRend.DrawTextOpenGLAbsString(str, gglm.NewVec3(xOff, float32(p.GlyphRend.Atlas.LineHeight)*5+yOff, 0), &p.Settings.DefaultColor)
|
p.GlyphRend.DrawTextOpenGLAbsString(str, gglm.NewVec3(xOff, float32(p.GlyphRend.Atlas.LineHeight)*5+yOff, 0), &p.Settings.DefaultColor)
|
||||||
p.win.SDLWin.SetTitle(fmt.Sprint("FPS: ", fps, " Draws/f: ", math.Ceil(charsPerFrame/glyphs.MaxGlyphsPerBatch), " chars/f: ", int(charsPerFrame), " chars/s: ", fps*int(charsPerFrame)))
|
p.win.SDLWin.SetTitle(fmt.Sprint("FPS: ", fps, " Draws/f: ", math.Ceil(charsPerFrame/glyphs.DefaultGlyphsPerBatch), " chars/f: ", int(charsPerFrame), " chars/s: ", fps*int(charsPerFrame)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -769,3 +784,111 @@ func clamp[T constraints.Ordered](x, min, max T) T {
|
|||||||
|
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FindNthOrLastIndex[T comparable](arr []T, x T, startIndex, n int64) (lastIndex int64) {
|
||||||
|
|
||||||
|
lastIndex = -1
|
||||||
|
if n >= 0 {
|
||||||
|
|
||||||
|
for i := startIndex; i < int64(len(arr)); i++ {
|
||||||
|
|
||||||
|
if arr[i] != x {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
lastIndex = i
|
||||||
|
|
||||||
|
n--
|
||||||
|
if n <= 0 {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
for i := startIndex; i >= 0; i-- {
|
||||||
|
|
||||||
|
if arr[i] != x {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
lastIndex = i
|
||||||
|
|
||||||
|
n++
|
||||||
|
if n >= 0 {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
func find_n_lines_index(arr []byte, startIndex, n, charsPerLine int64) (lastIndex, lastSize int64) {
|
||||||
|
|
||||||
|
lastIndex = -1
|
||||||
|
lastSize = 0
|
||||||
|
if n >= 0 {
|
||||||
|
// @Note we should ignore zero width glyphs
|
||||||
|
// @Note is this better in glyphs package?
|
||||||
|
bytesSeen := int64(0)
|
||||||
|
arrSize := int64(len(arr))
|
||||||
|
charsSeenThisLine := int64(0)
|
||||||
|
for startIndex+bytesSeen < arrSize {
|
||||||
|
|
||||||
|
r, size := utf8.DecodeRune(arr[startIndex+bytesSeen:])
|
||||||
|
if r == utf8.RuneError {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
charsSeenThisLine++
|
||||||
|
bytesSeen += int64(size)
|
||||||
|
|
||||||
|
// If this is true we covered one line
|
||||||
|
if charsSeenThisLine == charsPerLine || r == '\n' {
|
||||||
|
|
||||||
|
charsSeenThisLine = 0
|
||||||
|
lastSize = int64(size)
|
||||||
|
lastIndex = startIndex + bytesSeen
|
||||||
|
|
||||||
|
n--
|
||||||
|
if n <= 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
bytesSeen := int64(0)
|
||||||
|
charsSeenThisLine := int64(0)
|
||||||
|
for startIndex-bytesSeen > 0 {
|
||||||
|
|
||||||
|
r, size := utf8.DecodeLastRune(arr[:startIndex-bytesSeen+1])
|
||||||
|
if r == utf8.RuneError {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
charsSeenThisLine++
|
||||||
|
bytesSeen += int64(size)
|
||||||
|
|
||||||
|
// If this is true we covered one line
|
||||||
|
if charsSeenThisLine == charsPerLine || r == '\n' {
|
||||||
|
|
||||||
|
charsSeenThisLine = 0
|
||||||
|
lastSize = int64(size)
|
||||||
|
lastIndex = startIndex - bytesSeen + 1 + lastSize
|
||||||
|
|
||||||
|
n++
|
||||||
|
if n >= 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle reaching beginning before finding nth line
|
||||||
|
if startIndex-bytesSeen == 0 {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastIndex, lastSize
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user