mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-25 17:52:02 +02:00
Fix some responses from terminal sometimes leaking into shell on after kitten exit
Always do a roundtrip at kitten exit, except for special purpose kittens. This slows down exit by one round trip time (capped at 2 seconds), however it ensures that we never get terminal response leak. Fixes #9839
This commit is contained in:
@@ -237,6 +237,8 @@ Detailed list of changes
|
|||||||
- A new option :opt:`macos_fullscreen_ignore_safe_area_insets` to control
|
- A new option :opt:`macos_fullscreen_ignore_safe_area_insets` to control
|
||||||
whether to ignore the notch space when using :opt:`macos_traditional_fullscreen` (:pull:`9841`)
|
whether to ignore the notch space when using :opt:`macos_traditional_fullscreen` (:pull:`9841`)
|
||||||
|
|
||||||
|
- Fix some responses from terminal sometimes leaking into shell on after kitten exit (:iss:`9839`)
|
||||||
|
|
||||||
0.46.2 [2026-03-21]
|
0.46.2 [2026-03-21]
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ func run_plain_text_loop(opts *Options) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
lp.NoRoundtripToTerminalOnExit = true
|
||||||
dest := "c"
|
dest := "c"
|
||||||
if opts.UsePrimary {
|
if opts.UsePrimary {
|
||||||
dest = "p"
|
dest = "p"
|
||||||
|
|||||||
@@ -286,10 +286,11 @@ func parse_aliases(raw []string) (map[string][]string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func run_get_loop(opts *Options, args []string) (err error) {
|
func run_get_loop(opts *Options, args []string) (err error) {
|
||||||
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking, loop.NoInBandResizeNotifications)
|
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking, loop.NoInBandResizeNotifications, loop.NoFocusTracking)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
lp.NoRoundtripToTerminalOnExit = true
|
||||||
var available_mimes []string
|
var available_mimes []string
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
var getting_data_for string
|
var getting_data_for string
|
||||||
|
|||||||
@@ -43,10 +43,11 @@ func (self *Input) has_mime_matching(predicate func(string) bool) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func write_loop(inputs []*Input, opts *Options) (err error) {
|
func write_loop(inputs []*Input, opts *Options) (err error) {
|
||||||
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking, loop.NoInBandResizeNotifications)
|
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking, loop.NoInBandResizeNotifications, loop.NoFocusTracking)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
lp.NoRoundtripToTerminalOnExit = true
|
||||||
var waiting_for_write loop.IdType
|
var waiting_for_write loop.IdType
|
||||||
var buf [4096]byte
|
var buf [4096]byte
|
||||||
aliases, aerr := parse_aliases(opts.Alias)
|
aliases, aerr := parse_aliases(opts.Alias)
|
||||||
|
|||||||
@@ -21,11 +21,12 @@ func DetectSupport(timeout time.Duration) (memory, files, direct bool, err error
|
|||||||
temp_files_to_delete := make([]string, 0, 8)
|
temp_files_to_delete := make([]string, 0, 8)
|
||||||
shm_files_to_delete := make([]shm.MMap, 0, 8)
|
shm_files_to_delete := make([]shm.MMap, 0, 8)
|
||||||
var direct_query_id, file_query_id, memory_query_id uint32
|
var direct_query_id, file_query_id, memory_query_id uint32
|
||||||
lp, e := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking, loop.NoInBandResizeNotifications)
|
lp, e := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking, loop.NoInBandResizeNotifications, loop.NoFocusTracking)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
err = e
|
err = e
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
lp.NoRoundtripToTerminalOnExit = true
|
||||||
print_error := func(format string, args ...any) {
|
print_error := func(format string, args ...any) {
|
||||||
lp.Println(fmt.Sprintf(format, args...))
|
lp.Println(fmt.Sprintf(format, args...))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,10 +112,11 @@ func (p *parsed_data) generate_chunks(callback func(string)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *parsed_data) run_loop() (err error) {
|
func (p *parsed_data) run_loop() (err error) {
|
||||||
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking, loop.NoInBandResizeNotifications)
|
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking, loop.NoInBandResizeNotifications, loop.NoFocusTracking)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
lp.NoRoundtripToTerminalOnExit = true
|
||||||
activated := -1
|
activated := -1
|
||||||
prefix := ESC_CODE_PREFIX + "i=" + p.identifier
|
prefix := ESC_CODE_PREFIX + "i=" + p.identifier
|
||||||
|
|
||||||
|
|||||||
@@ -26,10 +26,11 @@ func main(cmd *cli.Command, opts *Options, args []string) (rc int, err error) {
|
|||||||
queries[i] = x
|
queries[i] = x
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lp, err := loop.New(loop.NoAlternateScreen, loop.NoKeyboardStateChange, loop.NoMouseTracking, loop.NoRestoreColors, loop.NoInBandResizeNotifications)
|
lp, err := loop.New(loop.NoAlternateScreen, loop.NoKeyboardStateChange, loop.NoMouseTracking, loop.NoRestoreColors, loop.NoInBandResizeNotifications, loop.NoFocusTracking)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 1, err
|
return 1, err
|
||||||
}
|
}
|
||||||
|
lp.NoRoundtripToTerminalOnExit = true
|
||||||
timed_out := false
|
timed_out := false
|
||||||
lp.OnInitialize = func() (string, error) {
|
lp.OnInitialize = func() (string, error) {
|
||||||
lp.QueryTerminal(queries...)
|
lp.QueryTerminal(queries...)
|
||||||
|
|||||||
@@ -145,7 +145,12 @@ class Callbacks:
|
|||||||
self.bell_count += 1
|
self.bell_count += 1
|
||||||
|
|
||||||
def on_da1(self) -> None:
|
def on_da1(self) -> None:
|
||||||
payload = da1(get_options())
|
opts = None
|
||||||
|
with suppress(RuntimeError):
|
||||||
|
opts = get_options()
|
||||||
|
if opts is None:
|
||||||
|
opts = defaults
|
||||||
|
payload = da1(opts)
|
||||||
self.da1.append(payload)
|
self.da1.append(payload)
|
||||||
if self.pty and self.pty.needs_da1:
|
if self.pty and self.pty.needs_da1:
|
||||||
self.pty.send_da1_response(payload)
|
self.pty.send_da1_response(payload)
|
||||||
@@ -331,7 +336,7 @@ class PTY:
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, argv=None, rows=25, columns=80, scrollback=100, cell_width=10, cell_height=20,
|
self, argv=None, rows=25, columns=80, scrollback=100, cell_width=10, cell_height=20,
|
||||||
cwd=None, env=None, stdin_fd=None, stdout_fd=None, needs_da1=False,
|
cwd=None, env=None, stdin_fd=None, stdout_fd=None, needs_da1=True,
|
||||||
):
|
):
|
||||||
self.is_child = False
|
self.is_child = False
|
||||||
if isinstance(argv, str):
|
if isinstance(argv, str):
|
||||||
|
|||||||
@@ -32,13 +32,14 @@ func do_chunked_io(io_data *rc_io_data) (serialized_response []byte, err error)
|
|||||||
// arrives, leading to the notification being sent to whatever is executed
|
// arrives, leading to the notification being sent to whatever is executed
|
||||||
// after us. Similarly no focus tracking.
|
// after us. Similarly no focus tracking.
|
||||||
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoInBandResizeNotifications, loop.NoFocusTracking)
|
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoInBandResizeNotifications, loop.NoFocusTracking)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
if io_data.on_key_event != nil {
|
if io_data.on_key_event != nil {
|
||||||
lp.FullKeyboardProtocol()
|
lp.FullKeyboardProtocol()
|
||||||
} else {
|
} else {
|
||||||
lp.NoKeyboardStateChange()
|
lp.NoKeyboardStateChange()
|
||||||
}
|
lp.NoRoundtripToTerminalOnExit = true
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -58,11 +58,17 @@ type Loop struct {
|
|||||||
style_ctx style.Context
|
style_ctx style.Context
|
||||||
atomic_update_active bool
|
atomic_update_active bool
|
||||||
pointer_shapes []PointerShape
|
pointer_shapes []PointerShape
|
||||||
waiting_for_capabilities_response bool
|
|
||||||
|
|
||||||
// Queried capabilities from terminal
|
// Queried capabilities from terminal
|
||||||
TerminalCapabilities TerminalCapabilities
|
TerminalCapabilities TerminalCapabilities
|
||||||
|
|
||||||
|
// Set this to true to avoid doing a query response loop to the terminal at
|
||||||
|
// exit. This loop is needed for most kittens to ensure that in-flight
|
||||||
|
// responses such as in-band resize notifications, color queries, kitty
|
||||||
|
// keyboard events, etc. are not leaked to the shell. For some special
|
||||||
|
// purpose uses of the loop, this is not appropriate, hence this setting.
|
||||||
|
NoRoundtripToTerminalOnExit bool
|
||||||
|
|
||||||
// Suspend the loop restoring terminal state, and run the provided function. When it returns terminal state is
|
// 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.
|
// put back to what it was before suspending unless the function returns an error or an error occurs saving/restoring state.
|
||||||
SuspendAndRun func(func() error) error
|
SuspendAndRun func(func() error) error
|
||||||
@@ -333,7 +339,7 @@ func (self *Loop) Run() (err error) {
|
|||||||
os.Stderr.WriteString(err.Error())
|
os.Stderr.WriteString(err.Error())
|
||||||
os.Stderr.WriteString("\n")
|
os.Stderr.WriteString("\n")
|
||||||
if is_terminal {
|
if is_terminal {
|
||||||
if term, err := tty.OpenControllingTerm(tty.SetRaw); err == nil {
|
if term, err := tty.OpenControllingTerm(tty.SetRaw); err != nil {
|
||||||
defer term.RestoreAndClose()
|
defer term.RestoreAndClose()
|
||||||
term.DebugPrintln(err.Error())
|
term.DebugPrintln(err.Error())
|
||||||
fmt.Println("Press any key to exit.\r")
|
fmt.Println("Press any key to exit.\r")
|
||||||
@@ -568,12 +574,7 @@ func (self *Loop) CurrentPointerShape() (ans PointerShape, has_shape bool) {
|
|||||||
// callback will be called once the query response is received. This
|
// callback will be called once the query response is received. This
|
||||||
// function should be called as early as possible ideally in OnInitialize.
|
// function should be called as early as possible ideally in OnInitialize.
|
||||||
func (self *Loop) QueryCapabilities() {
|
func (self *Loop) QueryCapabilities() {
|
||||||
if !self.waiting_for_capabilities_response {
|
self.QueueWriteString("\x1b[?u\x1b[?996n\x1b[c")
|
||||||
self.waiting_for_capabilities_response = true
|
|
||||||
self.StartAtomicUpdate()
|
|
||||||
self.QueueWriteString("\x1b[?u\x1b[?996n\x1b[c")
|
|
||||||
self.EndAtomicUpdate()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Alignment int
|
type Alignment int
|
||||||
|
|||||||
@@ -115,11 +115,17 @@ func has_da1_response(s string) bool {
|
|||||||
return pat.FindString(s) != ""
|
return pat.FindString(s) != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func read_until_primary_device_attributes_response(term *tty.Term, initial_bytes []byte, timeout time.Duration) {
|
func do_roundtrip_to_terminal(term *tty.Term, timeout time.Duration) {
|
||||||
s := strings.Builder{}
|
// ask for primary device attributes
|
||||||
if initial_bytes != nil {
|
for {
|
||||||
s.Write(initial_bytes)
|
if err := term.WriteAllString("\x1b[c"); err != nil && !is_temporary_error(err) {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
s := strings.Builder{}
|
||||||
|
s.Grow(256)
|
||||||
received := make(chan error)
|
received := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -128,17 +134,19 @@ func read_until_primary_device_attributes_response(term *tty.Term, initial_bytes
|
|||||||
received <- fmt.Errorf("%s", text)
|
received <- fmt.Errorf("%s", text)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
buf := make([]byte, 1024)
|
for {
|
||||||
n, err := read_ignoring_temporary_errors(term, buf)
|
buf := make([]byte, 1024)
|
||||||
if n > 0 {
|
n, err := read_ignoring_temporary_errors(term, buf)
|
||||||
s.Write(buf[:n])
|
if n > 0 {
|
||||||
if has_da1_response(s.String()) {
|
s.Write(buf[:n])
|
||||||
received <- nil
|
if has_da1_response(s.String()) {
|
||||||
return
|
received <- nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
received <- err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
received <- err
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
select {
|
select {
|
||||||
|
|||||||
@@ -87,38 +87,72 @@ func (self *Loop) update_screen_size() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *Loop) handle_csi(raw []byte) (err error) {
|
func (self *Loop) handle_csi(raw []byte) (err error) {
|
||||||
|
if len(raw) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
csi := string(raw)
|
csi := string(raw)
|
||||||
if strings.HasSuffix(csi, "t") && strings.HasPrefix(csi, "48;") {
|
switch raw[len(raw)-1] {
|
||||||
if parts := strings.Split(csi[3:len(csi)-1], ";"); len(parts) > 3 {
|
case 't':
|
||||||
var parsed [4]int
|
if strings.HasSuffix(csi, "t") {
|
||||||
ok := true
|
if parts := strings.Split(csi[3:len(csi)-1], ";"); len(parts) > 3 {
|
||||||
for i, x := range parts {
|
var parsed [4]int
|
||||||
x, _, _ = strings.Cut(x, ":")
|
ok := true
|
||||||
if parsed[i], err = strconv.Atoi(x); err != nil {
|
for i, x := range parts {
|
||||||
ok = false
|
x, _, _ = strings.Cut(x, ":")
|
||||||
break
|
if parsed[i], err = strconv.Atoi(x); err != nil {
|
||||||
|
ok = false
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if ok {
|
||||||
if ok {
|
self.seen_inband_resize = true
|
||||||
self.seen_inband_resize = true
|
old_size := self.screen_size
|
||||||
old_size := self.screen_size
|
s := &self.screen_size
|
||||||
s := &self.screen_size
|
s.updated = true
|
||||||
s.updated = true
|
s.HeightCells, s.WidthCells = uint(parsed[0]), uint(parsed[1])
|
||||||
s.HeightCells, s.WidthCells = uint(parsed[0]), uint(parsed[1])
|
s.HeightPx, s.WidthPx = uint(parsed[2]), uint(parsed[3])
|
||||||
s.HeightPx, s.WidthPx = uint(parsed[2]), uint(parsed[3])
|
s.CellWidth = s.WidthPx / s.WidthCells
|
||||||
s.CellWidth = s.WidthPx / s.WidthCells
|
s.CellHeight = s.HeightPx / s.HeightCells
|
||||||
s.CellHeight = s.HeightPx / s.HeightCells
|
if self.OnResize != nil {
|
||||||
if self.OnResize != nil {
|
return self.OnResize(old_size, self.screen_size)
|
||||||
return self.OnResize(old_size, self.screen_size)
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if csi == "I" || csi == "O" {
|
case 'I', 'O':
|
||||||
if self.OnFocusChange != nil {
|
if len(csi) == 1 {
|
||||||
return self.OnFocusChange(csi == "I")
|
if self.OnFocusChange != nil {
|
||||||
|
return self.OnFocusChange(csi == "I")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
case 'c':
|
||||||
|
if strings.HasPrefix(csi, "?") {
|
||||||
|
if self.OnCapabilitiesReceived != nil {
|
||||||
|
if err = self.OnCapabilitiesReceived(self.TerminalCapabilities); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 'u':
|
||||||
|
if strings.HasPrefix(csi, "?") {
|
||||||
|
self.TerminalCapabilities.KeyboardProtocol = true
|
||||||
|
self.TerminalCapabilities.KeyboardProtocolResponseReceived = true
|
||||||
|
}
|
||||||
|
case 'n':
|
||||||
|
if strings.HasPrefix(csi, "?997;") {
|
||||||
|
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
|
||||||
|
if self.OnColorSchemeChange != nil {
|
||||||
|
return self.OnColorSchemeChange(self.TerminalCapabilities.ColorPreference)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ke := KeyEventFromCSI(csi)
|
ke := KeyEventFromCSI(csi)
|
||||||
if ke != nil {
|
if ke != nil {
|
||||||
@@ -131,38 +165,6 @@ func (self *Loop) handle_csi(raw []byte) (err error) {
|
|||||||
return self.handle_mouse_event(me)
|
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 {
|
|
||||||
if err = self.OnCapabilitiesReceived(self.TerminalCapabilities); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} 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
|
|
||||||
}
|
|
||||||
} else if self.terminal_options.color_scheme_change_notification && 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
|
|
||||||
if self.OnColorSchemeChange != nil {
|
|
||||||
return self.OnColorSchemeChange(self.TerminalCapabilities.ColorPreference)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if self.OnEscapeCode != nil {
|
if self.OnEscapeCode != nil {
|
||||||
return self.OnEscapeCode(CSI, raw)
|
return self.OnEscapeCode(CSI, raw)
|
||||||
}
|
}
|
||||||
@@ -446,19 +448,16 @@ func (self *Loop) run() (err error) {
|
|||||||
// wait for tty reader to exit cleanly
|
// wait for tty reader to exit cleanly
|
||||||
for range tty_read_channel {
|
for range tty_read_channel {
|
||||||
}
|
}
|
||||||
if !self.waiting_for_capabilities_response {
|
|
||||||
close(tty_leftover_read_channel)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var pending_bytes []byte
|
|
||||||
select {
|
select {
|
||||||
case msg, ok := <-tty_leftover_read_channel:
|
case <-tty_leftover_read_channel:
|
||||||
if ok {
|
|
||||||
pending_bytes = msg
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
read_until_primary_device_attributes_response(controlling_term, pending_bytes, 2*time.Second)
|
if !self.NoRoundtripToTerminalOnExit {
|
||||||
|
// ensure that any terminal responses such as kitty keyboard events,
|
||||||
|
// color scheme changes, in-band resize notifications, etc. do not
|
||||||
|
// bleed into the shell.
|
||||||
|
do_roundtrip_to_terminal(controlling_term, 2*time.Second)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|||||||
Reference in New Issue
Block a user