From fabddf9e0ecfe5097b1cf0c8a9724cbf4e533e13 Mon Sep 17 00:00:00 2001
From: Pietro Gagliardi <pietro10@mac.com>
Date: Fri, 28 Feb 2014 19:34:18 -0500
Subject: [PATCH] Added a .m file to handle the edge cases in objc_msgSend()
 wrappers that cannot be easily expressed otherwise.

---
 darwintest/bleh_darwin.go | 27 +++++++++++++++++++++++
 darwintest/bleh_darwin.m  | 46 +++++++++++++++++++++++++++++++++++++++
 darwintest/objc_darwin.h  |  8 ++++---
 darwintest/runtimetest.go | 30 +++++++++----------------
 4 files changed, 88 insertions(+), 23 deletions(-)
 create mode 100644 darwintest/bleh_darwin.go
 create mode 100644 darwintest/bleh_darwin.m

diff --git a/darwintest/bleh_darwin.go b/darwintest/bleh_darwin.go
new file mode 100644
index 0000000..79fe4d3
--- /dev/null
+++ b/darwintest/bleh_darwin.go
@@ -0,0 +1,27 @@
+// 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)
+}
diff --git a/darwintest/bleh_darwin.m b/darwintest/bleh_darwin.m
new file mode 100644
index 0000000..5ed2155
--- /dev/null
+++ b/darwintest/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 <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);
+}
diff --git a/darwintest/objc_darwin.h b/darwintest/objc_darwin.h
index 85bf0b4..abf9712 100644
--- a/darwintest/objc_darwin.h
+++ b/darwintest/objc_darwin.h
@@ -14,6 +14,8 @@ The format should be self-explanatory.
 #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;
 
@@ -48,14 +50,14 @@ inline id objc_msgSend_noargs(id obj, SEL sel)
 
 m1(str, char *)		/* TODO Go string? */
 m1(id, id)
-/* TODO NSRect */
+extern id _objc_msgSend_rect(id obj, SEL sel, int64_t x, int64_t y, int64_t w, int64_t h);
 m1(sel, SEL)
-m1(uint, NSUInteger)
+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)
 
-/* TODO NSRect */
+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)
diff --git a/darwintest/runtimetest.go b/darwintest/runtimetest.go
index f92e0f8..46933a4 100644
--- a/darwintest/runtimetest.go
+++ b/darwintest/runtimetest.go
@@ -10,11 +10,6 @@ import (
 // #cgo LDFLAGS: -lobjc -framework Foundation -framework AppKit
 // #include <stdlib.h>
 // #include "objc_darwin.h"
-// /* avoid depending on Objective-C */
-// #include <CoreGraphics/CGGeometry.h>
-// /* cgo doesn't handle ... */
-// id objc_msgSend_NSRect_uint_uint_bool(id obj, SEL sel, CGRect a, NSUInteger b, NSUInteger c, BOOL d) { return objc_msgSend(obj, sel, a, b, c, d); }
-// id objc_msgSend_NSRect(id obj, SEL sel, CGRect a) { return objc_msgSend(obj, sel, a); }
 // Class NilClass = Nil; /* for newtypes.go */
 // id Nilid = nil;
 import "C"
@@ -97,7 +92,7 @@ func notify(source string) {
 		sel_getUid("performSelectorOnMainThread:withObject:waitUntilDone:"),
 		notesel,
 		src,
-		C.BOOL(C.TRUE))		// 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.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"))
 }
@@ -109,15 +104,13 @@ func main() {
 	setDelegate := sel_getUid("setDelegate:")
 	makeKeyAndOrderFront := sel_getUid("makeKeyAndOrderFront:")
 
-	rect := C.CGRect{
-		origin:	C.CGPoint{100, 100},
-		size:		C.CGSize{320, 240},
-	}
-	style := C.NSUInteger(NSTitledWindowMask | NSClosableWindowMask)
-	backing := C.NSUInteger(NSBackingStoreBuffered)
+	style := uintptr(NSTitledWindowMask | NSClosableWindowMask)
+	backing := uintptr(NSBackingStoreBuffered)
 	deferx := C.BOOL(C.YES)
 	window := C.objc_msgSend_noargs(NSWindow, alloc)
-	window = C.objc_msgSend_NSRect_uint_uint_bool(window, NSWindowinit, rect, style, backing, deferx)
+	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)
@@ -126,21 +119,18 @@ func main() {
 
 	NSButton := objc_getClass("NSButton")
 	button := C.objc_msgSend_noargs(NSButton, alloc)
-	button = C.objc_msgSend_NSRect(button,
+	button = objc_msgSend_rect(button,
 		sel_getUid("initWithFrame:"),
-		C.CGRect{
-			origin:	C.CGPoint{20, 20},
-			size:		C.CGSize{200, 200},
-		})
+		20, 20, 200, 200)
 	C.objc_msgSend_id(button,
 		sel_getUid("setTarget:"),
 		delegate)
 	C.objc_msgSend_sel(button,
 		sel_getUid("setAction:"),
 		sel_getUid("buttonClicked:"))
-	C.objc_msgSend_uint(button,
+	objc_msgSend_uint(button,
 		sel_getUid("setBezelStyle:"),
-		C.NSUInteger(NSRoundedBezelStyle))
+		NSRoundedBezelStyle)
 	C.objc_msgSend_id(windowView,
 		sel_getUid("addSubview:"),
 		button)