forked from Mirrors/bubbletea
fix: stop renderer before launching a child process.
Stops the renderer before starting a child process, which prevents the repaint race condition that writes to non-altscreen.
This commit is contained in:
parent
90c9124b0a
commit
402d2b4e2b
|
@ -58,6 +58,7 @@ func newRenderer(out *termenv.Output, useANSICompressor bool) renderer {
|
||||||
r := &standardRenderer{
|
r := &standardRenderer{
|
||||||
out: out,
|
out: out,
|
||||||
mtx: &sync.Mutex{},
|
mtx: &sync.Mutex{},
|
||||||
|
done: make(chan struct{}),
|
||||||
framerate: defaultFramerate,
|
framerate: defaultFramerate,
|
||||||
useANSICompressor: useANSICompressor,
|
useANSICompressor: useANSICompressor,
|
||||||
queuedMessageLines: []string{},
|
queuedMessageLines: []string{},
|
||||||
|
@ -72,12 +73,15 @@ func newRenderer(out *termenv.Output, useANSICompressor bool) renderer {
|
||||||
func (r *standardRenderer) start() {
|
func (r *standardRenderer) start() {
|
||||||
if r.ticker == nil {
|
if r.ticker == nil {
|
||||||
r.ticker = time.NewTicker(r.framerate)
|
r.ticker = time.NewTicker(r.framerate)
|
||||||
|
} else {
|
||||||
|
// If the ticker already exists, it has been stopped and we need to
|
||||||
|
// reset it.
|
||||||
|
r.ticker.Reset(r.framerate)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since the renderer can be restarted after a stop, we need to reset
|
// Since the renderer can be restarted after a stop, we need to reset
|
||||||
// the done channel and its corresponding sync.Once.
|
// the done channel and its corresponding sync.Once.
|
||||||
r.once = sync.Once{}
|
r.once = sync.Once{}
|
||||||
r.done = make(chan struct{})
|
|
||||||
|
|
||||||
go r.listen()
|
go r.listen()
|
||||||
}
|
}
|
||||||
|
@ -92,7 +96,7 @@ func (r *standardRenderer) stop() {
|
||||||
|
|
||||||
r.out.ClearLine()
|
r.out.ClearLine()
|
||||||
r.once.Do(func() {
|
r.once.Do(func() {
|
||||||
close(r.done)
|
r.done <- struct{}{}
|
||||||
})
|
})
|
||||||
|
|
||||||
if r.useANSICompressor {
|
if r.useANSICompressor {
|
||||||
|
@ -109,7 +113,7 @@ func (r *standardRenderer) kill() {
|
||||||
|
|
||||||
r.out.ClearLine()
|
r.out.ClearLine()
|
||||||
r.once.Do(func() {
|
r.once.Do(func() {
|
||||||
close(r.done)
|
r.done <- struct{}{}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,14 +121,12 @@ func (r *standardRenderer) kill() {
|
||||||
func (r *standardRenderer) listen() {
|
func (r *standardRenderer) listen() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-r.ticker.C:
|
|
||||||
if r.ticker != nil {
|
|
||||||
r.flush()
|
|
||||||
}
|
|
||||||
case <-r.done:
|
case <-r.done:
|
||||||
r.ticker.Stop()
|
r.ticker.Stop()
|
||||||
r.ticker = nil
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
case <-r.ticker.C:
|
||||||
|
r.flush()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
7
tea.go
7
tea.go
|
@ -567,6 +567,10 @@ func (p *Program) ReleaseTerminal() error {
|
||||||
p.cancelReader.Cancel()
|
p.cancelReader.Cancel()
|
||||||
p.waitForReadLoop()
|
p.waitForReadLoop()
|
||||||
|
|
||||||
|
if p.renderer != nil {
|
||||||
|
p.renderer.stop()
|
||||||
|
}
|
||||||
|
|
||||||
p.altScreenWasActive = p.renderer.altScreen()
|
p.altScreenWasActive = p.renderer.altScreen()
|
||||||
return p.restoreTerminalState()
|
return p.restoreTerminalState()
|
||||||
}
|
}
|
||||||
|
@ -590,6 +594,9 @@ func (p *Program) RestoreTerminal() error {
|
||||||
// entering alt screen already causes a repaint.
|
// entering alt screen already causes a repaint.
|
||||||
go p.Send(repaintMsg{})
|
go p.Send(repaintMsg{})
|
||||||
}
|
}
|
||||||
|
if p.renderer != nil {
|
||||||
|
p.renderer.start()
|
||||||
|
}
|
||||||
|
|
||||||
// If the output is a terminal, it may have been resized while another
|
// 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
|
// process was at the foreground, in which case we may not have received
|
||||||
|
|
Loading…
Reference in New Issue