diff --git a/delegate_darwin.go b/delegate_darwin.go index be26a1e..a3f7486 100644 --- a/delegate_darwin.go +++ b/delegate_darwin.go @@ -18,9 +18,7 @@ This creates a class goAppDelegate that will be used as the delegate for /everyt // #cgo LDFLAGS: -lobjc -framework Foundation -framework AppKit // #include // #include "objc_darwin.h" -// /* TODO this goes in objc_darwin.h once I take care of everything else */ -// extern id makeAppDelegate(void); -// extern id windowGetContentView(id); +// #include "delegateuitask_darwin.h" import "C" var ( diff --git a/delegate_darwin.m b/delegate_darwin.m deleted file mode 100644 index 638bfa3..0000000 --- a/delegate_darwin.m +++ /dev/null @@ -1,53 +0,0 @@ -// 13 may 2014 - -#include "objc_darwin.h" -#include "_cgo_export.h" -#include -#include -#include -#include -#include - -@interface appDelegate : NSObject -@end - -@implementation appDelegate - -- (void)uitask:(NSValue *)fp -{ - appDelegate_uitask([fp pointerValue]); -} - -- (BOOL)windowShouldClose:(id)win -{ - appDelegate_windowShouldClose(win); - return NO; // don't close -} - -- (void)windowDidResize:(NSNotification *)n -{ - appDelegate_windowDidResize([n object]); -} - -- (void)buttonClicked:(id)button -{ - appDelegate_buttonClicked(button); -} - -- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)app -{ - appDelegate_applicationShouldTerminate(); - return NSTerminateCancel; -} - -@end - -id makeAppDelegate(void) -{ - return [appDelegate new]; -} - -id windowGetContentView(id window) -{ - return [((NSWindow *) window) contentView]; -} diff --git a/delegateuitask_darwin.h b/delegateuitask_darwin.h new file mode 100644 index 0000000..cccc6a2 --- /dev/null +++ b/delegateuitask_darwin.h @@ -0,0 +1,10 @@ +/* 13 may 2014 */ + +//#include "common_darwin.h" + +extern id makeAppDelegate(void); +extern id windowGetContentView(id); +extern BOOL initCocoa(id); +extern void douitask(id, void *); +extern void breakMainLoop(void); +extern void cocoaMainLoop(void); diff --git a/delegateuitask_darwin.m b/delegateuitask_darwin.m new file mode 100644 index 0000000..5d2ec9e --- /dev/null +++ b/delegateuitask_darwin.m @@ -0,0 +1,95 @@ +// 13 may 2014 + +#include "objc_darwin.h" +#include "delegateuitask_darwin.h" +#include "_cgo_export.h" +#include +#include +#include +#include +#include +#include + +@interface appDelegate : NSObject +@end + +@implementation appDelegate + +- (void)uitask:(NSValue *)fp +{ + appDelegate_uitask([fp pointerValue]); +} + +- (BOOL)windowShouldClose:(id)win +{ + appDelegate_windowShouldClose(win); + return NO; // don't close +} + +- (void)windowDidResize:(NSNotification *)n +{ + appDelegate_windowDidResize([n object]); +} + +- (void)buttonClicked:(id)button +{ + appDelegate_buttonClicked(button); +} + +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)app +{ + appDelegate_applicationShouldTerminate(); + return NSTerminateCancel; +} + +@end + +id makeAppDelegate(void) +{ + return [appDelegate new]; +} + +id windowGetContentView(id window) +{ + return [((NSWindow *) window) contentView]; +} + +BOOL initCocoa(id appDelegate) +{ + [NSApplication sharedApplication]; + if ([NSApp setActivationPolicy:NSApplicationActivationPolicyRegular] != YES) + return NO; + [NSApp activateIgnoringOtherApps:YES]; // TODO actually do C.NO here? Russ Cox does YES in his devdraw; the docs say the Finder does NO + [NSApp setDelegate:appDelegate]; + return YES; +} + +void douitask(id appDelegate, void *p) +{ + NSAutoreleasePool *pool; + NSValue *fp; + + // we need to make an NSAutoreleasePool, otherwise we get leak warnings on stderr + pool = [NSAutoreleasePool new]; + fp = [NSValue valueWithPointer:p]; + [appDelegate performSelectorOnMainThread:@selector(uitask:) + withObject:fp + waitUntilDone: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 + [pool release]; +} + +void breakMainLoop(void) +{ + // -[NSApplication stop:] stops the event loop; it won't do a clean termination, but we're not too concerned with that (at least not on the other platforms either so) + // we can't call -[NSApplication terminate:] because that will just quit the program, ensuring we never leave ui.Go() + [NSApp stop:NSApp]; + // simply calling -[NSApplication stop:] is not good enough, as the stop flag is only checked when an event comes in + // we have to create a "proper" event; a blank event will just throw an exception + [NSApp postEvent:makeDummyEvent() // TODO inline this + atStart:NO]; // not at start, just in case there are other events pending (TODO is this correct?) +} + +void cocoaMainLoop(void) +{ + [NSApp run]; +} diff --git a/uitask_darwin.go b/uitask_darwin.go index 6d044f6..6b886cf 100644 --- a/uitask_darwin.go +++ b/uitask_darwin.go @@ -10,6 +10,7 @@ import ( // #cgo LDFLAGS: -lobjc -framework Foundation -framework AppKit // #include "objc_darwin.h" +// #include "delegateuitask_darwin.h" import "C" var uitask chan func() @@ -32,7 +33,7 @@ func ui(main func()) error { uitask = make(chan func()) - NSApp, err := initCocoa() + err := initCocoa() if err != nil { return err } @@ -40,36 +41,18 @@ func ui(main func()) error { // 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 := C.objc_msgSend_noargs(_NSAutoreleasePool, _new) - 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 - C.objc_msgSend_noargs(pool, _release) + C.douitask(appDelegate, unsafe.Pointer(&f)) } }() go func() { main() uitask <- func() { - // -[NSApplication stop:] stops the event loop; it won't do a clean termination, but we're not too concerned with that (at least not on the other platforms either so) - // we can't call -[NSApplication terminate:] because that will just quit the program, ensuring we never leave ui.Go() - C.objc_msgSend_id(NSApp, _stop, NSApp) - // simply calling -[NSApplication stop:] is not good enough, as the stop flag is only checked when an event comes in - // we have to create a "proper" event; a blank event will just throw an exception - C.objc_msgSend_id_bool(NSApp, - _postEventAtStart, - C.makeDummyEvent(), - C.BOOL(C.NO)) // not at start, just in case there are other events pending (TODO is this correct?) + C.breakMainLoop() } }() - C.objc_msgSend_noargs(NSApp, _run) + C.cocoaMainLoop() return nil } @@ -84,21 +67,16 @@ var ( // _setDelegate in sysdata_darwin.go ) -func initCocoa() (NSApp C.id, err error) { +func initCocoa() (err error) { C.initBleh() // initialize bleh_darwin.m functions - NSApp = C.objc_msgSend_noargs(_NSApplication, _sharedApplication) - r := C.objc_msgSend_int(NSApp, _setActivationPolicy, - 0) // NSApplicationActivationPolicyRegular - if C.BOOL(uintptr(unsafe.Pointer(r))) != C.BOOL(C.YES) { - err = fmt.Errorf("error setting NSApplication activation policy (basically identifies our program as a separate program; needed for several things, such as Dock icon, application menu, window resizing, etc.) (unknown reason)") - return - } - C.objc_msgSend_bool(NSApp, _activateIgnoringOtherApps, C.BOOL(C.YES)) // TODO actually do C.NO here? Russ Cox does YES in his devdraw; the docs say the Finder does NO err = mkAppDelegate() if err != nil { return } - C.objc_msgSend_id(NSApp, _setDelegate, appDelegate) + if C.initCocoa(appDelegate) != C.YES { + err = fmt.Errorf("error setting NSApplication activation policy (basically identifies our program as a separate program; needed for several things, such as Dock icon, application menu, window resizing, etc.) (unknown reason)") + return + } err = mkAreaClass() return }