Changed the semantics of uitask() to allow it to handle recursive main loops properly; important for Stop() to work correctly on non-Windows systems.
This commit is contained in:
parent
f36451d26e
commit
244061d878
|
@ -14,42 +14,52 @@ func Go() error {
|
||||||
if err := uiinit(); err != nil {
|
if err := uiinit(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
go uitask()
|
go uitask(Do)
|
||||||
uimsgloop()
|
uimsgloop()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop returns a Request for package ui to stop.
|
// Stop issues a Request for package ui to stop.
|
||||||
// Some time after this request is received, Go() will return without performing any final cleanup.
|
// Some time after this request is received, Go() will return without performing any final cleanup.
|
||||||
// If Stop is issued during an event handler, it will be registered when the event handler returns.
|
// Stop is internally issued to ui.Do, so it will not be registered until any event handlers and dialog boxes return.
|
||||||
func Stop() *Request {
|
func Stop() {
|
||||||
c := make(chan interface{})
|
go func() {
|
||||||
return &Request{
|
c := make(chan interface{})
|
||||||
op: func() {
|
Do <- &Request{
|
||||||
uistop()
|
op: func() {
|
||||||
c <- struct{}{}
|
uistop()
|
||||||
},
|
c <- struct{}{}
|
||||||
resp: c,
|
},
|
||||||
}
|
resp: c,
|
||||||
|
}
|
||||||
|
<-c
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is the ui main loop.
|
// This is the ui main loop.
|
||||||
// It is spawned by Go as a goroutine.
|
// It is spawned by Go as a goroutine.
|
||||||
func uitask() {
|
// It can also be called recursively using the recur/unrecur chain.
|
||||||
|
func uitask(doer Doer) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case req := <-Do:
|
case req := <-doer:
|
||||||
// TODO foreign event
|
// TODO foreign event
|
||||||
issue(req)
|
issue(req)
|
||||||
case <-stall: // wait for event to finish
|
case rec := <-recur: // want to perform event
|
||||||
<-stall // see below for information
|
c := make(Doer)
|
||||||
|
rec <- c
|
||||||
|
uitask(c)
|
||||||
|
case <-unrecur: // finished with event
|
||||||
|
close(doer)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// At each event, this is pulsed twice: once when the event begins, and once when the event ends.
|
// Send a channel over recur to have uitask() above enter a recursive loop in which the Doer sent back becomes the active request handler.
|
||||||
// Do is not processed in between.
|
// Pulse unrecur when finished.
|
||||||
var stall = make(chan struct{})
|
var recur = make(chan chan Doer)
|
||||||
|
var unrecur = make(chan struct{})
|
||||||
|
|
||||||
type event struct {
|
type event struct {
|
||||||
// All events internally return bool; those that don't will be wrapped around to return a dummy value.
|
// All events internally return bool; those that don't will be wrapped around to return a dummy value.
|
||||||
|
@ -95,25 +105,24 @@ func (e *event) setbool(f func(Doer) bool) {
|
||||||
// This is the common code for running an event.
|
// This is the common code for running an event.
|
||||||
// It runs on the main thread without a message pump; it provides its own.
|
// It runs on the main thread without a message pump; it provides its own.
|
||||||
func (e *event) fire() bool {
|
func (e *event) fire() bool {
|
||||||
stall <- struct{}{} // enter event handler
|
cc := make(chan Doer)
|
||||||
c := make(Doer)
|
recur <- cc
|
||||||
|
c := <-cc
|
||||||
result := false
|
result := false
|
||||||
|
finished := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
e.lock.Lock()
|
e.lock.Lock()
|
||||||
defer e.lock.Unlock()
|
defer e.lock.Unlock()
|
||||||
|
|
||||||
result = e.do(c)
|
result = e.do(c)
|
||||||
close(c)
|
finished <- struct{}{}
|
||||||
}()
|
}()
|
||||||
for req := range c {
|
<-finished
|
||||||
// note: this is perform, not issue!
|
close(finished)
|
||||||
// doevent runs on the main thread without a message pump!
|
// leave the event handler; leave it only after returning from the OS-side event handler so we must issue it like a normal Request
|
||||||
perform(req)
|
|
||||||
}
|
|
||||||
// leave the event handler; leave it only after returning from an event handler so we must issue it like a normal Request
|
|
||||||
issue(&Request{
|
issue(&Request{
|
||||||
op: func() {
|
op: func() {
|
||||||
stall <- struct{}{}
|
unrecur <- struct{}{}
|
||||||
},
|
},
|
||||||
// unfortunately, closing a nil channel causes a panic
|
// unfortunately, closing a nil channel causes a panic
|
||||||
// therefore, we have to make a dummy channel
|
// therefore, we have to make a dummy channel
|
||||||
|
|
|
@ -14,7 +14,7 @@ func init() {
|
||||||
w := GetNewWindow(Do, "Hello", 320, 240)
|
w := GetNewWindow(Do, "Hello", 320, 240)
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
Wait(Do, w.OnClosing(func(c Doer) bool {
|
Wait(Do, w.OnClosing(func(c Doer) bool {
|
||||||
Wait(c, Stop())
|
Stop()
|
||||||
done <- struct{}{}
|
done <- struct{}{}
|
||||||
return true
|
return true
|
||||||
}))
|
}))
|
||||||
|
|
Loading…
Reference in New Issue