Kitty graphics probe that works on macOS
Go cannot set a read deadline on a terminal device on macOS (kqueue does not support them), so the probe failed at once and Ghostty and kitty got block posters there. Wait for the answer with select(2) instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019reK4DUGYqDUjniWYJvwUW
This commit is contained in:
@@ -6,6 +6,7 @@ require (
|
|||||||
github.com/gdamore/tcell/v2 v2.13.10
|
github.com/gdamore/tcell/v2 v2.13.10
|
||||||
github.com/rivo/tview v0.42.0
|
github.com/rivo/tview v0.42.0
|
||||||
golang.org/x/image v0.47.0
|
golang.org/x/image v0.47.0
|
||||||
|
golang.org/x/sys v0.49.0
|
||||||
golang.org/x/term v0.37.0
|
golang.org/x/term v0.37.0
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -13,6 +14,5 @@ require (
|
|||||||
github.com/gdamore/encoding v1.0.1 // indirect
|
github.com/gdamore/encoding v1.0.1 // indirect
|
||||||
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
golang.org/x/sys v0.49.0 // indirect
|
|
||||||
golang.org/x/text v0.43.0 // indirect
|
golang.org/x/text v0.43.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ package ui
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,13 +38,37 @@ func probeKittyGraphics(timeout time.Duration) bool {
|
|||||||
// A terminal that does not know APC sequences may print the query; erase it.
|
// A terminal that does not know APC sequences may print the query; erase it.
|
||||||
defer tty.WriteString("\r\x1b[2K")
|
defer tty.WriteString("\r\x1b[2K")
|
||||||
|
|
||||||
if err := tty.SetReadDeadline(time.Now().Add(timeout)); err != nil {
|
// The wait uses select(2): a read deadline does not work on a terminal
|
||||||
return false
|
// device on macOS (kqueue does not support them), which made the probe
|
||||||
}
|
// give up at once there.
|
||||||
|
deadline := time.Now().Add(timeout)
|
||||||
var got []byte
|
var got []byte
|
||||||
buf := make([]byte, 256)
|
buf := make([]byte, 256)
|
||||||
for {
|
for {
|
||||||
n, err := tty.Read(buf)
|
left := time.Until(deadline)
|
||||||
|
if left <= 0 {
|
||||||
|
return false // no answer in time: assume no support
|
||||||
|
}
|
||||||
|
var fds unix.FdSet
|
||||||
|
fds.Set(fd)
|
||||||
|
tv := unix.NsecToTimeval(left.Nanoseconds())
|
||||||
|
n, err := unix.Select(fd+1, &fds, nil, nil, &tv)
|
||||||
|
if errors.Is(err, unix.EINTR) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if n == 0 {
|
||||||
|
continue // timed out; the loop ends above
|
||||||
|
}
|
||||||
|
n, err = unix.Read(fd, buf)
|
||||||
|
if errors.Is(err, unix.EINTR) || errors.Is(err, unix.EAGAIN) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil || n <= 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
got = append(got, buf[:n]...)
|
got = append(got, buf[:n]...)
|
||||||
if bytes.Contains(got, []byte("_Gi=31;OK")) {
|
if bytes.Contains(got, []byte("_Gi=31;OK")) {
|
||||||
return true
|
return true
|
||||||
@@ -50,8 +76,5 @@ func probeKittyGraphics(timeout time.Duration) bool {
|
|||||||
if da := bytes.Index(got, []byte("\x1b[?")); da >= 0 && bytes.IndexByte(got[da:], 'c') >= 0 {
|
if da := bytes.Index(got, []byte("\x1b[?")); da >= 0 && bytes.IndexByte(got[da:], 'c') >= 0 {
|
||||||
return false // device attributes answered, graphics query not
|
return false // device attributes answered, graphics query not
|
||||||
}
|
}
|
||||||
if err != nil {
|
|
||||||
return false // timeout or error: assume no support
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user