tea/vendor/github.com/muesli/termenv/termenv_unix.go

155 lines
3.1 KiB
Go

// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package termenv
import (
"fmt"
"os"
"runtime"
"strconv"
"strings"
"golang.org/x/sys/unix"
)
func colorProfile() Profile {
term := os.Getenv("TERM")
colorTerm := os.Getenv("COLORTERM")
switch strings.ToLower(colorTerm) {
case "24bit":
fallthrough
case "truecolor":
if term == "screen" || !strings.HasPrefix(term, "screen") {
// enable TrueColor in tmux, but not for old-school screen
return TrueColor
}
case "yes":
fallthrough
case "true":
return ANSI256
}
if strings.Contains(term, "256color") {
return ANSI256
}
if strings.Contains(term, "color") {
return ANSI
}
return Ascii
}
func foregroundColor() Color {
s, err := termStatusReport(10)
if err == nil {
c, err := xTermColor(s)
if err == nil {
return c
}
}
colorFGBG := os.Getenv("COLORFGBG")
if strings.Contains(colorFGBG, ";") {
c := strings.Split(colorFGBG, ";")
i, err := strconv.Atoi(c[0])
if err == nil {
return ANSIColor(i)
}
}
// default gray
return ANSIColor(7)
}
func backgroundColor() Color {
s, err := termStatusReport(11)
if err == nil {
c, err := xTermColor(s)
if err == nil {
return c
}
}
colorFGBG := os.Getenv("COLORFGBG")
if strings.Contains(colorFGBG, ";") {
c := strings.Split(colorFGBG, ";")
i, err := strconv.Atoi(c[1])
if err == nil {
return ANSIColor(i)
}
}
// default black
return ANSIColor(0)
}
func readWithTimeout(f *os.File) (string, bool) {
var readfds unix.FdSet
fd := int(f.Fd())
readfds.Set(fd)
for {
// Use select to attempt to read from os.Stdout for 100 ms
_, err := unix.Select(fd+1, &readfds, nil, nil, &unix.Timeval{Usec: 100000})
if err == nil {
break
}
// On MacOS we can see EINTR here if the user
// pressed ^Z. Similar to issue https://github.com/golang/go/issues/22838
if runtime.GOOS == "darwin" && err == unix.EINTR {
continue
}
// log.Printf("select(read error): %v", err)
return "", false
}
if !readfds.IsSet(fd) {
// log.Print("select(read timeout)")
return "", false
}
// n > 0 => is readable
var data []byte
b := make([]byte, 1)
for {
_, err := f.Read(b)
if err != nil {
// log.Printf("read(%d): %v %d", fd, err, n)
return "", false
}
// log.Printf("read %d bytes from stdout: %s %d\n", n, data, data[len(data)-1])
data = append(data, b[0])
if b[0] == '\a' || (b[0] == '\\' && len(data) > 2) {
break
}
}
// fmt.Printf("read %d bytes from stdout: %s\n", n, data)
return string(data), true
}
func termStatusReport(sequence int) (string, error) {
t, err := unix.IoctlGetTermios(unix.Stdout, tcgetattr)
if err != nil {
return "", ErrStatusReport
}
defer unix.IoctlSetTermios(unix.Stdout, tcsetattr, t)
noecho := *t
noecho.Lflag = noecho.Lflag &^ unix.ECHO
noecho.Lflag = noecho.Lflag &^ unix.ICANON
if err := unix.IoctlSetTermios(unix.Stdout, tcsetattr, &noecho); err != nil {
return "", ErrStatusReport
}
fmt.Printf("\033]%d;?\007", sequence)
s, ok := readWithTimeout(os.Stdout)
if !ok {
return "", ErrStatusReport
}
// fmt.Println("Rcvd", s[1:])
return s, nil
}