// +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 }