Merge branch 'add-synchronization-channels' of https://github.com/3cb/tview into 3cb-add-synchronization-channels

pull/177/head
Oliver 6 years ago
commit d830c42f6b

@ -46,11 +46,20 @@ type Application struct {
// Halts the event loop during suspended mode. // Halts the event loop during suspended mode.
suspendMutex sync.Mutex suspendMutex sync.Mutex
// Used to send screen events from separate goroutine to main event loop
events chan tcell.Event
// Used to send primitive updates from separate goroutines to the main event loop
updates chan func()
} }
// NewApplication creates and returns a new application. // NewApplication creates and returns a new application.
func NewApplication() *Application { func NewApplication() *Application {
return &Application{} return &Application{
events: make(chan tcell.Event, 100),
updates: make(chan func(), 100),
}
} }
// SetInputCapture sets a function which captures all key events before they are // SetInputCapture sets a function which captures all key events before they are
@ -136,61 +145,78 @@ func (a *Application) Run() error {
a.Unlock() a.Unlock()
a.Draw() a.Draw()
// Start event loop. // Separate loop to wait for screen events
for { go func() {
// Do not poll events during suspend mode for {
a.suspendMutex.Lock() // Do not poll events during suspend mode
a.RLock() a.suspendMutex.Lock()
screen := a.screen a.RLock()
a.RUnlock() screen := a.screen
if screen == nil { a.RUnlock()
a.suspendMutex.Unlock() if screen == nil {
break a.suspendMutex.Unlock()
} // send signal to stop main event loop
a.QueueEvent(nil)
break
}
// Wait for next event. // Wait for next event.
event := a.screen.PollEvent() a.QueueEvent(screen.PollEvent())
a.suspendMutex.Unlock() a.suspendMutex.Unlock()
if event == nil {
// The screen was finalized. Exit the loop.
break
} }
}()
switch event := event.(type) { // Start event loop.
case *tcell.EventKey: loop:
a.RLock() for {
p := a.focus select {
a.RUnlock() case event := <-a.events:
if event == nil {
// The screen was finalized. Exit the loop.
break loop
}
// Intercept keys. switch event := event.(type) {
if a.inputCapture != nil { case *tcell.EventKey:
event = a.inputCapture(event) a.RLock()
if event == nil { p := a.focus
break // Don't forward event. a.RUnlock()
// Intercept keys.
if a.inputCapture != nil {
event = a.inputCapture(event)
if event == nil {
break loop // Don't forward event.
}
} }
}
// Ctrl-C closes the application. // Ctrl-C closes the application.
if event.Key() == tcell.KeyCtrlC { if event.Key() == tcell.KeyCtrlC {
a.Stop() a.Stop()
} }
// Pass other key events to the currently focused primitive. // Pass other key events to the currently focused primitive.
if p != nil { if p != nil {
if handler := p.InputHandler(); handler != nil { if handler := p.InputHandler(); handler != nil {
handler(event, func(p Primitive) { handler(event, func(p Primitive) {
a.SetFocus(p) a.SetFocus(p)
}) })
a.Draw() a.Draw()
}
} }
case *tcell.EventResize:
a.RLock()
screen := a.screen
a.RUnlock()
screen.Clear()
a.Draw()
} }
case *tcell.EventResize:
a.RLock() case updater := <-a.updates:
screen := a.screen updater()
a.RUnlock()
screen.Clear()
a.Draw() a.Draw()
} }
} }
return nil return nil
@ -404,3 +430,15 @@ func (a *Application) GetFocus() Primitive {
defer a.RUnlock() defer a.RUnlock()
return a.focus return a.focus
} }
// QueueUpdate is used to synchronize changes to primitives by carrying an update function from separate goroutine to the Application event loop via channel
func (a *Application) QueueUpdate(f func()) *Application {
a.updates <- f
return a
}
// QueueEvent takes an Event instance and sends it to the Application event loop via channel
func (a *Application) QueueEvent(e tcell.Event) *Application {
a.events <- e
return a
}

Loading…
Cancel
Save