Skip to content

Commit

Permalink
fix: don't share a mutex between tea.Program and its renderer
Browse files Browse the repository at this point in the history
  • Loading branch information
muesli committed Jun 7, 2022
1 parent 958dc20 commit 8f4d388
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 33 deletions.
19 changes: 11 additions & 8 deletions standard_renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ type standardRenderer struct {

// newRenderer creates a new renderer. Normally you'll want to initialize it
// with os.Stdout as the first argument.
func newRenderer(out io.Writer, mtx *sync.Mutex, useANSICompressor bool) renderer {
func newRenderer(out io.Writer, useANSICompressor bool) renderer {
r := &standardRenderer{
out: out,
mtx: mtx,
mtx: &sync.Mutex{},
framerate: defaultFramerate,
useANSICompressor: useANSICompressor,
}
Expand Down Expand Up @@ -230,15 +230,24 @@ func (r *standardRenderer) write(s string) {
}

func (r *standardRenderer) repaint() {
r.mtx.Lock()
defer r.mtx.Unlock()

r.lastRender = ""
}

func (r *standardRenderer) altScreen() bool {
r.mtx.Lock()
defer r.mtx.Unlock()

return r.altScreenActive
}

func (r *standardRenderer) setAltScreen(v bool) {
r.mtx.Lock()
r.altScreenActive = v
r.mtx.Unlock()

r.repaint()
}

Expand Down Expand Up @@ -348,9 +357,7 @@ func (r *standardRenderer) handleMessages(msg Msg) {
case repaintMsg:
// Force a repaint by clearing the render cache as we slide into a
// render.
r.mtx.Lock()
r.repaint()
r.mtx.Unlock()

case WindowSizeMsg:
r.mtx.Lock()
Expand All @@ -363,9 +370,7 @@ func (r *standardRenderer) handleMessages(msg Msg) {

// Force a repaint on the area where the scrollable stuff was in this
// update cycle
r.mtx.Lock()
r.repaint()
r.mtx.Unlock()

case syncScrollAreaMsg:
// Re-render scrolling area
Expand All @@ -374,9 +379,7 @@ func (r *standardRenderer) handleMessages(msg Msg) {
r.insertTop(msg.lines, msg.topBoundary, msg.bottomBoundary)

// Force non-scrolling stuff to repaint in this update cycle
r.mtx.Lock()
r.repaint()
r.mtx.Unlock()

case scrollUpMsg:
r.insertTop(msg.lines, msg.topBoundary, msg.bottomBoundary)
Expand Down
38 changes: 13 additions & 25 deletions tea.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ type Program struct {
cancelReader cancelreader.CancelReader

renderer renderer
altScreenActive bool
altScreenWasActive bool // was the altscreen active before releasing the terminal?

// CatchPanics is incredibly useful for restoring the terminal to a usable
Expand Down Expand Up @@ -382,7 +381,7 @@ func (p *Program) StartReturningModel() (Model, error) {

// If no renderer is set use the standard one.
if p.renderer == nil {
p.renderer = newRenderer(p.output, p.mtx, p.startupOptions.has(withANSICompressor))
p.renderer = newRenderer(p.output, p.startupOptions.has(withANSICompressor))
}

// Honor program startup options.
Expand Down Expand Up @@ -411,7 +410,6 @@ func (p *Program) StartReturningModel() (Model, error) {

// Start the renderer.
p.renderer.start()
p.renderer.setAltScreen(p.altScreenActive)

// Render the initial view.
p.renderer.write(model.View())
Expand Down Expand Up @@ -503,9 +501,7 @@ func (p *Program) StartReturningModel() (Model, error) {
continue

case WindowSizeMsg:
p.mtx.Lock()
p.renderer.repaint()
p.mtx.Unlock()

case enterAltScreenMsg:
p.EnterAltScreen()
Expand Down Expand Up @@ -599,38 +595,30 @@ func (p *Program) shutdown(kill bool) {
//
// Deprecated. Use the WithAltScreen ProgramOption instead.
func (p *Program) EnterAltScreen() {
p.mtx.Lock()
defer p.mtx.Unlock()

if p.altScreenActive {
if p.renderer == nil {
return
}
if p.renderer.altScreen() {
return
}

enterAltScreen(p.output)

p.altScreenActive = true
if p.renderer != nil {
p.renderer.setAltScreen(p.altScreenActive)
}
p.renderer.setAltScreen(true)
}

// ExitAltScreen exits the alternate screen buffer.
//
// Deprecated. The altscreen will exited automatically when the program exits.
func (p *Program) ExitAltScreen() {
p.mtx.Lock()
defer p.mtx.Unlock()

if !p.altScreenActive {
if p.renderer == nil {
return
}
if !p.renderer.altScreen() {
return
}

exitAltScreen(p.output)

p.altScreenActive = false
if p.renderer != nil {
p.renderer.setAltScreen(p.altScreenActive)
}
p.renderer.setAltScreen(false)
}

// EnableMouseCellMotion enables mouse click, release, wheel and motion events
Expand Down Expand Up @@ -679,8 +667,8 @@ func (p *Program) DisableMouseAllMotion() {
func (p *Program) ReleaseTerminal() error {
p.ignoreSignals = true
p.cancelInput()
p.altScreenWasActive = p.altScreenActive
if p.altScreenActive {
p.altScreenWasActive = p.renderer.altScreen()
if p.renderer.altScreen() {
p.ExitAltScreen()
time.Sleep(time.Millisecond * 10) // give the terminal a moment to catch up
}
Expand Down

0 comments on commit 8f4d388

Please sign in to comment.