// 6 july 2014 package ui import ( "runtime" "sync" "unsafe" ) // Go initializes package ui. // TODO write this bit func Go() error { runtime.LockOSThread() if err := uiinit(); err != nil { return err } go uiissueloop() uimsgloop() return nil } // 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. var issuer = make(chan func()) // 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) issuer <- func() { f() done <- struct{}{} } <-done } // 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. // Stop will not have an effect until any event handlers or dialog boxes presently active return. // (TODO make sure this is the case for dialog boxes) func Stop() { issuer <- uistop } func uiissueloop() { for f := range issuer { issue(f) } } type event struct { // All events internally return bool; those that don't will be wrapped around to return a dummy value. do func() bool lock sync.Mutex } // do should never be nil; TODO should we make setters panic instead? func newEvent() *event { return &event{ do: func() bool { return false }, } } func (e *event) set(f func()) { e.lock.Lock() defer e.lock.Unlock() if f == nil { f = func() {} } e.do = func() bool { f() return false } } func (e *event) setbool(f func() bool) { e.lock.Lock() defer e.lock.Unlock() if f == nil { f = func() bool { return false } } e.do = f } // This is the common code for running an event. // It runs on the main thread without a message pump; it provides its own. func (e *event) fire() bool { e.lock.Lock() defer e.lock.Unlock() return e.do() } // Common code for performing a requested action (ui.Do() or ui.Stop()). // This should run on the main thread. // Implementations of issue() should call this. func perform(fp unsafe.Pointer) { f := (*func())(fp) (*f)() }