piss/elements/terminal.go

112 lines
2.4 KiB
Go

package elements
import "io"
import "os"
import "image"
import "os/exec"
import "github.com/creack/pty"
import "git.tebibyte.media/sashakoshka/tomo"
import "git.tebibyte.media/sashakoshka/piss/ansi"
type Terminal struct {
*Grid
stdin io.WriteCloser
stdout io.ReadCloser
stderr io.ReadCloser
pty *os.File
bufferSize int
decoder ansi.Decoder
cursor image.Point
foreground tomo.Color
background tomo.Color
}
func NewTerminal () (element *Terminal) {
element = &Terminal {
Grid: NewGrid(),
foreground: tomo.ColorForeground,
background: tomo.ColorBackground,
bufferSize: 256,
}
element.Grid.OnResize(element.handleResize)
element.decoder.OnText = element.handleText
return
}
func (element *Terminal) Start (command *exec.Cmd) (err error) {
winsize := element.winsize()
worked := false
defer func () { if !worked { element.Close() } } ()
element.stdin, err = command.StdinPipe()
if err != nil { return }
element.stdout, err = command.StdoutPipe()
if err != nil { return }
element.stderr, err = command.StderrPipe()
if err != nil { return }
element.pty, err = pty.StartWithSize(command, &winsize)
if err != nil { return }
worked = true
go element.run()
return
}
func (element *Terminal) Close () {
if element.pty != nil { element.pty.Close() }
if element.stdin != nil { element.stdin.Close() }
if element.stdout != nil { element.stdout.Close() }
if element.stderr != nil { element.stderr.Close() }
}
func (element *Terminal) run () {
buffer := make([]byte, element.bufferSize)
for {
amount, err := element.stdout.Read(buffer)
if err != nil { break }
got := buffer[:amount]
for len(got) > 0 {
wrote, err := element.writeFromProcess(got)
got = got[wrote:]
if err != nil { break }
}
}
}
func (element *Terminal) handleText (text string) {
for _, char := range text {
element.Set(element.cursor, Cell {
Rune: char,
Foreground: element.foreground,
Background: element.background,
})
}
}
func (element *Terminal) writeFromProcess (buffer []byte) (wrote int, err error) {
wrote, err = element.decoder.Write(buffer)
element.Push()
return
}
func (element *Terminal) handleResize () {
}
func (element *Terminal) winsize () pty.Winsize {
columns, rows := element.Size()
bounds := element.Bounds()
return pty.Winsize {
Rows: uint16(rows),
Cols: uint16(columns),
X: uint16(bounds.Dx()),
Y: uint16(bounds.Dy()),
}
}