From 45c636ec33695f046de8bbc793f7c6e5f533bdff Mon Sep 17 00:00:00 2001 From: Timon Ringwald Date: Mon, 4 Apr 2022 14:47:15 +0200 Subject: [PATCH] thread safety for event loop and redraw loop --- screen.go | 86 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 32 deletions(-) diff --git a/screen.go b/screen.go index eb44bb3..79750eb 100644 --- a/screen.go +++ b/screen.go @@ -9,10 +9,14 @@ import ( ) type Screen struct { - scr tcell.Screen - buf *ViewBuffer - stopCh chan error - Root View + scr tcell.Screen + buf *ViewBuffer + + stopCh chan error + redrawCh chan struct{} + + // Root is the root view which is currently shown on screen + Root View // KeyPressed is called every time a key or key-combination is pressed. KeyPressed func(event *KeyEvent) (consumed bool) @@ -28,32 +32,18 @@ func NewScreen(root View) (*Screen, error) { } s := &Screen{ - Root: root, - scr: scr, - stopCh: make(chan error, 1), + Root: root, + scr: scr, + stopCh: make(chan error, 1), + redrawCh: make(chan struct{}, 1), } go s.eventloop() + go s.drawloop() return s, nil } -func (s *Screen) eventloop() { - for evt := s.scr.PollEvent(); evt != nil; evt = s.scr.PollEvent() { - switch event := evt.(type) { - case *tcell.EventResize: - go s.Redraw() - case *tcell.EventKey: - go s.onKeyPressed(event) - case *tcell.EventMouse: - go s.onMouseClicked(convertMouseEvent(event)) - default: - s.StopWithError(errors.New(fmt.Sprintf("%#v", event))) - } - } - s.StopWithError(errors.New("unknown error occured")) -} - func (s *Screen) Start() error { err := s.scr.Init() if err != nil { @@ -99,13 +89,45 @@ func convertMouseEvent(original *tcell.EventMouse) *MouseEvent { } func (s *Screen) Redraw() { - w, h := s.scr.Size() - - if s.buf == nil || s.buf.Width() != w || s.buf.Height() != h { - s.buf = buf2d.NewBuffer(w, h, DefaultRune) - } - - rw, rh := s.Root.Layout() - s.Root.Draw(truncateBuffer(s.buf, rw, rh)) - drawBuffer(s.scr, s.buf) + s.redrawCh <- struct{}{} +} + +func (s *Screen) eventloop() { + defer s.stopOnPanic() + + for evt := s.scr.PollEvent(); evt != nil; evt = s.scr.PollEvent() { + switch event := evt.(type) { + case *tcell.EventResize: + go s.Redraw() + case *tcell.EventKey: + go s.onKeyPressed(event) + case *tcell.EventMouse: + go s.onMouseClicked(convertMouseEvent(event)) + default: + s.StopWithError(errors.New(fmt.Sprintf("%#v", event))) + } + } + s.StopWithError(errors.New("unknown error occured")) +} + +func (s *Screen) drawloop() { + defer s.stopOnPanic() + + for range s.redrawCh { + w, h := s.scr.Size() + + if s.buf == nil || s.buf.Width() != w || s.buf.Height() != h { + s.buf = buf2d.NewBuffer(w, h, DefaultRune) + } + + rw, rh := s.Root.Layout() + s.Root.Draw(truncateBuffer(s.buf, rw, rh)) + drawBuffer(s.scr, s.buf) + } +} + +func (s *Screen) stopOnPanic() { + if err := recover(); err != nil { + s.StopWithError(fmt.Errorf("%v", err)) + } }