mirror of
https://github.com/taigrr/bubbletea.git
synced 2026-04-02 02:59:09 -07:00
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:
@@ -4,18 +4,15 @@
|
|||||||
package tea
|
package tea
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"golang.org/x/term"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// listenForResize sends messages (or errors) when the terminal resizes.
|
// listenForResize sends messages (or errors) when the terminal resizes.
|
||||||
// Argument output should be the file descriptor for the terminal; usually
|
// Argument output should be the file descriptor for the terminal; usually
|
||||||
// os.Stdout.
|
// 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)
|
sig := make(chan os.Signal, 1)
|
||||||
signal.Notify(sig, syscall.SIGWINCH)
|
signal.Notify(sig, syscall.SIGWINCH)
|
||||||
|
|
||||||
@@ -26,20 +23,11 @@ func listenForResize(ctx context.Context, output *os.File, msgs chan Msg, errs c
|
|||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-p.ctx.Done():
|
||||||
return
|
return
|
||||||
case <-sig:
|
case <-sig:
|
||||||
}
|
}
|
||||||
|
|
||||||
w, h, err := term.GetSize(int(output.Fd()))
|
p.checkResize()
|
||||||
if err != nil {
|
|
||||||
errs <- err
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
case msgs <- WindowSizeMsg{w, h}:
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,8 @@
|
|||||||
|
|
||||||
package tea
|
package tea
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
// listenForResize is not available on windows because windows does not
|
// listenForResize is not available on windows because windows does not
|
||||||
// implement syscall.SIGWINCH.
|
// implement syscall.SIGWINCH.
|
||||||
func listenForResize(ctx context.Context, output *os.File, msgs chan Msg,
|
func (p *Program) listenForResize(done chan struct{}) {
|
||||||
errs chan error, done chan struct{},
|
|
||||||
) {
|
|
||||||
close(done)
|
close(done)
|
||||||
}
|
}
|
||||||
|
|||||||
21
tea.go
21
tea.go
@@ -24,7 +24,6 @@ import (
|
|||||||
isatty "github.com/mattn/go-isatty"
|
isatty "github.com/mattn/go-isatty"
|
||||||
"github.com/muesli/cancelreader"
|
"github.com/muesli/cancelreader"
|
||||||
"github.com/muesli/termenv"
|
"github.com/muesli/termenv"
|
||||||
"golang.org/x/term"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrProgramKilled is returned by [Program.Run] when the program got killed.
|
// 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()) {
|
if f, ok := p.output.TTY().(*os.File); ok && isatty.IsTerminal(f.Fd()) {
|
||||||
// Get the initial terminal size and send it to the program.
|
// Get the initial terminal size and send it to the program.
|
||||||
go func() {
|
go p.checkResize()
|
||||||
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}:
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Listen for window resizes.
|
// Listen for window resizes.
|
||||||
go listenForResize(p.ctx, f, p.msgs, p.errs, ch)
|
go p.listenForResize(ch)
|
||||||
} else {
|
} else {
|
||||||
close(ch)
|
close(ch)
|
||||||
}
|
}
|
||||||
@@ -577,6 +566,12 @@ func (p *Program) RestoreTerminal() error {
|
|||||||
go p.Send(repaintMsg{})
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
33
tty.go
33
tty.go
@@ -3,9 +3,12 @@ package tea
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
isatty "github.com/mattn/go-isatty"
|
||||||
"github.com/muesli/cancelreader"
|
"github.com/muesli/cancelreader"
|
||||||
|
"golang.org/x/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *Program) initTerminal() error {
|
func (p *Program) initTerminal() error {
|
||||||
@@ -76,7 +79,10 @@ func (p *Program) readLoop() {
|
|||||||
msgs, err := readInputs(p.cancelReader)
|
msgs, err := readInputs(p.cancelReader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.Is(err, io.EOF) && !errors.Is(err, cancelreader.ErrCanceled) {
|
if !errors.Is(err, io.EOF) && !errors.Is(err, cancelreader.ErrCanceled) {
|
||||||
p.errs <- err
|
select {
|
||||||
|
case <-p.ctx.Done():
|
||||||
|
case p.errs <- err:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -98,3 +104,28 @@ func (p *Program) waitForReadLoop() {
|
|||||||
// though it was not able to cancel the read.
|
// 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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user