app: remove OS X display link timer

The CGL display link is a timer keyed on screen refresh rate. It made
sense to use it when the app package controlled the screen paint
cycle. Now that the paint cycle control has moved to the user, and
given that we have always made the equivalent of Publish block until
vsync, it is just complicating matters. The user can come up with
their own timer, and safely dedicate a goroutine to event handling
that paints as fast as it likes without running over the vsync time.

A version of this for iOS will follow (giving up on the timer provided
by GLKViewController) when I get my iOS setup working again.

(Note there is also a bug in the way drawgl works presently. This CL
doesn't fix the bug, but is a first step in untangling the draw loop
so I can fix it.)

Change-Id: I464d5b15f018527d98b792026fb3899681f24e4b
Reviewed-on: https://go-review.googlesource.com/15470
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
diff --git a/app/darwin_amd64.go b/app/darwin_amd64.go
index dcee145..6935769 100644
--- a/app/darwin_amd64.go
+++ b/app/darwin_amd64.go
@@ -12,7 +12,7 @@
 
 /*
 #cgo CFLAGS: -x objective-c
-#cgo LDFLAGS: -framework Cocoa -framework OpenGL -framework QuartzCore
+#cgo LDFLAGS: -framework Cocoa -framework OpenGL
 #import <Carbon/Carbon.h> // for HIToolbox/Events.h
 #import <Cocoa/Cocoa.h>
 #include <pthread.h>
@@ -70,45 +70,52 @@
 // events in runApp, it starts loop on another goroutine. It is locked
 // to an OS thread for its OpenGL context.
 //
-// Two Cocoa threads deliver draw signals to loop. The primary source of
-// draw events is the CVDisplayLink timer, which is tied to the display
-// vsync. Secondary draw events come from [NSView drawRect:] when the
-// window is resized.
+// The loop processes GL calls until a publish event appears.
+// Then it runs any remaining GL calls and flushes the screen.
+//
+// As NSOpenGLCPSwapInterval is set to 1, the call to CGLFlushDrawable
+// blocks until the screen refresh.
 func (a *app) loop(ctx C.GLintptr) {
 	runtime.LockOSThread()
 	C.makeCurrentContext(ctx)
 
 	workAvailable := a.worker.WorkAvailable()
+
 	for {
 		select {
 		case <-workAvailable:
 			a.worker.DoWork()
-		case <-draw:
+		case <-theApp.publish:
 		loop1:
 			for {
 				select {
 				case <-workAvailable:
 					a.worker.DoWork()
-				case <-theApp.publish:
-					C.CGLFlushDrawable(C.CGLGetCurrentContext())
-					theApp.publishResult <- PublishResult{}
+				default:
 					break loop1
 				}
 			}
-			drawDone <- struct{}{}
+			C.CGLFlushDrawable(C.CGLGetCurrentContext())
+			theApp.publishResult <- PublishResult{}
+			select {
+			case drawDone <- struct{}{}:
+			default:
+			}
 		}
 	}
 }
 
-var (
-	draw     = make(chan struct{})
-	drawDone = make(chan struct{})
-)
+var drawDone = make(chan struct{})
 
+// drawgl is used by Cocoa to occasionally request screen updates.
+//
 //export drawgl
 func drawgl() {
-	draw <- struct{}{}
-	<-drawDone
+	switch theApp.lifecycleStage {
+	case lifecycle.StageFocused, lifecycle.StageVisible:
+		theApp.Send(paint.Event{})
+		<-drawDone
+	}
 }
 
 //export startloop
diff --git a/app/darwin_amd64.m b/app/darwin_amd64.m
index 49ef8e2..c7c5fcd 100644
--- a/app/darwin_amd64.m
+++ b/app/darwin_amd64.m
@@ -11,14 +11,6 @@
 #import <Cocoa/Cocoa.h>
 #import <Foundation/Foundation.h>
 #import <OpenGL/gl3.h>
-#import <QuartzCore/CVReturn.h>
-#import <QuartzCore/CVBase.h>
-
-static CVReturn displayLinkDraw(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
-{
-	drawgl();
-	return kCVReturnSuccess;
-}
 
 void makeCurrentContext(GLintptr context) {
 	NSOpenGLContext* ctx = (NSOpenGLContext*)context;
@@ -33,10 +25,8 @@
 	return id;
 }
 
-
 @interface MobileGLView : NSOpenGLView<NSApplicationDelegate, NSWindowDelegate>
 {
-	CVDisplayLinkRef displayLink;
 }
 @end
 
@@ -46,13 +36,6 @@
 	GLint swapInt = 1;
 	[[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
 
-	CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
-	CVDisplayLinkSetOutputCallback(displayLink, &displayLinkDraw, self);
-
-	CGLContextObj cglContext = [[self openGLContext] CGLContextObj];
-	CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
-	CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat);
-
 	// Using attribute arrays in OpenGL 3.3 requires the use of a VBA.
 	// But VBAs don't exist in ES 2. So we bind a default one.
 	GLuint vba;
@@ -96,11 +79,8 @@
 }
 
 - (void)drawRect:(NSRect)theRect {
-	// Called during resize. Do an extra draw if we are visible.
-	// This gets rid of flicker when resizing.
-	if (CVDisplayLinkIsRunning(displayLink)) {
-		drawgl();
-	}
+	// Called during resize. This gets rid of flicker when resizing.
+	drawgl();
 }
 
 - (void)mouseDown:(NSEvent *)theEvent {
@@ -136,7 +116,6 @@
 	[[NSRunningApplication currentApplication] activateWithOptions:(NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps)];
 	[self.window makeKeyAndOrderFront:self];
 	lifecycleVisible();
-	CVDisplayLinkStart(displayLink);
 }
 
 - (void)applicationWillTerminate:(NSNotification *)aNotification {
@@ -144,17 +123,14 @@
 }
 
 - (void)applicationDidHide:(NSNotification *)aNotification {
-	CVDisplayLinkStop(displayLink);
 	lifecycleAlive();
 }
 
 - (void)applicationWillUnhide:(NSNotification *)notification {
 	lifecycleVisible();
-	CVDisplayLinkStart(displayLink);
 }
 
 - (void)windowWillClose:(NSNotification *)notification {
-	CVDisplayLinkStop(displayLink);
 	lifecycleAlive();
 }
 @end
@@ -204,7 +180,6 @@
 }
 @end
 
-
 void
 runApp(void) {
 	[NSAutoreleasePool new];