112 lines
2.4 KiB
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()),
|
|
}
|
|
}
|