go-prompt/output_posix.go

102 lines
2.0 KiB
Go

// +build !windows
package prompt
import (
"context"
"log"
"os"
"os/signal"
"syscall"
"unsafe"
)
const flushMaxRetryCount = 3
// PosixWriter is a ConsoleWriter implementation for POSIX environment.
// To control terminal emulator, this outputs VT100 escape sequences.
type PosixWriter struct {
VT100Writer
fd int
}
// Flush to flush buffer
func (w *PosixWriter) Flush() error {
l := len(w.buffer)
offset := 0
retry := 0
for {
n, err := syscall.Write(w.fd, w.buffer[offset:])
if err != nil {
log.Printf("[DEBUG] flush error: %s", err)
if retry < flushMaxRetryCount {
retry++
continue
}
return err
}
offset += n
if offset == l {
break
}
}
w.buffer = []byte{}
return nil
}
// winsize is winsize struct got from the ioctl(2) system call.
type ioctlWinsize struct {
Row uint16
Col uint16
X uint16 // pixel value
Y uint16 // pixel value
}
// GetWinSize returns WinSize object to represent width and height of terminal.
func (w *PosixWriter) GetWinSize() WinSize {
ws := &ioctlWinsize{}
retCode, _, errno := syscall.Syscall(
syscall.SYS_IOCTL,
uintptr(w.fd),
uintptr(syscall.TIOCGWINSZ),
uintptr(unsafe.Pointer(ws)))
if int(retCode) == -1 {
panic(errno)
}
return WinSize{
Row: ws.Row,
Col: ws.Col,
}
}
// SIGWINCH returns WinSize channel which send WinSize when window size is changed.
func (w *PosixWriter) SIGWINCH(ctx context.Context) chan WinSize {
sigwinsz := make(chan WinSize, 1)
sigwinch := make(chan os.Signal, 1)
signal.Notify(sigwinch, syscall.SIGWINCH)
go func() {
for {
select {
case <-ctx.Done():
return
case <-sigwinch:
sigwinsz <- w.GetWinSize()
}
}
}()
return sigwinsz
}
var _ ConsoleWriter = &PosixWriter{}
// NewStandardOutputWriter returns ConsoleWriter object to write to stdout.
// This generates VT100 escape sequences because almost terminal emulators
// in POSIX OS built on top of a VT100 specification.
func NewStandardOutputWriter() (ConsoleWriter, error) {
return &PosixWriter{
fd: syscall.Stdout,
}, nil
}