diff --git a/delegate_darwin.go b/delegate_darwin.go new file mode 100644 index 0000000..e903a13 --- /dev/null +++ b/delegate_darwin.go @@ -0,0 +1,100 @@ +// 27 february 2014 +package ui + +import ( + "fmt" + "unsafe" +) + +/* +This creates a class goAppDelegate that will be used as the delegate for /everything/. Specifically, it: + - runs uitask requests (uitask:) + - handles window close events (windowShouldClose:) + - handles window resize events (xxxx:) + - handles button click events (buttonClick:) +*/ + +// #cgo LDFLAGS: -lobjc -framework Foundation -framework AppKit +// #include +// #include "objc_darwin.h" +// extern void appDelegate_uitask(id, SEL, id); /* from uitask_darwin.go */ +// extern BOOL appDelegate_windowShouldClose(id, SEL, id); +// /* because cgo doesn't like Nil */ +// static Class NilClass = Nil; +import "C" + +var ( + appDelegate C.id +) + +const ( + _goAppDelegate = "goAppDelegate" +) + +var ( + _uitask = sel_getUid("uitask:") + _windowShouldClose = sel_getUid("windowShouldClose:") +) + +func mkAppDelegate() error { + var appdelegateclass C.Class + + appdelegateclass, err = makeDelegateClass(_goAppDelegate) + if err != nil { + return fmt.Errorf("error creating NSApplication delegate: %v", err) + } + err = addDelegateMethod(appdelegateclass, _uitask, + C.appDelegate_uitask, delegate_void) + if err != nil { + return fmt.Errorf("error adding NSApplication delegate uitask: method (to do UI tasks): %v", err) + } + err = addDelegateMethod(appdelegateclass, _windowShouldClose, + C.appDelegate_windowShouldClose, delegate_bool) + if err != nil { + return fmt.Errorf("error adding NSApplication delegate windowShouldClose: method (to handle window close button events): %v", err) + } + // 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)) + return nil +} + +//export appDelegate_windowShouldClose +func appDelegate_windowShouldClose(self C.id, sel C.SEL, win C.id) C.BOOL { + sysData := getSysData(win) + sysData.signal() + return C.BOOL(C.NO) // don't close +} + +// this actually constructs the delegate class + +var ( + _NSObject_Class = C.object_getClass(_NSObject) +) + +func makeDelegateClass(name string) (C.Class, error) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + + c := C.objc_allocateClassPair(_NSObject_Class, cname, 0) + if c == C.NilClass { + return C.NilClass, fmt.Errorf("unable to create Objective-C class %s; reason unknown", name) + } + C.objc_registerClassPair(c) + return c, nil +} + +var ( + delegate_void = []C.char{'v', '@', ':', '@', 0} // void (*)(id, SEL, id) + delegate_bool = []C.char{'#', '@', ':', '@', 0} // BOOL (*)(id, SEL, id) +) + +// according to errors spit out by cgo, C function pointers are unsafe.Pointer +func addDelegateMethod(class C.Class, sel C.SEL, imp unsafe.Pointer, ty []C.char) error { + ok := C.class_addMethod(class, sel, C.IMP(imp), &ty[0]) + if ok == C.BOOL(C.NO) { + // TODO get function name + return fmt.Errorf("unable to add selector %v/imp %v (reason unknown)", sel, imp) + } + return nil +} diff --git a/delegatetypes_darwin.go b/delegatetypes_darwin.go deleted file mode 100644 index 2bc3498..0000000 --- a/delegatetypes_darwin.go +++ /dev/null @@ -1,49 +0,0 @@ -// 27 february 2014 -package ui - -import ( - "fmt" - "unsafe" -) - -// #cgo LDFLAGS: -lobjc -framework Foundation -// #include -// #include "objc_darwin.h" -// /* because cgo doesn't like Nil */ -// Class NilClass = Nil; -import "C" - -var ( - _NSObject_Class = C.object_getClass(_NSObject) -) - -func makeDelegateClass(name string) (C.Class, error) { - cname := C.CString(name) - defer C.free(unsafe.Pointer(cname)) - - c := C.objc_allocateClassPair(_NSObject_Class, cname, 0) - if c == C.NilClass { - return C.NilClass, fmt.Errorf("unable to create Objective-C class %s; reason unknown", name) - } - C.objc_registerClassPair(c) - return c, nil -} - -// according to errors spit out by cgo, C function pointers are unsafe.Pointer -func addDelegateMethod(class C.Class, sel C.SEL, imp unsafe.Pointer) error { - // maps to void (*)(id, SEL, id) - ty := []C.char{'v', '@', ':', '@', 0} - - // clas methods get stored in the metaclass; the objc_allocateClassPair() docs say this will work - // metaclass := C.object_getClass(C.id(unsafe.Pointer(class))) - // we're adding instance methods, so just class will do - ok := C.class_addMethod(class, - sel, - C.IMP(imp), - &ty[0]) - if ok == C.BOOL(C.NO) { - // TODO get function name - return fmt.Errorf("unable to add selector %v/imp %v (reason unknown)", sel, imp) - } - return nil -} diff --git a/uitask_darwin.go b/uitask_darwin.go index 2a32f86..493fd8c 100644 --- a/uitask_darwin.go +++ b/uitask_darwin.go @@ -7,17 +7,8 @@ import ( "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_uitask(id, SEL, id); import "C" // temporary for now @@ -30,7 +21,6 @@ var ( _NSAutoreleasePool = objc_getClass("NSAutoreleasePool") _NSValue = objc_getClass("NSValue") - _uitask = sel_getUid("uitask:") _valueWithPointer = sel_getUid("valueWithPointer:") _performSelectorOnMainThread = sel_getUid("performSelectorOnMainThread:withObject:waitUntilDone:") @@ -43,7 +33,7 @@ func ui(main func()) error { uitask = make(chan func()) - NSApp, appDelegate, err := initCocoa() + NSApp, err := initCocoa() if err != nil { return err } @@ -73,34 +63,15 @@ func ui(main func()) error { // TODO move to init_darwin.go? -const ( - _goAppDelegate = "goAppDelegate" -) - var ( _NSApplication = objc_getClass("NSApplication") _sharedApplication = sel_getUid("sharedApplication") ) -func initCocoa() (NSApp C.id, appDelegate C.id, err error) { - var appdelegateclass C.Class - +func initCocoa() (NSApp C.id, err error) { NSApp = C.objc_msgSend_noargs(_NSApplication, _sharedApplication) - appdelegateclass, err = makeDelegateClass(_goAppDelegate) - if err != nil { - err = fmt.Errorf("error creating NSApplication delegate: %v", err) - return - } - err = addDelegateMethod(appdelegateclass, _uitask, C.appDelegate_uitask) - if err != nil { - 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)) - + err = mkAppDelegate() return }