2014-07-06 20:42:11 -05:00
|
|
|
// 6 july 2014
|
|
|
|
|
|
|
|
package ui
|
|
|
|
|
2014-07-07 20:04:44 -05:00
|
|
|
import (
|
|
|
|
"runtime"
|
2014-07-08 10:23:31 -05:00
|
|
|
"sync"
|
2014-07-07 20:04:44 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
// Go initializes package ui.
|
|
|
|
// TODO write this bit
|
|
|
|
func Go() error {
|
|
|
|
runtime.LockOSThread()
|
|
|
|
if err := uiinit(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-07-13 19:26:16 -05:00
|
|
|
go uitask(Do)
|
2014-07-07 20:04:44 -05:00
|
|
|
uimsgloop()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-07-13 19:28:15 -05:00
|
|
|
// Stop issues a Request for package ui to stop and returns immediately.
|
2014-07-13 00:14:55 -05:00
|
|
|
// Some time after this request is received, Go() will return without performing any final cleanup.
|
2014-07-13 19:28:15 -05:00
|
|
|
// Stop is internally issued to Do, so it will not be registered until any event handlers and dialog boxes return.
|
2014-07-13 19:26:16 -05:00
|
|
|
func Stop() {
|
|
|
|
go func() {
|
|
|
|
c := make(chan interface{})
|
|
|
|
Do <- &Request{
|
|
|
|
op: func() {
|
|
|
|
uistop()
|
|
|
|
c <- struct{}{}
|
|
|
|
},
|
|
|
|
resp: c,
|
|
|
|
}
|
|
|
|
<-c
|
|
|
|
}()
|
2014-07-13 00:14:55 -05:00
|
|
|
}
|
2014-07-06 20:42:11 -05:00
|
|
|
|
|
|
|
// This is the ui main loop.
|
|
|
|
// It is spawned by Go as a goroutine.
|
2014-07-13 19:26:16 -05:00
|
|
|
// It can also be called recursively using the recur/unrecur chain.
|
|
|
|
func uitask(doer Doer) {
|
2014-07-06 20:42:11 -05:00
|
|
|
for {
|
|
|
|
select {
|
2014-07-13 19:26:16 -05:00
|
|
|
case req := <-doer:
|
2014-07-06 20:42:11 -05:00
|
|
|
// TODO foreign event
|
|
|
|
issue(req)
|
2014-07-13 19:26:16 -05:00
|
|
|
case rec := <-recur: // want to perform event
|
|
|
|
c := make(Doer)
|
|
|
|
rec <- c
|
|
|
|
uitask(c)
|
|
|
|
case <-unrecur: // finished with event
|
|
|
|
close(doer)
|
|
|
|
return
|
2014-07-06 20:42:11 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-13 19:26:16 -05:00
|
|
|
// Send a channel over recur to have uitask() above enter a recursive loop in which the Doer sent back becomes the active request handler.
|
|
|
|
// Pulse unrecur when finished.
|
|
|
|
var recur = make(chan chan Doer)
|
|
|
|
var unrecur = make(chan struct{})
|
2014-07-06 20:42:11 -05:00
|
|
|
|
2014-07-08 10:23:31 -05:00
|
|
|
type event struct {
|
|
|
|
// All events internally return bool; those that don't will be wrapped around to return a dummy value.
|
|
|
|
do func(c Doer) bool
|
|
|
|
lock sync.Mutex
|
|
|
|
}
|
|
|
|
|
2014-07-08 11:04:51 -05:00
|
|
|
// do should never be nil; TODO should we make setters panic instead?
|
|
|
|
|
|
|
|
func newEvent() *event {
|
|
|
|
return &event{
|
|
|
|
do: func(c Doer) bool {
|
|
|
|
return false
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-08 10:23:31 -05:00
|
|
|
func (e *event) set(f func(Doer)) {
|
|
|
|
e.lock.Lock()
|
|
|
|
defer e.lock.Unlock()
|
|
|
|
|
2014-07-08 11:04:51 -05:00
|
|
|
if f == nil {
|
|
|
|
f = func(c Doer) {}
|
|
|
|
}
|
2014-07-08 10:23:31 -05:00
|
|
|
e.do = func(c Doer) bool {
|
|
|
|
f(c)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *event) setbool(f func(Doer) bool) {
|
|
|
|
e.lock.Lock()
|
|
|
|
defer e.lock.Unlock()
|
|
|
|
|
2014-07-08 11:04:51 -05:00
|
|
|
if f == nil {
|
|
|
|
f = func(c Doer) bool {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
2014-07-08 10:23:31 -05:00
|
|
|
e.do = f
|
|
|
|
}
|
|
|
|
|
2014-07-06 20:42:11 -05:00
|
|
|
// This is the common code for running an event.
|
2014-07-07 23:29:43 -05:00
|
|
|
// It runs on the main thread without a message pump; it provides its own.
|
2014-07-08 10:23:31 -05:00
|
|
|
func (e *event) fire() bool {
|
2014-07-13 19:26:16 -05:00
|
|
|
cc := make(chan Doer)
|
|
|
|
recur <- cc
|
|
|
|
c := <-cc
|
2014-07-18 21:56:30 -05:00
|
|
|
close(cc)
|
2014-07-08 10:23:31 -05:00
|
|
|
result := false
|
2014-07-13 19:26:16 -05:00
|
|
|
finished := make(chan struct{})
|
2014-07-06 20:42:11 -05:00
|
|
|
go func() {
|
2014-07-08 10:23:31 -05:00
|
|
|
e.lock.Lock()
|
|
|
|
defer e.lock.Unlock()
|
|
|
|
|
|
|
|
result = e.do(c)
|
2014-07-13 19:26:16 -05:00
|
|
|
finished <- struct{}{}
|
2014-07-06 20:42:11 -05:00
|
|
|
}()
|
2014-07-13 19:26:16 -05:00
|
|
|
<-finished
|
|
|
|
close(finished)
|
|
|
|
// leave the event handler; leave it only after returning from the OS-side event handler so we must issue it like a normal Request
|
2014-07-07 23:29:43 -05:00
|
|
|
issue(&Request{
|
|
|
|
op: func() {
|
2014-07-13 19:26:16 -05:00
|
|
|
unrecur <- struct{}{}
|
2014-07-07 23:29:43 -05:00
|
|
|
},
|
|
|
|
// unfortunately, closing a nil channel causes a panic
|
|
|
|
// therefore, we have to make a dummy channel
|
|
|
|
// TODO add conditional checks to the request handler instead?
|
|
|
|
resp: make(chan interface{}),
|
|
|
|
})
|
2014-07-08 10:23:31 -05:00
|
|
|
return result
|
2014-07-07 23:29:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Common code for performing a Request.
|
|
|
|
// This should run on the main thread.
|
|
|
|
// Implementations of issue() should call this.
|
|
|
|
func perform(req *Request) {
|
|
|
|
req.op()
|
|
|
|
close(req.resp)
|
2014-07-06 20:42:11 -05:00
|
|
|
}
|