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-19 09:07:42 -05:00
"unsafe"
2014-08-18 15:52:20 -05:00
"reflect"
2014-07-07 20:04:44 -05:00
)
2014-08-14 08:53:29 -05:00
// Go initializes and runs package ui.
// It returns a non-nil error if initialization fails.
// Otherwise, it will run the event loop and not return until Stop is called.
// Due to platform-specific issues, it must be called from the main OS thread; in general, do not call Go() from anywhere except main() (including any goroutines).
2014-07-07 20:04:44 -05:00
func Go ( ) error {
runtime . LockOSThread ( )
if err := uiinit ( ) ; err != nil {
return err
}
2014-07-19 09:10:52 -05:00
go uiissueloop ( )
2014-07-07 20:04:44 -05:00
uimsgloop ( )
return nil
}
2014-07-19 09:10:52 -05:00
// To ensure that Do() and Stop() only do things after Go() has been called, this channel accepts the requests to issue. The issuing is done by uiissueloop() below.
2014-08-11 20:57:20 -05:00
// Notice that this is a pointer ot a function. See Do() below for details.
var issuer = make ( chan * func ( ) )
2014-07-19 09:10:52 -05:00
2014-07-19 08:57:01 -05:00
// Do performs f on the main loop, as if it were an event handler.
// It waits for f to execute before returning.
// Do cannot be called within event handlers or within Do itself.
func Do ( f func ( ) ) {
done := make ( chan struct { } )
defer close ( done )
2014-08-11 20:57:20 -05:00
// THIS MUST BE A POINTER.
// Previously, the pointer was constructed within issue().
// This meant that if the Do() was stalled, the garbage collector came in and reused the pointer value too soon!
call := func ( ) {
2014-07-19 08:57:01 -05:00
f ( )
done <- struct { } { }
2014-07-19 09:10:52 -05:00
}
2014-08-11 20:57:20 -05:00
issuer <- & call
2014-07-19 08:57:01 -05:00
<- done
2014-07-13 00:14:55 -05:00
}
2014-07-06 20:42:11 -05:00
2014-07-19 08:57:01 -05:00
// Stop informs package ui that it should stop.
// Stop then returns immediately.
// Some time after this request is received, Go() will return without performing any final cleanup.
2014-08-14 08:38:12 -05:00
// Stop will not have an effect until any event handlers return.
2014-07-19 08:57:01 -05:00
func Stop ( ) {
2014-08-11 20:57:20 -05:00
// can't send this directly across issuer
go func ( ) {
Do ( uistop )
} ( )
2014-07-19 09:10:52 -05:00
}
func uiissueloop ( ) {
for f := range issuer {
issue ( f )
}
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.
2014-07-19 09:07:42 -05:00
do func ( ) bool
2014-07-08 10:23:31 -05:00
lock sync . Mutex
}
2014-07-08 11:04:51 -05:00
func newEvent ( ) * event {
return & event {
2014-07-19 09:07:42 -05:00
do : func ( ) bool {
2014-07-08 11:04:51 -05:00
return false
} ,
}
}
2014-07-19 09:07:42 -05:00
func ( e * event ) set ( f func ( ) ) {
2014-07-08 10:23:31 -05:00
e . lock . Lock ( )
defer e . lock . Unlock ( )
2014-07-08 11:04:51 -05:00
if f == nil {
2014-07-19 09:07:42 -05:00
f = func ( ) { }
2014-07-08 11:04:51 -05:00
}
2014-07-19 09:07:42 -05:00
e . do = func ( ) bool {
f ( )
2014-07-08 10:23:31 -05:00
return false
}
}
2014-07-19 09:07:42 -05:00
func ( e * event ) setbool ( f func ( ) bool ) {
2014-07-08 10:23:31 -05:00
e . lock . Lock ( )
defer e . lock . Unlock ( )
2014-07-08 11:04:51 -05:00
if f == nil {
2014-07-19 09:07:42 -05:00
f = func ( ) bool {
2014-07-08 11:04:51 -05:00
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-19 08:57:01 -05:00
e . lock . Lock ( )
defer e . lock . Unlock ( )
2014-07-08 10:23:31 -05:00
2014-07-19 09:07:42 -05:00
return e . do ( )
2014-07-07 23:29:43 -05:00
}
2014-07-19 08:57:01 -05:00
// Common code for performing a requested action (ui.Do() or ui.Stop()).
2014-07-07 23:29:43 -05:00
// This should run on the main thread.
// Implementations of issue() should call this.
2014-07-19 08:57:01 -05:00
func perform ( fp unsafe . Pointer ) {
f := ( * func ( ) ) ( fp )
( * f ) ( )
2014-07-06 20:42:11 -05:00
}
2014-08-18 15:52:20 -05:00
// ForeignEvent wraps a channel in such a way that it can be used safely with package ui.
type ForeignEvent struct {
c reflect . Value
e * event
d interface { }
}
// NewForeignEvent creates a new ForeignEvent with the specified channel.
// It panics if the argument is not a receivable channel.
// The returned ForeignEvent assumes ownership of the channel.
// Each time a value is received on the channel, the returned function is invoked on the main thread.
func NewForeignEvent ( channel interface { } , handler func ( data interface { } ) ) * ForeignEvent {
c := reflect . ValueOf ( channel )
t := c . Type ( )
if t . Kind ( ) != reflect . Chan || ( t . ChanDir ( ) & reflect . RecvDir ) == 0 {
panic ( "non-channel or non-receivable channel passed to NewForeignEvent()" )
}
fe := & ForeignEvent {
c : c ,
e : newEvent ( ) ,
}
fe . e . set ( func ( ) {
handler ( fe . d )
} )
go fe . do ( )
return fe
}
func ( fe * ForeignEvent ) do ( ) {
for {
v , ok := fe . c . Recv ( )
if ! ok {
break
}
fe . d = v . Interface ( )
Do ( func ( ) {
fe . e . fire ( )
} )
}
}
// Stop ceases all future invocations of the handler passed to NewForeignEvent() on fe; the values read from the channel are merely discarded.
func ( fe * ForeignEvent ) Stop ( ) {
fe . e . set ( nil )
}