Skip to content

Commit

Permalink
fix: detect terminal size after exec
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
muesli committed Oct 15, 2022
1 parent 3609d87 commit b4bdb8c
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 32 deletions.
18 changes: 3 additions & 15 deletions signals_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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()
}
}
4 changes: 1 addition & 3 deletions signals_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import (

// 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 changes: 8 additions & 13 deletions tea.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -574,6 +563,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
}

Expand Down
33 changes: 32 additions & 1 deletion tty.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand All @@ -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,
})
}

0 comments on commit b4bdb8c

Please sign in to comment.