Removed Request completely, also updating the uitask files. Also renamed xperform to doissue for consistency.

This commit is contained in:
Pietro Gagliardi 2014-07-19 09:57:01 -04:00
parent 48c5055eb9
commit 104337188f
6 changed files with 38 additions and 153 deletions

View File

@ -1,69 +0,0 @@
// 6 july 2014
package ui
// Doer is a channel that takes Requests returned by the various functions and methods of package ui.
// There are two main Doers: ui.Do, which is for outside event handlers, and the Doer passed into an event handler function.
// You should not create or use your own Doers; these are meaningless.
type Doer chan *Request
// Do is the main way to issue requests to package ui.
// Requests returned by the various functions and methods of package ui should be sent across Do to have them performed.
// When an event is dispatched to an event handler, that event handler will receive a new Doer which is active for the life of the event handler, and any requests made to Do will block until the event handler returns.
var Do = make(Doer)
// Request represents a request issued to the package.
// These are returned by the various functions and methods of package ui and are sent to either Do or to the currently active event handler channel.
// There are also several convenience functions that perfrom common operations with requests.
type Request struct {
op func()
resp chan interface{}
}
// Response returns a channel which is pulsed exactly once, then immeidately closed, with the response from the function that issued the request.
// If the function does not return a value, this value will be the zero value of struct{}.
// Otherwise, the type of this value depends on the function that created the Request.
func (r *Request) Response() <-chan interface{} {
return r.resp
}
// Wait is a convenience function that performs a Request and waits for that request to be processed.
// If the request returns a value, it is discarded.
// You should generally use Wait on functions that do not return a value.
// See the documentation of Bool for an example.
func Wait(c Doer, r *Request) {
c <- r
<-r.resp
}
// Bool is a convenience function that performs a Request that returns a bool, waits for that request to be processed, and returns the result.
// For example:
// if ui.Bool(ui.Do, checkbox.Checked()) { /* do stuff */ }
func Bool(c Doer, r *Request) bool {
c <- r
return (<-r.resp).(bool)
}
// Int is like Bool, but for int.
func Int(c Doer, r *Request) int {
c <- r
return (<-r.resp).(int)
}
// String is like Bool, but for string.
func String(c Doer, r *Request) string {
c <- r
return (<-r.resp).(string)
}
// IntSlice is like Bool, but for []int.
func IntSlice(c Doer, r *Request) []int {
c <- r
return (<-r.resp).([]int)
}
// StringSlice is like Bool, but for []string.
func StringSlice(c Doer, r *Request) []string {
c <- r
return (<-r.resp).([]string)
}

View File

@ -14,53 +14,32 @@ func Go() error {
if err := uiinit(); err != nil { if err := uiinit(); err != nil {
return err return err
} }
go uitask(Do)
uimsgloop() uimsgloop()
return nil return nil
} }
// Stop issues a Request for package ui to stop and returns immediately. // 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)
issue(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. // Some time after this request is received, Go() will return without performing any final cleanup.
// Stop is internally issued to Do, so it will not be registered until any event handlers and dialog boxes return. // 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() { func Stop() {
go func() { issue(uistop)
c := make(chan interface{})
Do <- &Request{
op: func() {
uistop()
c <- struct{}{}
},
resp: c,
}
<-c
}()
} }
// This is the ui main loop.
// It is spawned by Go as a goroutine.
// It can also be called recursively using the recur/unrecur chain.
func uitask(doer Doer) {
for {
select {
case req := <-doer:
// TODO foreign event
issue(req)
case rec := <-recur: // want to perform event
c := make(Doer)
rec <- c
uitask(c)
case <-unrecur: // finished with event
close(doer)
return
}
}
}
// 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{})
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.
do func(c Doer) bool do func(c Doer) bool
@ -105,38 +84,16 @@ 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 {
cc := make(chan Doer) e.lock.Lock()
recur <- cc defer e.lock.Unlock()
c := <-cc
close(cc)
result := false
finished := make(chan struct{})
go func() {
e.lock.Lock()
defer e.lock.Unlock()
result = e.do(c) return e.do(c)
finished <- struct{}{}
}()
<-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
issue(&Request{
op: func() {
unrecur <- struct{}{}
},
// 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{}),
})
return result
} }
// Common code for performing a Request. // Common code for performing a requested action (ui.Do() or ui.Stop()).
// This should run on the main thread. // This should run on the main thread.
// Implementations of issue() should call this. // Implementations of issue() should call this.
func perform(req *Request) { func perform(fp unsafe.Pointer) {
req.op() f := (*func())(fp)
close(req.resp) (*f)()
} }

View File

@ -25,12 +25,11 @@ func uistop() {
C.uistop() C.uistop()
} }
func issue(req *Request) { func issue(f func()) {
C.issue(unsafe.Pointer(req)) C.issue(unsafe.Pointer(&f))
} }
//export doissue //export doissue
func doissue(r unsafe.Pointer) { func doissue(fp unsafe.Pointer) {
req := (*Request)(unsafe.Pointer(r)) perform(fp)
perform(req)
} }

View File

@ -27,13 +27,12 @@ func uistop() {
C.gtk_main_quit() C.gtk_main_quit()
} }
func issue(req *Request) { func issue(f func()) {
C.gdk_threads_add_idle(C.GSourceFunc(C.doissue), C.gpointer(unsafe.Pointer(req))) C.gdk_threads_add_idle(C.GSourceFunc(C.doissue), C.gpointer(unsafe.Pointer(&f)))
} }
//export doissue //export doissue
func doissue(data C.gpointer) C.gboolean { func doissue(data C.gpointer) C.gboolean {
req := (*Request)(unsafe.Pointer(data)) perform(unsafe.Pointer(data))
perform(req)
return C.FALSE // don't repeat return C.FALSE // don't repeat
} }

View File

@ -38,7 +38,7 @@ static LRESULT CALLBACK msgwinproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM l
case WM_COMMAND: case WM_COMMAND:
return forwardCommand(hwnd, uMsg, wParam, lParam); return forwardCommand(hwnd, uMsg, wParam, lParam);
case msgRequest: case msgRequest:
xperform((void *) lParam); doissue((void *) lParam);
return 0; return 0;
default: default:
return DefWindowProcW(hwnd, uMsg, wParam, lParam); return DefWindowProcW(hwnd, uMsg, wParam, lParam);

View File

@ -41,8 +41,8 @@ func uistop() {
C.PostQuitMessage(0) C.PostQuitMessage(0)
} }
func issue(req *Request) { func issue(f func()) {
C.issue(unsafe.Pointer(req)) C.issue(unsafe.Pointer(&f))
} }
func makemsgwin() error { func makemsgwin() error {
@ -55,8 +55,7 @@ func makemsgwin() error {
return nil return nil
} }
//export xperform //export doissue
func xperform(xreq unsafe.Pointer) { func doissue(fp unsafe.Pointer) {
req := (*Request)(xreq) perform(fp)
perform(req)
} }