From 3975c921c240692b622cde5b5aeaa7269d6af83a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 28 Feb 2014 23:01:48 -0500 Subject: [PATCH] Added the ui() for OS X. --- uitask_darwin.go | 115 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 uitask_darwin.go diff --git a/uitask_darwin.go b/uitask_darwin.go new file mode 100644 index 0000000..8c15cf4 --- /dev/null +++ b/uitask_darwin.go @@ -0,0 +1,115 @@ +// 28 february 2014 +package ui + +import ( + "fmt" + "runtime" + "unsafe" +) + +/* +We will create an Objective-C class goAppDelegate. It contains two methods: + - (void)applicationDidFinishLoading:(NSNotification *)unused + will signal to ui() that we are now in the Cocoa event loop; we make our goAppDelegate instance the NSApplication delegate + - (void)uitask:(NSValue *)functionPointer + the function that actually performs our UI task functions; it is called with NSObject's performSelectorOnMainThread system +*/ + +// #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" + +var uitask chan func() + +var mtret chan interface{} + +var ( + _NSAutoreleasePool = objc_getClass("NSAutoreleasePool") + _NSValue = objc_getClass("NSValue") + + _uitask = sel_getUid("uitask:") + _valueWithPointer = sel_getUid("valueWithPointer:") + _performSelectorOnMainThread = + sel_getUid("performSelectorOnMainThread:withObject:waitUntilDone:") + _pointerValue = sel_getUid("pointerValue") +) + +func ui(initDone chan error) { + runtime.LockOSThread() + + uitask = make(chan func()) + mtret = make(chan interface{}) + go mainThread() + v := <-mtret + if err, ok := v.(error); err { + 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) + } +} + +const ( + _goAppDelegate = "goAppDelegate" +) + +var ( + _NSApplication = objc_getClass("NSApplication") + + _sharedApplication = sel_getUid("sharedApplication") + _applicationDidFinishLaunching = sel_getUid("applicationDidFinishLaunching:") + _run = sel_getUid("run") +) + +func mainThread() { + runtime.LockOSThread() + + _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 + } + 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) + return + } + appDelegate := objc_new(objc_getClass(_goAppDelegate)) + C.objc_msgSend_id(_NSApp, _setDelegate, appDelegate) + // and that's it, really + C.objc_msgSend_noargs(_NSApp, _run) +} + +//export appDelegate_applicationDidFinishLaunching +func appDelegate_applicationDidFinishLaunching(self C.id, sel C.SEL, arg C.id) { + mtret <- self +} + +//export appDelegate_uitask +func appDelegate_uitask(self C.id, sel C.SEL, arg C.id) { + p := C.objc_msgSend_noargs(arg, _pointerValue) + f := (*func ())(unsafe.Pointer(p)) + (*f)() +}