package main import ( _ "embed" "log" "sync" "time" "unsafe" "github.com/progrium/macdriver/cocoa" "github.com/progrium/macdriver/core" "github.com/progrium/macdriver/objc" ) /* #cgo CFLAGS: -x objective-c -DGL_SILENCE_DEPRECATION #cgo LDFLAGS: -framework OpenGL -framework AppKit #import "MyOpenGLView.h" */ import "C" //go:generate ibtool --output-format human-readable-text --compile app.nib app.xib //go:embed app.nib var nibData []byte var renderFuncs sync.Map var app cocoa.NSApplication var view cocoa.NSView func main() { app = cocoa.NSApp_WithDidLaunch(didLaunch) app.SetActivationPolicy(cocoa.NSApplicationActivationPolicyRegular) app.Run() } func didLaunch(objc.Object) { loadNib(app, nibData, func(o objc.Object) { switch o.Class().String() { case "NSWindow": view = cocoa.NSWindow{Object: o}.ContentView() } }) // Did we find a window? if view.Pointer() == 0 { log.Fatal("Could not locate window") } // Is the content view a MyOpenGLView? if view.Class().String() != "MyOpenGLView" { log.Fatal("Content view is the wrong type") } // Initialize OpenGL err := setupGL() if err != nil { log.Fatal(err) } // Request updates at 60Hz go func() { for range time.Tick(time.Second / 60) { core.Dispatch(func() { view.Send("setNeedsDisplay:", true) }) } }() app.ActivateIgnoringOtherApps(true) } func loadNib(owner objc.Object, data []byte, fn func(objc.Object)) bool { nsdata := core.NSData_WithBytes(data, uint64(len(data))) bundle := cocoa.NSBundle_Main() nib := objc.Get("NSNib").Alloc().Send("initWithNibData:bundle:", nsdata, bundle) if fn == nil { return nib.Send("instantiateWithOwner:topLevelObjects:", owner, nil).Bool() } var ptr uintptr ok := nib.Send("instantiateWithOwner:topLevelObjects:", owner, &ptr).Bool() if !ok { return false } tlo := core.NSArray{Object: objc.ObjectPtr(ptr)} for i, n := uint64(0), tlo.Count(); i < n; i++ { fn(tlo.ObjectAtIndex(i)) } return true } type glRenderFunc func(cocoa.NSView) bool func setGLRenderFunc(view objc.Object, fn glRenderFunc) { if view.Class().String() != "MyOpenGLView" { panic("view is not a MyOpenGLView") } renderFuncs.Store(view.Pointer(), fn) } //export callGLRenderFunc func callGLRenderFunc(id unsafe.Pointer) C.bool { fn, ok := renderFuncs.Load(uintptr(id)) if !ok { return false } o := objc.ObjectPtr(uintptr(id)) return (C.bool)(fn.(glRenderFunc)(cocoa.NSView{Object: o})) } //export deleteGLRenderFunc func deleteGLRenderFunc(id unsafe.Pointer) { renderFuncs.Delete(uintptr(id)) }