| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- package term
- import (
- "image/color"
- "io"
- "time"
- "github.com/charmbracelet/x/ansi"
- "github.com/charmbracelet/x/input"
- )
- // File represents a file that has a file descriptor and can be read from,
- // written to, and closed.
- type File interface {
- io.ReadWriteCloser
- Fd() uintptr
- }
- // QueryBackgroundColor queries the terminal for the background color.
- // If the terminal does not support querying the background color, nil is
- // returned.
- //
- // Note: you will need to set the input to raw mode before calling this
- // function.
- //
- // state, _ := term.MakeRaw(in.Fd())
- // defer term.Restore(in.Fd(), state)
- 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:
- return false
- }
- }
- return true
- }, ansi.RequestBackgroundColor+ansi.RequestPrimaryDeviceAttributes)
- return
- }
- // QueryKittyKeyboard returns the enabled Kitty keyboard protocol options.
- // -1 means the terminal does not support the feature.
- //
- // Note: you will need to set the input to raw mode before calling this
- // function.
- //
- // state, _ := term.MakeRaw(in.Fd())
- // defer term.Restore(in.Fd(), state)
- func QueryKittyKeyboard(in io.Reader, out io.Writer) (flags int, err error) {
- flags = -1
- // nolint: errcheck
- err = QueryTerminal(in, out, defaultQueryTimeout,
- func(events []input.Event) bool {
- for _, e := range events {
- switch event := e.(type) {
- case input.KittyKeyboardEvent:
- flags = int(event)
- continue // we need to consume the next DA1 event
- case input.PrimaryDeviceAttributesEvent:
- return false
- }
- }
- return true
- }, ansi.RequestKittyKeyboard+ansi.RequestPrimaryDeviceAttributes)
- return
- }
- const defaultQueryTimeout = time.Second * 2
- // 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
- // QueryTerminal queries the terminal for support of various features and
- // returns a list of response events.
- // Most of the time, you will need to set stdin to raw mode before calling this
- // function.
- // Note: This function will block until the terminal responds or the timeout
- // is reached.
- func QueryTerminal(
- in io.Reader,
- out io.Writer,
- timeout time.Duration,
- filter QueryTerminalFilter,
- query string,
- ) error {
- rd, err := input.NewDriver(in, "", 0)
- if err != nil {
- return err
- }
- defer rd.Close() // nolint: errcheck
- done := make(chan struct{}, 1)
- defer close(done)
- go func() {
- select {
- case <-done:
- case <-time.After(timeout):
- rd.Cancel()
- }
- }()
- if _, err := io.WriteString(out, query); err != nil {
- return err
- }
- for {
- events, err := rd.ReadEvents()
- if err != nil {
- return err
- }
- if !filter(events) {
- break
- }
- }
- return nil
- }
|