// Used to send screen events from separate goroutine to main event loop
eventschantcell.Event
// Used to send primitive updates from separate goroutines to the main event loop
updateschanfunc()
}
}
// NewApplication creates and returns a new application.
// NewApplication creates and returns a new application.
funcNewApplication()*Application{
funcNewApplication()*Application{
return&Application{}
return&Application{
events:make(chantcell.Event,100),
updates:make(chanfunc(),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{
gofunc(){
// 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
ifscreen==nil{
a.RUnlock()
a.suspendMutex.Unlock()
ifscreen==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()
ifevent==nil{
// The screen was finalized. Exit the loop.
break
}
}
}()
switchevent:=event.(type){
// Start event loop.
case*tcell.EventKey:
loop:
a.RLock()
for{
p:=a.focus
select{
a.RUnlock()
caseevent:=<-a.events:
ifevent==nil{
// The screen was finalized. Exit the loop.
breakloop
}
// Intercept keys.
switchevent:=event.(type){
ifa.inputCapture!=nil{
case*tcell.EventKey:
event=a.inputCapture(event)
a.RLock()
ifevent==nil{
p:=a.focus
break// Don't forward event.
a.RUnlock()
// Intercept keys.
ifa.inputCapture!=nil{
event=a.inputCapture(event)
ifevent==nil{
breakloop// Don't forward event.
}
}
}
}
// Ctrl-C closes the application.
// Ctrl-C closes the application.
ifevent.Key()==tcell.KeyCtrlC{
ifevent.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.
ifp!=nil{
ifp!=nil{
ifhandler:=p.InputHandler();handler!=nil{
ifhandler:=p.InputHandler();handler!=nil{
handler(event,func(pPrimitive){
handler(event,func(pPrimitive){
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()
caseupdater:=<-a.updates:
screen:=a.screen
updater()
a.RUnlock()
screen.Clear()
a.Draw()
a.Draw()
}
}
}
}
returnnil
returnnil
@ -404,3 +430,15 @@ func (a *Application) GetFocus() Primitive {
defera.RUnlock()
defera.RUnlock()
returna.focus
returna.focus
}
}
// QueueUpdate is used to synchronize changes to primitives by carrying an update function from separate goroutine to the Application event loop via channel