okami.mom/albedo

git clone https://git.nkp.pet/okami.mom/albedo

Aly Raffauffix(tui): report terminal restoration failures during color detection9c745c5

main
1.9 KiB76 linesraw
1//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd
2
3package tui
4
5import (
6	"fmt"
7	"os"
8	"strconv"
9	"strings"
10	"time"
11
12	"github.com/charmbracelet/x/term"
13	"golang.org/x/sys/unix"
14)
15
16// queryColors asks for the text and background colors and the palette entries the theme mixes from, then asks
17// for the device attributes every terminal answers, so a terminal that
18// ignores the color queries ends the read instead of stalling it.
19func queryColors() (replyText string, restoreErr error) {
20	tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
21	if err != nil {
22		return "", nil
23	}
24	defer tty.Close()
25	// Query failures use default colors; restoration failures must reach startup.
26	fd := tty.Fd()
27	state, err := term.MakeRaw(fd)
28	if err != nil {
29		return "", nil
30	}
31	defer func() {
32		if err := term.Restore(fd, state); err != nil {
33			restoreErr = fmt.Errorf("could not restore terminal settings after color detection: %w", err)
34		}
35	}()
36	var query strings.Builder
37	query.WriteString("\x1b]10;?\x1b\\\x1b]11;?\x1b\\")
38	for _, i := range queriedPalette {
39		query.WriteString("\x1b]4;")
40		query.WriteString(strconv.Itoa(i))
41		query.WriteString(";?\x1b\\")
42	}
43	query.WriteString("\x1b[c")
44	if _, err := tty.WriteString(query.String()); err != nil {
45		return "", nil
46	}
47	var reply strings.Builder
48	deadline := time.Now().Add(300 * time.Millisecond)
49	buf := make([]byte, 256)
50	for {
51		left := time.Until(deadline)
52		if left <= 0 {
53			break
54		}
55		tv := unix.NsecToTimeval(int64(left))
56		var ready unix.FdSet
57		ready.Set(int(fd))
58		n, err := unix.Select(int(fd)+1, &ready, nil, nil, &tv)
59		if err == unix.EINTR {
60			continue
61		}
62		if err != nil || n == 0 {
63			break
64		}
65		n, err = tty.Read(buf)
66		if err != nil {
67			break
68		}
69		reply.Write(buf[:n])
70		// The device-attributes answer every terminal sends closes the read.
71		if da := strings.Index(reply.String(), "\x1b[?"); da >= 0 && strings.Contains(reply.String()[da:], "c") {
72			break
73		}
74	}
75	return reply.String(), nil
76}