Now that we have everything up to and including Buttons up and running, we no longer need the darwintest.
This commit is contained in:
parent
4599e0d25e
commit
3e44abc1b8
|
@ -1,46 +0,0 @@
|
||||||
/* 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 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);
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
// 28 february 2014
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// #cgo LDFLAGS: -lobjc -framework Foundation -framework AppKit
|
|
||||||
// #include "objc_darwin.h"
|
|
||||||
// extern void windowShouldClose(id, SEL, id);
|
|
||||||
// extern void buttonClicked(id, SEL, id);
|
|
||||||
// extern void gotNotification(id, SEL, id);
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
// TODO move these around later
|
|
||||||
var (
|
|
||||||
_stop = sel_getUid("stop:")
|
|
||||||
)
|
|
||||||
|
|
||||||
//export windowShouldClose
|
|
||||||
func windowShouldClose(self C.id, sel C.SEL, sender C.id) {
|
|
||||||
fmt.Println("-[hello windowShouldClose:]")
|
|
||||||
C.objc_msgSend_id(NSApp, _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) {
|
|
||||||
fmt.Printf("got notification from %s\n", fromNSString(object))
|
|
||||||
}
|
|
||||||
|
|
||||||
func mk(name string, selW C.SEL, selB C.SEL, selN C.SEL) C.id {
|
|
||||||
class := newClass(name)
|
|
||||||
addDelegateMethod(class, selW, C.windowShouldClose)
|
|
||||||
addDelegateMethod(class, selB, C.buttonClicked)
|
|
||||||
addDelegateMethod(class, selN, C.gotNotification)
|
|
||||||
return objc_getClass(name)
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
// 27 february 2014
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// #cgo LDFLAGS: -lobjc -framework Foundation
|
|
||||||
// #include <stdlib.h>
|
|
||||||
// #include "objc_darwin.h"
|
|
||||||
// /* because cgo doesn't like Nil */
|
|
||||||
// Class NilClass = Nil;
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
var (
|
|
||||||
_NSObject_Class = C.object_getClass(_NSObject)
|
|
||||||
)
|
|
||||||
|
|
||||||
func newClass(name string) C.Class {
|
|
||||||
cname := C.CString(name)
|
|
||||||
defer C.free(unsafe.Pointer(cname))
|
|
||||||
|
|
||||||
c := C.objc_allocateClassPair(_NSObject_Class, cname, 0)
|
|
||||||
if c == C.NilClass {
|
|
||||||
panic("unable to create Objective-C class " + name)
|
|
||||||
}
|
|
||||||
C.objc_registerClassPair(c)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// according to errors spit out by cgo, C function pointers are unsafe.Pointer
|
|
||||||
func addDelegateMethod(class C.Class, sel C.SEL, imp unsafe.Pointer) {
|
|
||||||
// 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) {
|
|
||||||
panic("unable to add ourMethod")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,84 +0,0 @@
|
||||||
// 28 february 2014
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// #cgo LDFLAGS: -lobjc -framework Foundation
|
|
||||||
// #include <stdlib.h>
|
|
||||||
// #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)
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
/* 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>
|
|
||||||
|
|
||||||
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)
|
|
|
@ -1,126 +0,0 @@
|
||||||
// 27 february 2014
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// #cgo LDFLAGS: -lobjc -framework Foundation -framework AppKit
|
|
||||||
// #include <stdlib.h>
|
|
||||||
// #include "objc_darwin.h"
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
var (
|
|
||||||
_NSApplication = objc_getClass("NSApplication")
|
|
||||||
|
|
||||||
_sharedApplication = sel_getUid("sharedApplication")
|
|
||||||
_run = sel_getUid("run")
|
|
||||||
)
|
|
||||||
|
|
||||||
var NSApp C.id
|
|
||||||
var delegate C.id
|
|
||||||
var notesel C.SEL
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// need an NSApplication first - see https://github.com/TooTallNate/NodObjC/issues/21
|
|
||||||
NSApp = C.objc_msgSend_noargs(_NSApplication, _sharedApplication)
|
|
||||||
|
|
||||||
selW := sel_getUid("windowShouldClose:")
|
|
||||||
selB := sel_getUid("buttonClicked:")
|
|
||||||
selN := sel_getUid("gotNotification:")
|
|
||||||
mk("hello", selW, selB, selN)
|
|
||||||
delegate = objc_alloc(objc_getClass("hello"))
|
|
||||||
|
|
||||||
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
|
|
||||||
// TODO copy in the rest?
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
_NSAutoreleasePool = objc_getClass("NSAutoreleasePool")
|
|
||||||
|
|
||||||
_performSelectorOnMainThread =
|
|
||||||
sel_getUid("performSelectorOnMainThread:withObject:waitUntilDone:")
|
|
||||||
)
|
|
||||||
|
|
||||||
func notify(source string) {
|
|
||||||
// we need to make an NSAutoreleasePool, otherwise we get leak warnings on stderr
|
|
||||||
pool := objc_new(_NSAutoreleasePool)
|
|
||||||
src := toNSString(source)
|
|
||||||
C.objc_msgSend_sel_id_bool(
|
|
||||||
delegate,
|
|
||||||
_performSelectorOnMainThread,
|
|
||||||
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
|
|
||||||
objc_release(pool)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
_NSWindow = objc_getClass("NSWindow")
|
|
||||||
_NSButton = objc_getClass("NSButton")
|
|
||||||
|
|
||||||
_initWithContentRect = sel_getUid("initWithContentRect:styleMask:backing:defer:")
|
|
||||||
_setDelegate = sel_getUid("setDelegate:")
|
|
||||||
_makeKeyAndOrderFront = sel_getUid("makeKeyAndOrderFront:")
|
|
||||||
_contentView = sel_getUid("contentView")
|
|
||||||
_initWithFrame = sel_getUid("initWithFrame:")
|
|
||||||
_setTarget = sel_getUid("setTarget:")
|
|
||||||
_setAction = sel_getUid("setAction:")
|
|
||||||
_setBezelStyle = sel_getUid("setBezelStyle:")
|
|
||||||
_addSubview = sel_getUid("addSubview:")
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
style := uintptr(NSTitledWindowMask | NSClosableWindowMask)
|
|
||||||
backing := uintptr(NSBackingStoreBuffered)
|
|
||||||
deferx := C.BOOL(C.YES)
|
|
||||||
window := objc_alloc(_NSWindow)
|
|
||||||
window = objc_msgSend_rect_uint_uint_bool(window,
|
|
||||||
_initWithContentRect,
|
|
||||||
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, _contentView)
|
|
||||||
|
|
||||||
button := objc_alloc(_NSButton)
|
|
||||||
button = objc_msgSend_rect(button,
|
|
||||||
_initWithFrame,
|
|
||||||
20, 20, 200, 200)
|
|
||||||
C.objc_msgSend_id(button, _setTarget, delegate)
|
|
||||||
C.objc_msgSend_sel(button,
|
|
||||||
_setAction,
|
|
||||||
sel_getUid("buttonClicked:"))
|
|
||||||
objc_msgSend_uint(button, _setBezelStyle, NSRoundedBezelStyle)
|
|
||||||
C.objc_msgSend_id(windowView, _addSubview, button)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
<-time.After(5 * time.Second)
|
|
||||||
fmt.Println("five seconds passed; sending notification...")
|
|
||||||
notify("timer")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
C.objc_msgSend_noargs(NSApp, _run)
|
|
||||||
}
|
|
Loading…
Reference in New Issue