Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: manually query terminal for background color #429

Merged
merged 1 commit into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@ require (
github.com/charmbracelet/colorprofile v0.1.2
github.com/charmbracelet/x/ansi v0.4.2
github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a
github.com/charmbracelet/x/input v0.2.0
github.com/charmbracelet/x/term v0.2.0
github.com/lucasb-eyer/go-colorful v1.2.0
github.com/muesli/cancelreader v0.2.2
github.com/rivo/uniseg v0.4.7
golang.org/x/sys v0.24.0
)

require (
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
)
6 changes: 1 addition & 5 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,8 @@ github.com/charmbracelet/x/ansi v0.4.2 h1:0JM6Aj/g/KC154/gOP4vfxun0ff6itogDYk41k
github.com/charmbracelet/x/ansi v0.4.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a h1:G99klV19u0QnhiizODirwVksQB91TJKV/UaTnACcG30=
github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
github.com/charmbracelet/x/input v0.2.0 h1:1Sv+y/flcqUfUH2PXNIDKDIdT2G8smOnGOgawqhwy8A=
github.com/charmbracelet/x/input v0.2.0/go.mod h1:KUSFIS6uQymtnr5lHVSOK9j8RvwTD4YHnWnzJUYnd/M=
github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0=
github.com/charmbracelet/x/term v0.2.0/go.mod h1:GVxgxAbjUrmpvIINHIQnJJKpMlHiZ4cktEQCN6GWyF0=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
Expand All @@ -21,6 +17,6 @@ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
87 changes: 72 additions & 15 deletions terminal.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
"fmt"
"image/color"
"io"
"strconv"
"strings"
"time"

"github.com/charmbracelet/x/ansi"
"github.com/charmbracelet/x/input"
"github.com/charmbracelet/x/ansi/parser"
"github.com/muesli/cancelreader"
)

// queryBackgroundColor queries the terminal for the background color.
Expand All @@ -24,13 +27,20 @@
func queryBackgroundColor(in io.Reader, out io.Writer) (c color.Color, err error) {
//nolint: errcheck
err = queryTerminal(in, out, defaultQueryTimeout,
func(events []input.Event) bool {
for _, e := range events {
switch e := e.(type) {
case input.BackgroundColorEvent:
c = e.Color
continue // we need to consume the next DA1 event
case input.PrimaryDeviceAttributesEvent:
func(seq string, pa *ansi.Parser) bool {
switch {
case ansi.HasOscPrefix(seq):
switch pa.Cmd {
case 11: // OSC 11

Check failure on line 34 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (ubuntu-latest)

Magic number: 11, in <case> detected (mnd)

Check failure on line 34 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (macos-latest)

Magic number: 11, in <case> detected (mnd)

Check failure on line 34 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (windows-latest)

Magic number: 11, in <case> detected (mnd)

Check failure on line 34 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (ubuntu-latest)

Magic number: 11, in <case> detected (mnd)

Check failure on line 34 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (macos-latest)

Magic number: 11, in <case> detected (mnd)

Check failure on line 34 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (windows-latest)

Magic number: 11, in <case> detected (mnd)
parts := strings.Split(string(pa.Data[:pa.DataLen]), ";")
if len(parts) != 2 {

Check failure on line 36 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (ubuntu-latest)

Magic number: 2, in <condition> detected (mnd)

Check failure on line 36 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (macos-latest)

Magic number: 2, in <condition> detected (mnd)

Check failure on line 36 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (windows-latest)

Magic number: 2, in <condition> detected (mnd)

Check failure on line 36 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (ubuntu-latest)

Magic number: 2, in <condition> detected (mnd)

Check failure on line 36 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (macos-latest)

Magic number: 2, in <condition> detected (mnd)

Check failure on line 36 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (windows-latest)

Magic number: 2, in <condition> detected (mnd)
break // invalid, but we still need to parse the next sequence
}
c = xParseColor(parts[1])
}
case ansi.HasCsiPrefix(seq):
switch pa.Cmd {
case 'c' | '?'<<parser.MarkerShift: // DA1
return false
}
}
Expand All @@ -44,7 +54,7 @@
// queryTerminalFilter is a function that filters input events using a type
// switch. If false is returned, the QueryTerminal function will stop reading
// input.
type queryTerminalFilter func(events []input.Event) bool
type queryTerminalFilter func(seq string, pa *ansi.Parser) bool

// queryTerminal queries the terminal for support of various features and
// returns a list of response events.
Expand All @@ -60,9 +70,9 @@
filter queryTerminalFilter,
query string,
) error {
rd, err := input.NewDriver(in, "", 0)
rd, err := cancelreader.NewReader(in)
if err != nil {
return fmt.Errorf("could not create driver: %w", err)
return fmt.Errorf("could not create cancel reader: %w", err)
}

defer rd.Close() //nolint: errcheck
Expand All @@ -81,16 +91,63 @@
return fmt.Errorf("could not write query: %w", err)
}

pa := ansi.GetParser()
defer ansi.PutParser(pa)

var buf [256]byte // 256 bytes should be enough for most responses
for {
events, err := rd.ReadEvents()
n, err := rd.Read(buf[:])
if err != nil {
return fmt.Errorf("could not read events: %s", err)
return fmt.Errorf("could not read from input: %w", err)
}

if !filter(events) {
break
var state byte
p := buf[:]
for n > 0 {
seq, _, read, newState := ansi.DecodeSequence(p[:n], state, pa)
if !filter(string(seq), pa) {
return nil
}

state = newState
n -= read
p = p[read:]
}
}
}

func shift(x uint64) uint64 {
if x > 0xff {

Check failure on line 120 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (ubuntu-latest)

Magic number: 0xff, in <condition> detected (mnd)

Check failure on line 120 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (macos-latest)

Magic number: 0xff, in <condition> detected (mnd)

Check failure on line 120 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (windows-latest)

Magic number: 0xff, in <condition> detected (mnd)

Check failure on line 120 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (ubuntu-latest)

Magic number: 0xff, in <condition> detected (mnd)

Check failure on line 120 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (macos-latest)

Magic number: 0xff, in <condition> detected (mnd)

Check failure on line 120 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (windows-latest)

Magic number: 0xff, in <condition> detected (mnd)
x >>= 8
}
return x
}

func xParseColor(s string) color.Color {
switch {
case strings.HasPrefix(s, "rgb:"):
parts := strings.Split(s[4:], "/")
if len(parts) != 3 {

Check failure on line 130 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (ubuntu-latest)

Magic number: 3, in <condition> detected (mnd)

Check failure on line 130 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (macos-latest)

Magic number: 3, in <condition> detected (mnd)

Check failure on line 130 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (windows-latest)

Magic number: 3, in <condition> detected (mnd)

Check failure on line 130 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (ubuntu-latest)

Magic number: 3, in <condition> detected (mnd)

Check failure on line 130 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (macos-latest)

Magic number: 3, in <condition> detected (mnd)

Check failure on line 130 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (windows-latest)

Magic number: 3, in <condition> detected (mnd)
return color.Black
}

r, _ := strconv.ParseUint(parts[0], 16, 32)
g, _ := strconv.ParseUint(parts[1], 16, 32)
b, _ := strconv.ParseUint(parts[2], 16, 32)

return color.RGBA{uint8(shift(r)), uint8(shift(g)), uint8(shift(b)), 255} //nolint:gosec
case strings.HasPrefix(s, "rgba:"):
parts := strings.Split(s[5:], "/")
if len(parts) != 4 {

Check failure on line 141 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (ubuntu-latest)

Magic number: 4, in <condition> detected (mnd)

Check failure on line 141 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (macos-latest)

Magic number: 4, in <condition> detected (mnd)

Check failure on line 141 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (windows-latest)

Magic number: 4, in <condition> detected (mnd)

Check failure on line 141 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (ubuntu-latest)

Magic number: 4, in <condition> detected (mnd)

Check failure on line 141 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (macos-latest)

Magic number: 4, in <condition> detected (mnd)

Check failure on line 141 in terminal.go

View workflow job for this annotation

GitHub Actions / lint / lint-soft (windows-latest)

Magic number: 4, in <condition> detected (mnd)
return color.Black
}

r, _ := strconv.ParseUint(parts[0], 16, 32)
g, _ := strconv.ParseUint(parts[1], 16, 32)
b, _ := strconv.ParseUint(parts[2], 16, 32)
a, _ := strconv.ParseUint(parts[3], 16, 32)

return color.RGBA{uint8(shift(r)), uint8(shift(g)), uint8(shift(b)), uint8(shift(a))} //nolint:gosec
}
return nil
}
Loading