diff --git a/bleh_darwin.m b/bleh_darwin.m new file mode 100644 index 0000000..f24806c --- /dev/null +++ b/bleh_darwin.m @@ -0,0 +1,46 @@ +/* 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 +#include +#include + +#include + +#include + +/* +NSUInteger is listed as being in ... which doesn't exist. Rather than relying on undocumented header file locations 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); +} diff --git a/objc_darwin.go b/objc_darwin.go new file mode 100644 index 0000000..524d9f7 --- /dev/null +++ b/objc_darwin.go @@ -0,0 +1,84 @@ +// 28 february 2014 +package main + +import ( + "unsafe" +) + +// #cgo LDFLAGS: -lobjc -framework Foundation +// #include +// #include "objc_darwin.h" +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) +} + +// Common Objective-C types and selectors. +var ( + _NSObject = objc_getClass("NSObject") + _NSString = objc_getClass("NSString") + + _alloc = sel_getUid("alloc") + _new = sel_getUid("new") + _release = sel_getUid("release") + _stringWithUTF8String = sel_getUid("stringWithUTF8String:") + _UTF8String = sel_getUid("UTF8String") +) + +// some helper functions + +func objc_alloc(class C.id) C.id { + return C.objc_msgSend_noargs(class, _alloc) +} + +func objc_new(class C.id) C.id { + return C.objc_msgSend_noargs(class, _new) +} + +func objc_release(obj C.id) { + C.objc_msgSend_noargs(obj, _release) +} + +func toNSString(str string) C.id { + cstr := C.CString(str) + defer C.free(unsafe.Pointer(cstr)) + + return C.objc_msgSend_str(_NSString, + _stringWithUTF8String, + cstr) +} + +func fromNSString(str C.id) string { + cstr := C.objc_msgSend_noargs(str, _UTF8String) + return C.GoString((*C.char)(unsafe.Pointer(cstr))) +} + +/* +These are wrapper functions for the functions in bleh_darwin.m to wrap around stdint.h type casting. +*/ + +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) +} diff --git a/objc_darwin.h b/objc_darwin.h new file mode 100644 index 0000000..171d260 --- /dev/null +++ b/objc_darwin.h @@ -0,0 +1,60 @@ +/* 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 +#include +#include + +#include + +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)