diff options
| author | Pietro Gagliardi <[email protected]> | 2014-03-01 15:18:29 -0500 |
|---|---|---|
| committer | Pietro Gagliardi <[email protected]> | 2014-03-01 15:18:29 -0500 |
| commit | be5458c0a37cf83f2392f3951233dd3f9f406c14 (patch) | |
| tree | ae358af220f4533465ca8807157889b819dcf884 /uitask_darwin.go | |
| parent | c8c257f8c84c163354a2b3ba7c97bd76f2700443 (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.
Diffstat (limited to 'uitask_darwin.go')
| -rw-r--r-- | uitask_darwin.go | 84 |
1 files changed, 35 insertions, 49 deletions
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 |
