mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-17 14:04:52 +02:00
Code to query terminal for capabilities
This commit is contained in:
@@ -57,6 +57,10 @@ type Loop struct {
|
||||
style_ctx style.Context
|
||||
atomic_update_active bool
|
||||
pointer_shapes []PointerShape
|
||||
waiting_for_capabilities_response bool
|
||||
|
||||
// Queried capabilities from terminal
|
||||
TerminalCapabilities TerminalCapabilities
|
||||
|
||||
// Suspend the loop restoring terminal state, and run the provided function. When it returns terminal state is
|
||||
// put back to what it was before suspending unless the function returns an error or an error occurs saving/restoring state.
|
||||
@@ -111,6 +115,9 @@ type Loop struct {
|
||||
|
||||
// Called on SIGTERM return true if you wish to handle it yourself
|
||||
OnSIGTERM func() (bool, error)
|
||||
|
||||
// Called when capabilities response is received
|
||||
OnCapabilitiesReceived func(TerminalCapabilities)
|
||||
}
|
||||
|
||||
func New(options ...func(self *Loop)) (*Loop, error) {
|
||||
@@ -538,3 +545,15 @@ func (self *Loop) CurrentPointerShape() (ans PointerShape, has_shape bool) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Query the terminal for various capabilities, the OnCapabilitiesReceived
|
||||
// callback will be called once the query response is received. This
|
||||
// function should be called as early as possible ideally in OnInitialize.
|
||||
func (self *Loop) QueryCapabilities() {
|
||||
if !self.waiting_for_capabilities_response {
|
||||
self.waiting_for_capabilities_response = true
|
||||
self.StartAtomicUpdate()
|
||||
self.QueueWriteString("\x1b[?u\x1b[?996n\x1b[c")
|
||||
self.EndAtomicUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
23
tools/tui/loop/capabilities.go
Normal file
23
tools/tui/loop/capabilities.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package loop
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var _ = fmt.Print
|
||||
|
||||
type ColorPreference uint8
|
||||
|
||||
const (
|
||||
NO_COLOR_PREFERENCE ColorPreference = iota
|
||||
DARK_COLOR_PREFERENCE
|
||||
LIGHT_COLOR_PREFERENCE
|
||||
)
|
||||
|
||||
type TerminalCapabilities struct {
|
||||
KeyboardProtocol bool
|
||||
KeyboardProtocolResponseReceived bool
|
||||
|
||||
ColorPreference ColorPreference
|
||||
ColorPreferenceResponseReceived bool
|
||||
}
|
||||
@@ -6,6 +6,9 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
@@ -40,7 +43,7 @@ func read_ignoring_temporary_errors(f *tty.Term, buf []byte) (int, error) {
|
||||
return n, err
|
||||
}
|
||||
|
||||
func read_from_tty(pipe_r *os.File, term *tty.Term, results_channel chan<- []byte, err_channel chan<- error, quit_channel <-chan byte) {
|
||||
func read_from_tty(pipe_r *os.File, term *tty.Term, results_channel chan<- []byte, err_channel chan<- error, quit_channel <-chan byte, leftover_channel chan<- []byte) {
|
||||
keep_going := true
|
||||
pipe_fd := int(pipe_r.Fd())
|
||||
tty_fd := term.Fd()
|
||||
@@ -94,7 +97,39 @@ func read_from_tty(pipe_r *os.File, term *tty.Term, results_channel chan<- []byt
|
||||
select {
|
||||
case results_channel <- send:
|
||||
case <-quit_channel:
|
||||
leftover_channel <- send
|
||||
keep_going = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func has_da1_response(s string) bool {
|
||||
pat := regexp.MustCompile("\x1b\\[\\?[0-9:;]+c")
|
||||
return pat.FindString(s) != ""
|
||||
}
|
||||
|
||||
func read_until_primary_device_attributes_response(term *tty.Term, initial_bytes []byte, timeout time.Duration) {
|
||||
s := strings.Builder{}
|
||||
if initial_bytes != nil {
|
||||
s.Write(initial_bytes)
|
||||
}
|
||||
received := make(chan error)
|
||||
go func() {
|
||||
buf := make([]byte, 1024)
|
||||
n, err := read_ignoring_temporary_errors(term, buf)
|
||||
if n > 0 {
|
||||
s.Write(buf[:n])
|
||||
if has_da1_response(s.String()) {
|
||||
received <- nil
|
||||
return
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
received <- err
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case <-received:
|
||||
case <-time.After(timeout):
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,6 +124,25 @@ func (self *Loop) handle_csi(raw []byte) (err error) {
|
||||
return self.handle_mouse_event(me)
|
||||
}
|
||||
}
|
||||
if self.waiting_for_capabilities_response {
|
||||
if strings.HasPrefix(csi, "?") && strings.HasSuffix(csi, "c") {
|
||||
self.waiting_for_capabilities_response = false
|
||||
if self.OnCapabilitiesReceived != nil {
|
||||
self.OnCapabilitiesReceived(self.TerminalCapabilities)
|
||||
}
|
||||
} else if strings.HasPrefix(csi, "?997;") && strings.HasSuffix(csi, "n") {
|
||||
switch csi[len(csi)-2] {
|
||||
case '1':
|
||||
self.TerminalCapabilities.ColorPreference = DARK_COLOR_PREFERENCE
|
||||
case '2':
|
||||
self.TerminalCapabilities.ColorPreference = LIGHT_COLOR_PREFERENCE
|
||||
}
|
||||
self.TerminalCapabilities.ColorPreferenceResponseReceived = true
|
||||
} else if strings.HasPrefix(csi, "?") && strings.HasSuffix(csi, "u") {
|
||||
self.TerminalCapabilities.KeyboardProtocol = true
|
||||
self.TerminalCapabilities.KeyboardProtocolResponseReceived = true
|
||||
}
|
||||
}
|
||||
if self.OnEscapeCode != nil {
|
||||
return self.OnEscapeCode(CSI, raw)
|
||||
}
|
||||
@@ -368,6 +387,7 @@ func (self *Loop) run() (err error) {
|
||||
var r_r, r_w, w_r, w_w *os.File
|
||||
var tty_reading_done_channel chan byte
|
||||
var tty_read_channel chan []byte
|
||||
var tty_leftover_read_channel chan []byte
|
||||
|
||||
start_tty_reader := func() (err error) {
|
||||
r_r, r_w, err = os.Pipe()
|
||||
@@ -376,7 +396,8 @@ func (self *Loop) run() (err error) {
|
||||
}
|
||||
tty_read_channel = make(chan []byte)
|
||||
tty_reading_done_channel = make(chan byte)
|
||||
go read_from_tty(r_r, controlling_term, tty_read_channel, err_channel, tty_reading_done_channel)
|
||||
tty_leftover_read_channel = make(chan []byte, 1)
|
||||
go read_from_tty(r_r, controlling_term, tty_read_channel, err_channel, tty_reading_done_channel, tty_leftover_read_channel)
|
||||
return
|
||||
}
|
||||
err = start_tty_reader()
|
||||
@@ -404,6 +425,19 @@ func (self *Loop) run() (err error) {
|
||||
// wait for tty reader to exit cleanly
|
||||
for range tty_read_channel {
|
||||
}
|
||||
if !self.waiting_for_capabilities_response {
|
||||
close(tty_leftover_read_channel)
|
||||
return
|
||||
}
|
||||
var pending_bytes []byte
|
||||
select {
|
||||
case msg, ok := <-tty_leftover_read_channel:
|
||||
if ok {
|
||||
pending_bytes = msg
|
||||
}
|
||||
default:
|
||||
}
|
||||
read_until_primary_device_attributes_response(controlling_term, pending_bytes, 2*time.Second)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
|
||||
Reference in New Issue
Block a user