aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ui
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ui')
-rw-r--r--lib/ui/interfaces.go3
-rw-r--r--lib/ui/textinput.go2
-rw-r--r--lib/ui/ui.go58
3 files changed, 21 insertions, 42 deletions
diff --git a/lib/ui/interfaces.go b/lib/ui/interfaces.go
index 8ede22c7..19f0c04f 100644
--- a/lib/ui/interfaces.go
+++ b/lib/ui/interfaces.go
@@ -4,9 +4,6 @@ import (
"github.com/gdamore/tcell/v2"
)
-// AercMsg is used to communicate within aerc
-type AercMsg interface{}
-
// Drawable is a UI component that can draw. Unless specified, all methods must
// only be called from a single goroutine, the UI goroutine.
type Drawable interface {
diff --git a/lib/ui/textinput.go b/lib/ui/textinput.go
index d76e9349..b2ba33f6 100644
--- a/lib/ui/textinput.go
+++ b/lib/ui/textinput.go
@@ -329,7 +329,7 @@ func (ti *TextInput) showCompletions() {
}
ti.completions, ti.prefix = ti.tabcomplete(ti.StringLeft())
ti.completeIndex = -1
- QueueRedraw()
+ Invalidate()
}
func (ti *TextInput) OnChange(onChange func(ti *TextInput)) {
diff --git a/lib/ui/ui.go b/lib/ui/ui.go
index bc054ac5..628946fa 100644
--- a/lib/ui/ui.go
+++ b/lib/ui/ui.go
@@ -3,52 +3,43 @@ package ui
import (
"sync/atomic"
- "git.sr.ht/~rjarry/aerc/log"
"github.com/gdamore/tcell/v2"
)
const (
// nominal state, UI is up to date
CLEAN int32 = iota
- // redraw is required but not explicitly requested
+ // UI render has been queued in Redraw channel
DIRTY
- // redraw has been explicitly requested
- REDRAW_PENDING
)
-var MsgChannel = make(chan AercMsg, 50)
-
-type AercFuncMsg struct {
- Func func()
-}
-
// State of the UI. Any value other than 0 means the UI is in a dirty state.
// This should only be accessed via atomic operations to maintain thread safety
var uiState int32
-// QueueRedraw marks the UI as invalid and sends a nil message into the
-// MsgChannel. Nothing will handle this message, but a redraw will occur
-func QueueRedraw() {
- if atomic.SwapInt32(&uiState, REDRAW_PENDING) != REDRAW_PENDING {
- MsgChannel <- nil
- }
-}
+var Callbacks = make(chan func(), 50)
// QueueFunc queues a function to be called in the main goroutine. This can be
// used to prevent race conditions from delayed functions
func QueueFunc(fn func()) {
- MsgChannel <- &AercFuncMsg{Func: fn}
+ Callbacks <- fn
}
-// Invalidate marks the entire UI as invalid. Invalidate can be called from any
-// goroutine
+// Use a buffered channel of size 1 to avoid blocking callers of Invalidate()
+var Redraw = make(chan bool, 1)
+
+// Invalidate marks the entire UI as invalid and request a redraw as soon as
+// possible. Invalidate can be called from any goroutine and will never block.
func Invalidate() {
- atomic.StoreInt32(&uiState, DIRTY)
+ if atomic.SwapInt32(&uiState, DIRTY) != DIRTY {
+ Redraw <- true
+ }
}
type UI struct {
Content DrawableInteractive
- exit atomic.Value // bool
+ Quit chan struct{}
+ Events chan tcell.Event
ctx *Context
screen tcell.Screen
popover *Popover
@@ -73,11 +64,14 @@ func Initialize(content DrawableInteractive) (*UI, error) {
state := UI{
Content: content,
screen: screen,
+ // Use unbuffered channels (always blocking unless somebody can
+ // read immediately) We are merely using this as a proxy to
+ // tcell screen internal event channel.
+ Events: make(chan tcell.Event),
+ Quit: make(chan struct{}),
}
state.ctx = NewContext(width, height, screen, state.onPopover)
- state.exit.Store(false)
-
Invalidate()
if beeper, ok := content.(DrawableInteractiveBeeper); ok {
beeper.OnBeep(screen.Beep)
@@ -87,6 +81,7 @@ func Initialize(content DrawableInteractive) (*UI, error) {
if root, ok := content.(RootDrawable); ok {
root.Initialize(&state)
}
+ go state.screen.ChannelEvents(state.Events, state.Quit)
return &state, nil
}
@@ -95,12 +90,8 @@ func (state *UI) onPopover(p *Popover) {
state.popover = p
}
-func (state *UI) ShouldExit() bool {
- return state.exit.Load().(bool)
-}
-
func (state *UI) Exit() {
- state.exit.Store(true)
+ close(state.Quit)
}
func (state *UI) Close() {
@@ -124,15 +115,6 @@ func (state *UI) EnableMouse() {
state.screen.EnableMouse()
}
-func (state *UI) ChannelEvents() {
- go func() {
- defer log.PanicHandler()
- for {
- MsgChannel <- state.screen.PollEvent()
- }
- }()
-}
-
func (state *UI) HandleEvent(event tcell.Event) {
if event, ok := event.(*tcell.EventResize); ok {
state.screen.Clear()