forked from Mirrors/bubbletea
fix: detect terminal size after exec
Based on @knz's work in #499, but slightly supersedes this change. A little more coupling in the resize handling, but a lot less code & logic repetition. Co-authored-by: Raphael 'kena' Poss <knz@thaumogen.net>
This commit is contained in:
parent
9bcfc026a2
commit
80f44c9384
|
@ -4,18 +4,15 @@
|
|||
package tea
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
// listenForResize sends messages (or errors) when the terminal resizes.
|
||||
// Argument output should be the file descriptor for the terminal; usually
|
||||
// os.Stdout.
|
||||
func listenForResize(ctx context.Context, output *os.File, msgs chan Msg, errs chan error, done chan struct{}) {
|
||||
func (p *Program) listenForResize(done chan struct{}) {
|
||||
sig := make(chan os.Signal, 1)
|
||||
signal.Notify(sig, syscall.SIGWINCH)
|
||||
|
||||
|
@ -26,20 +23,11 @@ func listenForResize(ctx context.Context, output *os.File, msgs chan Msg, errs c
|
|||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case <-p.ctx.Done():
|
||||
return
|
||||
case <-sig:
|
||||
}
|
||||
|
||||
w, h, err := term.GetSize(int(output.Fd()))
|
||||
if err != nil {
|
||||
errs <- err
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case msgs <- WindowSizeMsg{w, h}:
|
||||
}
|
||||
p.checkResize()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,15 +3,8 @@
|
|||
|
||||
package tea
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
)
|
||||
|
||||
// listenForResize is not available on windows because windows does not
|
||||
// implement syscall.SIGWINCH.
|
||||
func listenForResize(ctx context.Context, output *os.File, msgs chan Msg,
|
||||
errs chan error, done chan struct{},
|
||||
) {
|
||||
func (p *Program) listenForResize(done chan struct{}) {
|
||||
close(done)
|
||||
}
|
||||
|
|
21
tea.go
21
tea.go
|
@ -24,7 +24,6 @@ import (
|
|||
isatty "github.com/mattn/go-isatty"
|
||||
"github.com/muesli/cancelreader"
|
||||
"github.com/muesli/termenv"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
// ErrProgramKilled is returned by [Program.Run] when the program got killed.
|
||||
|
@ -205,20 +204,10 @@ func (p *Program) handleResize() chan struct{} {
|
|||
|
||||
if f, ok := p.output.TTY().(*os.File); ok && isatty.IsTerminal(f.Fd()) {
|
||||
// Get the initial terminal size and send it to the program.
|
||||
go func() {
|
||||
w, h, err := term.GetSize(int(f.Fd()))
|
||||
if err != nil {
|
||||
p.errs <- err
|
||||
}
|
||||
|
||||
select {
|
||||
case <-p.ctx.Done():
|
||||
case p.msgs <- WindowSizeMsg{w, h}:
|
||||
}
|
||||
}()
|
||||
go p.checkResize()
|
||||
|
||||
// Listen for window resizes.
|
||||
go listenForResize(p.ctx, f, p.msgs, p.errs, ch)
|
||||
go p.listenForResize(ch)
|
||||
} else {
|
||||
close(ch)
|
||||
}
|
||||
|
@ -577,6 +566,12 @@ func (p *Program) RestoreTerminal() error {
|
|||
go p.Send(repaintMsg{})
|
||||
}
|
||||
|
||||
// If the output is a terminal, it may have been resized while another
|
||||
// process was at the foreground, in which case we may not have received
|
||||
// SIGWINCH. Detect any size change now and propagate the new size as
|
||||
// needed.
|
||||
go p.checkResize()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
33
tty.go
33
tty.go
|
@ -3,9 +3,12 @@ package tea
|
|||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
isatty "github.com/mattn/go-isatty"
|
||||
"github.com/muesli/cancelreader"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
func (p *Program) initTerminal() error {
|
||||
|
@ -76,7 +79,10 @@ func (p *Program) readLoop() {
|
|||
msgs, err := readInputs(p.cancelReader)
|
||||
if err != nil {
|
||||
if !errors.Is(err, io.EOF) && !errors.Is(err, cancelreader.ErrCanceled) {
|
||||
p.errs <- err
|
||||
select {
|
||||
case <-p.ctx.Done():
|
||||
case p.errs <- err:
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
|
@ -98,3 +104,28 @@ func (p *Program) waitForReadLoop() {
|
|||
// though it was not able to cancel the read.
|
||||
}
|
||||
}
|
||||
|
||||
// checkResize detects the current size of the output and informs the program
|
||||
// via a WindowSizeMsg.
|
||||
func (p *Program) checkResize() {
|
||||
f, ok := p.output.TTY().(*os.File)
|
||||
if !ok || !isatty.IsTerminal(f.Fd()) {
|
||||
// can't query window size
|
||||
return
|
||||
}
|
||||
|
||||
w, h, err := term.GetSize(int(f.Fd()))
|
||||
if err != nil {
|
||||
select {
|
||||
case <-p.ctx.Done():
|
||||
case p.errs <- err:
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
p.Send(WindowSizeMsg{
|
||||
Width: w,
|
||||
Height: h,
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue