1package lipgloss
2
3import (
4 "fmt"
5 "image/color"
6 "os"
7 "runtime"
8
9 "github.com/charmbracelet/x/term"
10)
11
12func backgroundColor(in term.File, out term.File) (color.Color, error) {
13 state, err := term.MakeRaw(in.Fd())
14 if err != nil {
15 return nil, fmt.Errorf("error setting raw state to detect background color: %w", err)
16 }
17
18 defer term.Restore(in.Fd(), state) //nolint:errcheck
19
20 bg, err := queryBackgroundColor(in, out)
21 if err != nil {
22 return nil, err
23 }
24
25 return bg, nil
26}
27
28// BackgroundColor queries the terminal's background color. Typically, you'll
29// want to query against stdin and either stdout or stderr, depending on what
30// you're writing to.
31//
32// This function is intended for standalone Lip Gloss use only. If you're using
33// Bubble Tea, listen for tea.BackgroundColorMsg in your update function.
34func BackgroundColor(in term.File, out term.File) (bg color.Color, err error) {
35 if runtime.GOOS == "windows" { //nolint:nestif
36 // On Windows, when the input/output is redirected or piped, we need to
37 // open the console explicitly.
38 // See https://learn.microsoft.com/en-us/windows/console/getstdhandle#remarks
39 if !term.IsTerminal(in.Fd()) {
40 f, err := os.OpenFile("CONIN$", os.O_RDWR, 0o644) //nolint:gosec
41 if err != nil {
42 return nil, fmt.Errorf("error opening CONIN$: %w", err)
43 }
44 in = f
45 }
46 if !term.IsTerminal(out.Fd()) {
47 f, err := os.OpenFile("CONOUT$", os.O_RDWR, 0o644) //nolint:gosec
48 if err != nil {
49 return nil, fmt.Errorf("error opening CONOUT$: %w", err)
50 }
51 out = f
52 }
53 return backgroundColor(in, out)
54 }
55
56 // NOTE: On Unix, one of the given files must be a tty.
57 for _, f := range []term.File{in, out} {
58 if bg, err = backgroundColor(f, f); err == nil {
59 return bg, nil
60 }
61 }
62
63 return
64}
65
66// HasDarkBackground detects whether the terminal has a light or dark
67// background.
68//
69// Typically, you'll want to query against stdin and either stdout or stderr
70// depending on what you're writing to.
71//
72// hasDarkBG := HasDarkBackground(os.Stdin, os.Stdout)
73// lightDark := LightDark(hasDarkBG)
74// myHotColor := lightDark("#ff0000", "#0000ff")
75//
76// This is intended for use in standalone Lip Gloss only. In Bubble Tea, listen
77// for tea.BackgroundColorMsg in your Update function.
78//
79// case tea.BackgroundColorMsg:
80// hasDarkBackground = msg.IsDark()
81//
82// By default, this function will return true if it encounters an error.
83func HasDarkBackground(in term.File, out term.File) bool {
84 bg, err := BackgroundColor(in, out)
85 if err != nil || bg == nil {
86 return true
87 }
88 return isDarkColor(bg)
89}