diff --git a/redo/request.go b/redo/request.go deleted file mode 100644 index 4f1d98a..0000000 --- a/redo/request.go +++ /dev/null @@ -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) -} diff --git a/redo/uitask.go b/redo/uitask.go index 42006fe..8b5e523 100644 --- a/redo/uitask.go +++ b/redo/uitask.go @@ -14,53 +14,32 @@ func Go() error { if err := uiinit(); err != nil { return err } - go uitask(Do) uimsgloop() 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. -// 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() { - go func() { - c := make(chan interface{}) - Do <- &Request{ - op: func() { - uistop() - c <- struct{}{} - }, - resp: c, - } - <-c - }() + issue(uistop) } -// 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 { // All events internally return bool; those that don't will be wrapped around to return a dummy value. 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. // It runs on the main thread without a message pump; it provides its own. func (e *event) fire() bool { - cc := make(chan Doer) - recur <- cc - c := <-cc - close(cc) - result := false - finished := make(chan struct{}) - go func() { - e.lock.Lock() - defer e.lock.Unlock() + e.lock.Lock() + defer e.lock.Unlock() - result = 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 + return e.do(c) } -// 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. // Implementations of issue() should call this. -func perform(req *Request) { - req.op() - close(req.resp) +func perform(fp unsafe.Pointer) { + f := (*func())(fp) + (*f)() } diff --git a/redo/uitask_darwin.go b/redo/uitask_darwin.go index 45fa8d1..eda0f29 100644 --- a/redo/uitask_darwin.go +++ b/redo/uitask_darwin.go @@ -25,12 +25,11 @@ func uistop() { C.uistop() } -func issue(req *Request) { - C.issue(unsafe.Pointer(req)) +func issue(f func()) { + C.issue(unsafe.Pointer(&f)) } //export doissue -func doissue(r unsafe.Pointer) { - req := (*Request)(unsafe.Pointer(r)) - perform(req) +func doissue(fp unsafe.Pointer) { + perform(fp) } diff --git a/redo/uitask_unix.go b/redo/uitask_unix.go index 0b8fe4a..b1a556e 100644 --- a/redo/uitask_unix.go +++ b/redo/uitask_unix.go @@ -27,13 +27,12 @@ func uistop() { C.gtk_main_quit() } -func issue(req *Request) { - C.gdk_threads_add_idle(C.GSourceFunc(C.doissue), C.gpointer(unsafe.Pointer(req))) +func issue(f func()) { + C.gdk_threads_add_idle(C.GSourceFunc(C.doissue), C.gpointer(unsafe.Pointer(&f))) } //export doissue func doissue(data C.gpointer) C.gboolean { - req := (*Request)(unsafe.Pointer(data)) - perform(req) + perform(unsafe.Pointer(data)) return C.FALSE // don't repeat } diff --git a/redo/uitask_windows.c b/redo/uitask_windows.c index 291ef3b..e9f7e8a 100644 --- a/redo/uitask_windows.c +++ b/redo/uitask_windows.c @@ -38,7 +38,7 @@ static LRESULT CALLBACK msgwinproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM l case WM_COMMAND: return forwardCommand(hwnd, uMsg, wParam, lParam); case msgRequest: - xperform((void *) lParam); + doissue((void *) lParam); return 0; default: return DefWindowProcW(hwnd, uMsg, wParam, lParam); diff --git a/redo/uitask_windows.go b/redo/uitask_windows.go index 3cb5012..38f6bfe 100644 --- a/redo/uitask_windows.go +++ b/redo/uitask_windows.go @@ -41,8 +41,8 @@ func uistop() { C.PostQuitMessage(0) } -func issue(req *Request) { - C.issue(unsafe.Pointer(req)) +func issue(f func()) { + C.issue(unsafe.Pointer(&f)) } func makemsgwin() error { @@ -55,8 +55,7 @@ func makemsgwin() error { return nil } -//export xperform -func xperform(xreq unsafe.Pointer) { - req := (*Request)(xreq) - perform(req) +//export doissue +func doissue(fp unsafe.Pointer) { + perform(fp) }