summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--init.go23
-rwxr-xr-xtest.sh2
-rw-r--r--test/main.go (renamed from main_test.go)13
-rw-r--r--uitask_darwin.go84
-rw-r--r--uitask_unix.go10
-rw-r--r--uitask_windows.go11
6 files changed, 69 insertions, 74 deletions
diff --git a/init.go b/init.go
index 1f6d542..a47266c 100644
--- a/init.go
+++ b/init.go
@@ -1,20 +1,11 @@
// 11 february 2014
package ui
-import (
- "os"
-)
-
-func init() {
- initDone := make(chan error)
- go ui(initDone)
- err := <-initDone
- if err != nil {
- // TODO provide copying instructions? will need to be system-specific
- MsgBoxError("UI Library Init Failure",
- "A failure occured during UI library initialization:\n%v\n" +
- "Please report this to the application developer or on http://github.com/andlabs/ui.",
- err)
- os.Exit(1)
- }
+// Go sets up the UI environment and runs main in a goroutine.
+// If initialization fails, Go returns an error.
+// Otherwise, Go does not return to its caller until (unless? TODO) the application loop exits, at which point it returns nil.
+//
+// This model is undesirable, but Cocoa limitations require it.
+func Go(main func()) error {
+ return ui(main)
}
diff --git a/test.sh b/test.sh
new file mode 100755
index 0000000..7667318
--- /dev/null
+++ b/test.sh
@@ -0,0 +1,2 @@
+cd test
+go build main.go
diff --git a/main_test.go b/test/main.go
index 163024e..1984e58 100644
--- a/main_test.go
+++ b/test/main.go
@@ -1,9 +1,9 @@
// 11 february 2014
-package ui
+package main
import (
"fmt"
- "testing"
+ . ".."
)
func gridWindow() (*Window, error) {
@@ -25,7 +25,7 @@ func gridWindow() (*Window, error) {
return w, w.Open(g)
}
-func TestMain(t *testing.T) {
+func myMain() {
w := NewWindow("Main Window", 320, 240)
w.Closing = Event()
b := NewButton("Click Me")
@@ -119,3 +119,10 @@ mainloop:
gw.Hide()
w.Hide()
}
+
+func main() {
+ err := Go(myMain)
+ if err != nil {
+ panic(err)
+ }
+}
diff --git a/uitask_darwin.go b/uitask_darwin.go
index 3a6d633..2fb6b45 100644
--- a/uitask_darwin.go
+++ b/uitask_darwin.go
@@ -17,7 +17,6 @@ We will create an Objective-C class goAppDelegate. It contains two methods:
// #cgo LDFLAGS: -lobjc -framework Foundation -framework AppKit
// #include "objc_darwin.h"
-// extern void appDelegate_applicationDidFinishLaunching(id, SEL, id);
// extern void appDelegate_uitask(id, SEL, id);
import "C"
@@ -27,8 +26,6 @@ func msgBoxError(string, string){}
var uitask chan func()
-var mtret chan interface{}
-
var (
_NSAutoreleasePool = objc_getClass("NSAutoreleasePool")
_NSValue = objc_getClass("NSValue")
@@ -38,36 +35,44 @@ var (
_performSelectorOnMainThread =
sel_getUid("performSelectorOnMainThread:withObject:waitUntilDone:")
_pointerValue = sel_getUid("pointerValue")
+ _run = sel_getUid("run")
)
-func ui(initDone chan error) {
+func ui(main func()) error {
runtime.LockOSThread()
uitask = make(chan func())
- mtret = make(chan interface{})
- go mainThread()
- v := <-mtret
- if err, ok := v.(error); ok {
- initDone <- fmt.Errorf("error initializing Cocoa: %v", err)
- return
- }
- appDelegate := v.(C.id)
- for f := range uitask {
- // we need to make an NSAutoreleasePool, otherwise we get leak warnings on stderr
- pool := objc_new(_NSAutoreleasePool)
- fp := C.objc_msgSend_ptr(_NSValue, _valueWithPointer,
- unsafe.Pointer(&f))
- C.objc_msgSend_sel_id_bool(
- appDelegate,
- _performSelectorOnMainThread,
- _uitask,
- fp,
- C.BOOL(C.YES)) // wait so we can properly drain the autorelease pool; on other platforms we wind up waiting anyway (since the main thread can only handle one thing at a time) so
- objc_release(pool)
+ NSApp, appDelegate, err := initCocoa()
+ if err != nil {
+ return err
}
+
+ // Cocoa must run on the first thread created by the program, so we run our dispatcher on another thread instead
+ go func() {
+ for f := range uitask {
+ // we need to make an NSAutoreleasePool, otherwise we get leak warnings on stderr
+ pool := objc_new(_NSAutoreleasePool)
+ fp := C.objc_msgSend_ptr(_NSValue, _valueWithPointer,
+ unsafe.Pointer(&f))
+ C.objc_msgSend_sel_id_bool(
+ appDelegate,
+ _performSelectorOnMainThread,
+ _uitask,
+ fp,
+ C.BOOL(C.YES)) // wait so we can properly drain the autorelease pool; on other platforms we wind up waiting anyway (since the main thread can only handle one thing at a time) so
+ objc_release(pool)
+ }
+ }()
+
+ go main()
+
+ C.objc_msgSend_noargs(NSApp, _run)
+ return nil
}
+// TODO move to init_darwin.go?
+
const (
_goAppDelegate = "goAppDelegate"
)
@@ -76,41 +81,22 @@ var (
_NSApplication = objc_getClass("NSApplication")
_sharedApplication = sel_getUid("sharedApplication")
- _applicationDidFinishLaunching = sel_getUid("applicationDidFinishLaunching:")
- _run = sel_getUid("run")
)
-func mainThread() {
- runtime.LockOSThread()
+func initCocoa() (NSApp C.id, appDelegate C.id, err error) {
+ var appdelegateclass C.Class
- _NSApp := C.objc_msgSend_noargs(_NSApplication, _sharedApplication)
- appdelegateclass, err := makeDelegateClass(_goAppDelegate)
- if err != nil {
- mtret <- fmt.Errorf("error creating NSApplication delegate: %v", err)
- return
- }
- err = addDelegateMethod(appdelegateclass, _applicationDidFinishLaunching,
- C.appDelegate_applicationDidFinishLaunching)
- if err != nil {
- mtret <- fmt.Errorf("error adding NSApplication delegate applicationDidFinishLaunching: method (to start UI loop): %v", err)
- return
- }
+ NSApp = C.objc_msgSend_noargs(_NSApplication, _sharedApplication)
err = addDelegateMethod(appdelegateclass, _uitask, C.appDelegate_uitask)
if err != nil {
- mtret <- fmt.Errorf("error adding NSApplication delegate uitask: method (to do UI tasks): %v", err)
+ err = fmt.Errorf("error adding NSApplication delegate uitask: method (to do UI tasks): %v", err)
return
}
// TODO using objc_new() causes a segfault; find out why
// TODO make alloc followed by init (I thought NSObject provided its own init?)
- appDelegate := objc_alloc(objc_getClass(_goAppDelegate))
- objc_setDelegate(_NSApp, appDelegate)
- // and that's it, really
- C.objc_msgSend_noargs(_NSApp, _run)
-}
+ appDelegate = objc_alloc(objc_getClass(_goAppDelegate))
-//export appDelegate_applicationDidFinishLaunching
-func appDelegate_applicationDidFinishLaunching(self C.id, sel C.SEL, arg C.id) {
- mtret <- self
+ return
}
//export appDelegate_uitask
diff --git a/uitask_unix.go b/uitask_unix.go
index fad1503..759ac1d 100644
--- a/uitask_unix.go
+++ b/uitask_unix.go
@@ -10,15 +10,13 @@ import (
var uitask chan func()
-func ui(initDone chan error) {
+func ui(main func()) error {
runtime.LockOSThread()
uitask = make(chan func())
if gtk_init() != true {
- initDone <- fmt.Errorf("gtk_init failed (reason unknown; TODO)")
- return
+ return fmt.Errorf("gtk_init failed (reason unknown; TODO)")
}
- initDone <- nil
// thanks to tristan in irc.gimp.net/#gtk
gdk_threads_add_idle(func() bool {
@@ -29,5 +27,9 @@ func ui(initDone chan error) {
}
return true // don't destroy the callback
})
+
+ go main()
+
gtk_main()
+ return nil
}
diff --git a/uitask_windows.go b/uitask_windows.go
index c1f867c..33ab0ad 100644
--- a/uitask_windows.go
+++ b/uitask_windows.go
@@ -35,17 +35,22 @@ var (
_postThreadMessage = user32.NewProc("PostThreadMessageW")
)
-func ui(initDone chan error) {
+func ui(main func()) error {
runtime.LockOSThread()
uitask = make(chan *uimsg)
- initDone <- doWindowsInit()
+ err := doWindowsInit()
+ if err != nil {
+ return err
+ }
threadIDReq := make(chan uintptr)
msglooperrs := make(chan error)
go msgloop(threadIDReq, msglooperrs)
threadID := <-threadIDReq
+ go main()
+
quit := false
for !quit {
select {
@@ -66,6 +71,8 @@ func ui(initDone chan error) {
}
}
}
+
+ return nil
}
var (