Skip to content

Commit

Permalink
Always listen for SIGINT
Browse files Browse the repository at this point in the history
  • Loading branch information
meowgorithm committed Jan 11, 2021
1 parent 64da3bc commit d2e3878
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 22 deletions.
54 changes: 43 additions & 11 deletions tea.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@
package tea

import (
"context"
"fmt"
"io"
"os"
"os/signal"
"runtime/debug"
"sync"
"syscall"

isatty "github.com/mattn/go-isatty"
te "github.com/muesli/termenv"
Expand Down Expand Up @@ -109,6 +112,9 @@ type Program struct {
// from panics, print the stack trace, and disable raw mode. This feature
// is on by default.
CatchPanics bool

inputIsTTY bool
outputIsTTY bool
}

// Quit is a special command that tells the Bubble Tea program to exit.
Expand Down Expand Up @@ -167,17 +173,43 @@ func (p *Program) Start() error {
msgs = make(chan Msg)
errs = make(chan error)
done = make(chan struct{})

// If output is a file (e.g. os.Stdout) then this will be set
// accordingly. Most of the time you should refer to p.outputIsTTY
// rather than do a nil check against the value here.
outputAsFile *os.File
)

// Is output a file?
outputAsFile, outputIsFile := p.output.(*os.File)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// Is output a TTY?
var isTTY bool
if outputIsFile {
isTTY = isatty.IsTerminal(outputAsFile.Fd())
// Is output a terminal?
if f, ok := p.output.(*os.File); ok {
outputAsFile = f
p.outputIsTTY = isatty.IsTerminal(f.Fd())
}

// Is input a terminal?
if f, ok := p.input.(*os.File); ok {
p.inputIsTTY = isatty.IsTerminal(f.Fd())
}

// Listen for SIGINT. Note that in most cases ^C will not send an
// interrupt because the terminal will be in raw mode and thus capture
// that keystroke and send it along to Program.Update. If input is not a
// TTY, however, ^C will be caught here.
go func() {
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT)
defer signal.Stop(sig)

select {
case <-ctx.Done():
case <-sig:
msgs <- quitMsg{}
}
}()

if p.CatchPanics {
defer func() {
if r := recover(); r != nil {
Expand All @@ -193,12 +225,12 @@ func (p *Program) Start() error {

// Check if output is a TTY before entering raw mode, hiding the cursor and
// so on.
if isTTY {
err := initTerminal(p.output)
{
err := p.initTerminal()
if err != nil {
return err
}
defer restoreTerminal(p.output)
defer p.restoreTerminal()
}

// Initialize program
Expand All @@ -218,7 +250,7 @@ func (p *Program) Start() error {
p.renderer.write(model.View())

// Subscribe to user input
if p.input != nil {
if p.inputIsTTY {
go func() {
for {
msg, err := readInput(p.input)
Expand All @@ -234,7 +266,7 @@ func (p *Program) Start() error {
}()
}

if isTTY {
if p.outputIsTTY {
// Get initial terminal size
go func() {
w, h, err := terminal.GetSize(int(outputAsFile.Fd()))
Expand Down
31 changes: 20 additions & 11 deletions tty.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,35 @@
package tea

import (
"io"

"github.com/containerd/console"
)

var tty console.Console

func initTerminal(w io.Writer) error {
tty = console.Current()
err := tty.SetRaw()
if err != nil {
return err
func (p Program) initTerminal() error {
if p.outputIsTTY {
tty = console.Current()
}

if p.inputIsTTY {
err := tty.SetRaw()
if err != nil {
return err
}
}

if p.outputIsTTY {
enableAnsiColors(p.output)
hideCursor(p.output)
}

enableAnsiColors(w)
hideCursor(w)
return nil
}

func restoreTerminal(w io.Writer) error {
showCursor(w)
func (p Program) restoreTerminal() error {
if !p.outputIsTTY {
return nil
}
showCursor(p.output)
return tty.Reset()
}

0 comments on commit d2e3878

Please sign in to comment.