summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPietro Gagliardi <[email protected]>2014-03-01 15:18:29 -0500
committerPietro Gagliardi <[email protected]>2014-03-01 15:18:29 -0500
commitbe5458c0a37cf83f2392f3951233dd3f9f406c14 (patch)
treeae358af220f4533465ca8807157889b819dcf884
parentc8c257f8c84c163354a2b3ba7c97bd76f2700443 (diff)
Major code restructure to allow Cocoa to work correctly. Cocoa requires that the application loop run on the very first OS thread created, not just an any ordinary thread. To support this, your code must now be run by the UI init code. Windows and Unix builds still work fine; Mac OS X fails for reasons I now have to debug.
-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 (