Last active
September 15, 2016 16:00
-
-
Save andlabs/9282874 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 28 february 2014 | |
package main | |
/* | |
These are wrapper functions for the functions in bleh_darwin.m to wrap around stdint.h type casting. | |
This will eventually be expanded to include the other Objective-C runtime support functions. | |
*/ | |
// #cgo LDFLAGS: -lobjc -framework Foundation | |
// #include "objc_darwin.h" | |
import "C" | |
func objc_msgSend_rect(obj C.id, sel C.SEL, x int, y int, w int, h int) C.id { | |
return C._objc_msgSend_rect(obj, sel, | |
C.int64_t(x), C.int64_t(y), C.int64_t(w), C.int64_t(h)) | |
} | |
func objc_msgSend_uint(obj C.id, sel C.SEL, a uintptr) C.id { | |
return C._objc_msgSend_uint(obj, sel, C.uintptr_t(a)) | |
} | |
func objc_msgSend_rect_uint_uint_bool(obj C.id, sel C.SEL, x int, y int, w int, h int, b uintptr, c uintptr, d C.BOOL) C.id { | |
return C._objc_msgSend_rect_uint_uint_bool(obj, sel, | |
C.int64_t(x), C.int64_t(y), C.int64_t(w), C.int64_t(h), | |
C.uintptr_t(b), C.uintptr_t(c), d) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 28 february 2014 */ | |
/* | |
I wanted to avoid invoking Objective-C directly, preferring to do everything directly with the API. However, there are some things that simply cannot be done too well; for those situations, there's this. It does use the Objective-C runtime, eschewing the actual Objective-C part of this being an Objective-C file. | |
The main culprits are: | |
- data types listed as being defined in nonexistent headers | |
- 32-bit/64-bit type differences that are more than just a different typedef | |
Go wrapper functions (bleh_darwin.go) call these directly and take care of stdint.h -> Go type conversions. | |
*/ | |
#include <objc/message.h> | |
#include <objc/objc.h> | |
#include <objc/runtime.h> | |
#include <stdint.h> | |
#include <Foundation/NSGeometry.h> | |
/* | |
NSUInteger is listed as being in <objc/NSObjCRuntime.h>... which doesn't exist. Rather than relying on undocumented behavior or explicitly typedef-ing NSUInteger to the (documented) unsigned long, I'll just place things here for maximum safety. I use uintptr_t as that should encompass every possible unsigned long. | |
*/ | |
id _objc_msgSend_uint(id obj, SEL sel, uintptr_t a) | |
{ | |
return objc_msgSend(obj, sel, (NSUInteger) a); | |
} | |
/* | |
These are the objc_msgSend() wrappers around NSRect. The problem is that while on 32-bit systems, NSRect is a concrete structure, on 64-bit systems it's just a typedef to CGRect. While in practice just using CGRect everywhere seems to work, better to be safe than sorry. | |
I use int64_t for maximum safety, as my coordinates are stored as Go ints and Go int -> C int (which is what is documented as happening) isn't reliable. | |
*/ | |
#define OurRect() (NSMakeRect((CGFloat) x, (CGFloat) y, (CGFloat) w, (CGFloat) h)) | |
id _objc_msgSend_rect(id obj, SEL sel, int64_t x, int64_t y, int64_t w, int64_t h) | |
{ | |
return objc_msgSend(obj, sel, OurRect()); | |
} | |
id _objc_msgSend_rect_uint_uint_bool(id obj, SEL sel, int64_t x, int64_t y, int64_t w, int64_t h, uintptr_t b, uintptr_t c, BOOL d) | |
{ | |
return objc_msgSend(obj, sel, OurRect(), (NSUInteger) b, (NSUInteger) c, d); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 27 february 2014 | |
package main | |
import ( | |
"fmt" | |
"unsafe" | |
) | |
// #cgo CFLAGS: -Dqqq | |
// #cgo LDFLAGS: -lobjc -framework Foundation | |
// #include <stdlib.h> | |
// #include "objc_darwin.h" | |
// extern void windowShouldClose(id, SEL, id); | |
// extern void buttonClicked(id, SEL, id); | |
// extern void gotNotification(id, SEL, id); | |
// /* cgo doesn't like nil or Nil */ | |
// extern Class NilClass; /* in runtimetest.go because of cgo limitations */ | |
// extern id Nilid; | |
import "C" | |
var NSObject = C.object_getClass(objc_getClass("NSObject")) | |
func newClass(name string) C.Class { | |
cname := C.CString(name) | |
defer C.free(unsafe.Pointer(cname)) | |
c := C.objc_allocateClassPair(NSObject, cname, 0) | |
if c == C.NilClass { | |
panic("unable to create Objective-C class " + name) | |
} | |
return c | |
} | |
//export windowShouldClose | |
func windowShouldClose(self C.id, sel C.SEL, sender C.id) { | |
fmt.Println("-[hello windowShouldClose:]") | |
C.objc_msgSend_id(NSApp, | |
sel_getUid("stop:"), | |
sender) | |
} | |
//export buttonClicked | |
func buttonClicked(self C.id, sel C.SEL, sender C.id) { | |
fmt.Println("button clicked; sending notification...") | |
notify("button") | |
} | |
//export gotNotification | |
func gotNotification(self C.id, sel C.SEL, object C.id) { | |
source := (*C.char)(unsafe.Pointer( | |
C.objc_msgSend_noargs(object, | |
sel_getUid("UTF8String")))) | |
fmt.Println("got notification from %s", | |
C.GoString(source)) | |
} | |
func addOurMethod(class C.Class, sel C.SEL, imp C.IMP) { | |
// ty := []C.char{'v', '@', ':', 0} // according to the example for class_addMethod() | |
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))) | |
// ok := C.class_addMethod(metaclass, | |
ok := C.class_addMethod(class, | |
sel, | |
imp, | |
&ty[0]) | |
if ok == C.BOOL(C.NO) { | |
panic("unable to add ourMethod") | |
} | |
} | |
func mk(name string, selW C.SEL, selB C.SEL, selN C.SEL) C.id { | |
class := newClass(name) | |
addOurMethod(class, selW, | |
// using &C.ourMethod causes faults for some reason | |
C.IMP(unsafe.Pointer(C.windowShouldClose))) | |
C.objc_registerClassPair(class) | |
addOurMethod(class, selB, | |
C.IMP(unsafe.Pointer(C.buttonClicked))) | |
addOurMethod(class, selN, | |
C.IMP(unsafe.Pointer(C.gotNotification))) | |
return objc_getClass(name) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 28 february 2014 */ | |
/* | |
This includes all Objective-C runtime headers for convenience. It also creates wrappers around objc_msgSend() out of necessity. | |
cgo doesn't support calling variable argument list C functions, so objc_msgSend() cannot be called directly. | |
Furthermore, Objective-C selectors work by basically sending the arguments to objc_msgSend() verbatim across the wire. This basically means we're stuck making wrapper functions for every possible argument list. What fun! | |
The format should be self-explanatory. | |
*/ | |
#include <objc/message.h> | |
#include <objc/objc.h> | |
#include <objc/runtime.h> | |
#include <stdint.h> | |
/* TODO this HAS to be unsafe, but <objc/NSObjCRuntime.h> not found?! */ | |
typedef unsigned long NSUInteger; | |
inline id objc_msgSend_noargs(id obj, SEL sel) | |
{ | |
return objc_msgSend(obj, sel); | |
} | |
#define m1(name, type1) \ | |
inline id objc_msgSend_ ## name (id obj, SEL sel, type1 a) \ | |
{ \ | |
return objc_msgSend(obj, sel, a); \ | |
} | |
#define m2(name, type1, type2) \ | |
inline id objc_msgSend_ ## name (id obj, SEL sel, type1 a, type2 b) \ | |
{ \ | |
return objc_msgSend(obj, sel, a, b); \ | |
} | |
#define m3(name, type1, type2, type3) \ | |
inline id objc_msgSend_ ## name (id obj, SEL sel, type1 a, type2 b, type3 c) \ | |
{ \ | |
return objc_msgSend(obj, sel, a, b, c); \ | |
} | |
#define m4(name, type1, type2, type3, type4) \ | |
inline id objc_msgSend_ ## name (id obj, SEL sel, type1 a, type2 b, type3 c, type4 d) \ | |
{ \ | |
return objc_msgSend(obj, sel, a, b, c, d); \ | |
} | |
m1(str, char *) /* TODO Go string? */ | |
m1(id, id) | |
extern id _objc_msgSend_rect(id obj, SEL sel, int64_t x, int64_t y, int64_t w, int64_t h); | |
m1(sel, SEL) | |
extern id _objc_msgSend_uint(id obj, SEL sel, uintptr_t a); | |
m2(id_id, id, id) | |
m3(id_id_id, id, id, id) | |
m3(sel_id_bool, SEL, id, BOOL) | |
extern id _objc_msgSend_rect_uint_uint_bool(id obj, SEL sel, int64_t x, int64_t y, int64_t w, int64_t h, uintptr_t b, uintptr_t c, BOOL d); | |
m4(id_sel_id_id, id, SEL, id, id) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 27 february 2014 | |
package main | |
import ( | |
"fmt" | |
"unsafe" | |
"time" | |
) | |
// #cgo LDFLAGS: -lobjc -framework Foundation -framework AppKit | |
// #include <stdlib.h> | |
// #include "objc_darwin.h" | |
// Class NilClass = Nil; /* for newtypes.go */ | |
// id Nilid = nil; | |
import "C" | |
func objc_getClass(class string) C.id { | |
cclass := C.CString(class) | |
defer C.free(unsafe.Pointer(cclass)) | |
return C.objc_getClass(cclass) | |
} | |
func sel_getUid(sel string) C.SEL { | |
csel := C.CString(sel) | |
defer C.free(unsafe.Pointer(csel)) | |
return C.sel_getUid(csel) | |
} | |
var NSApp C.id | |
var defNC C.id | |
var delegate C.id | |
var notesel C.SEL | |
func init() { | |
// need an NSApplication first - see https://github.com/TooTallNate/NodObjC/issues/21 | |
NSApplication := objc_getClass("NSApplication") | |
sharedApplication := sel_getUid("sharedApplication") | |
NSApp = C.objc_msgSend_noargs(NSApplication, sharedApplication) | |
defNC = C.objc_msgSend_noargs( | |
objc_getClass("NSNotificationCenter"), | |
sel_getUid("defaultCenter")) | |
selW := sel_getUid("windowShouldClose:") | |
selB := sel_getUid("buttonClicked:") | |
selN := sel_getUid("gotNotification:") | |
mk("hello", selW, selB, selN) | |
delegate = C.objc_msgSend_noargs( | |
objc_getClass("hello"), | |
alloc) | |
notesel = selN | |
} | |
const ( | |
NSBorderlessWindowMask = 0 | |
NSTitledWindowMask = 1 << 0 | |
NSClosableWindowMask = 1 << 1 | |
NSMiniaturizableWindowMask = 1 << 2 | |
NSResizableWindowMask = 1 << 3 | |
NSTexturedBackgroundWindowMask = 1 << 8 | |
) | |
const ( | |
// NSBackingStoreRetained = 0 // "You should not use this mode." | |
// NSBackingStoreNonretained = 1 // "You should not use this mode." | |
NSBackingStoreBuffered = 2 | |
) | |
const ( | |
NSRoundedBezelStyle = 1 | |
) | |
var alloc = sel_getUid("alloc") | |
func notify(source string) { | |
csource := C.CString(source) | |
defer C.free(unsafe.Pointer(csource)) | |
// we need to make an NSAutoreleasePool, otherwise we get leak warnings on stderr | |
pool := C.objc_msgSend_noargs( | |
objc_getClass("NSAutoreleasePool"), | |
sel_getUid("new")) | |
src := C.objc_msgSend_str( | |
objc_getClass("NSString"), | |
sel_getUid("stringWithUTF8String:"), | |
csource) | |
C.objc_msgSend_sel_id_bool( | |
delegate, | |
sel_getUid("performSelectorOnMainThread:withObject:waitUntilDone:"), | |
notesel, | |
src, | |
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, | |
sel_getUid("release")) | |
} | |
func main() { | |
NSWindow := objc_getClass("NSWindow") | |
NSWindowinit := | |
sel_getUid("initWithContentRect:styleMask:backing:defer:") | |
setDelegate := sel_getUid("setDelegate:") | |
makeKeyAndOrderFront := sel_getUid("makeKeyAndOrderFront:") | |
style := uintptr(NSTitledWindowMask | NSClosableWindowMask) | |
backing := uintptr(NSBackingStoreBuffered) | |
deferx := C.BOOL(C.YES) | |
window := C.objc_msgSend_noargs(NSWindow, alloc) | |
window = objc_msgSend_rect_uint_uint_bool(window, NSWindowinit, | |
100, 100, 320, 240, | |
style, backing, deferx) | |
C.objc_msgSend_id(window, makeKeyAndOrderFront, window) | |
C.objc_msgSend_id(window, setDelegate, | |
delegate) | |
windowView := C.objc_msgSend_noargs(window, | |
sel_getUid("contentView")) | |
NSButton := objc_getClass("NSButton") | |
button := C.objc_msgSend_noargs(NSButton, alloc) | |
button = objc_msgSend_rect(button, | |
sel_getUid("initWithFrame:"), | |
20, 20, 200, 200) | |
C.objc_msgSend_id(button, | |
sel_getUid("setTarget:"), | |
delegate) | |
C.objc_msgSend_sel(button, | |
sel_getUid("setAction:"), | |
sel_getUid("buttonClicked:")) | |
objc_msgSend_uint(button, | |
sel_getUid("setBezelStyle:"), | |
NSRoundedBezelStyle) | |
C.objc_msgSend_id(windowView, | |
sel_getUid("addSubview:"), | |
button) | |
go func() { | |
for { | |
<-time.After(5 * time.Second) | |
fmt.Println("five seconds passed; sending notification...") | |
notify("timer") | |
} | |
}() | |
C.objc_msgSend_noargs(NSApp, | |
sel_getUid("run")) | |
} | |
func helloworld() { | |
_hello := C.CString("hello, world\n") | |
defer C.free(unsafe.Pointer(_hello)) | |
NSString := objc_getClass("NSString") | |
stringWithUTF8String := | |
sel_getUid("stringWithUTF8String:") | |
str := C.objc_msgSend_str(NSString, | |
stringWithUTF8String, | |
_hello) | |
UTF8String := sel_getUid("UTF8String") | |
res := C.objc_msgSend_noargs(str, | |
UTF8String) | |
cres := (*C.char)(unsafe.Pointer(res)) | |
fmt.Printf("%s", C.GoString(cres)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment