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>
245 lines
6.7 KiB
Objective-C
245 lines
6.7 KiB
Objective-C
// Copyright 2014 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// +build darwin
|
|
|
|
#include "_cgo_export.h"
|
|
#include <pthread.h>
|
|
#include <stdio.h>
|
|
|
|
#import <Cocoa/Cocoa.h>
|
|
#import <Foundation/Foundation.h>
|
|
#import <OpenGL/gl3.h>
|
|
|
|
void makeCurrentContext(GLintptr context) {
|
|
NSOpenGLContext* ctx = (NSOpenGLContext*)context;
|
|
[ctx makeCurrentContext];
|
|
}
|
|
|
|
uint64 threadID() {
|
|
uint64 id;
|
|
if (pthread_threadid_np(pthread_self(), &id)) {
|
|
abort();
|
|
}
|
|
return id;
|
|
}
|
|
|
|
@interface MobileGLView : NSOpenGLView<NSApplicationDelegate, NSWindowDelegate>
|
|
{
|
|
}
|
|
@end
|
|
|
|
@implementation MobileGLView
|
|
- (void)prepareOpenGL {
|
|
[self setWantsBestResolutionOpenGLSurface:YES];
|
|
GLint swapInt = 1;
|
|
[[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
|
|
|
|
// 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;
|
|
glGenVertexArrays(1, &vba);
|
|
glBindVertexArray(vba);
|
|
|
|
startloop((GLintptr)[self openGLContext]);
|
|
}
|
|
|
|
- (void)reshape {
|
|
[super reshape];
|
|
|
|
// Calculate screen PPI.
|
|
//
|
|
// Note that the backingScaleFactor converts from logical
|
|
// pixels to actual pixels, but both of these units vary
|
|
// independently from real world size. E.g.
|
|
//
|
|
// 13" Retina Macbook Pro, 2560x1600, 227ppi, backingScaleFactor=2, scale=3.15
|
|
// 15" Retina Macbook Pro, 2880x1800, 220ppi, backingScaleFactor=2, scale=3.06
|
|
// 27" iMac, 2560x1440, 109ppi, backingScaleFactor=1, scale=1.51
|
|
// 27" Retina iMac, 5120x2880, 218ppi, backingScaleFactor=2, scale=3.03
|
|
NSScreen *screen = [NSScreen mainScreen];
|
|
double screenPixW = [screen frame].size.width * [screen backingScaleFactor];
|
|
|
|
CGDirectDisplayID display = (CGDirectDisplayID)[[[screen deviceDescription] valueForKey:@"NSScreenNumber"] intValue];
|
|
CGSize screenSizeMM = CGDisplayScreenSize(display); // in millimeters
|
|
float ppi = 25.4 * screenPixW / screenSizeMM.width;
|
|
float pixelsPerPt = ppi/72.0;
|
|
|
|
// The width and height reported to the geom package are the
|
|
// bounds of the OpenGL view. Several steps are necessary.
|
|
// First, [self bounds] gives us the number of logical pixels
|
|
// in the view. Multiplying this by the backingScaleFactor
|
|
// gives us the number of actual pixels.
|
|
NSRect r = [self bounds];
|
|
int w = r.size.width * [screen backingScaleFactor];
|
|
int h = r.size.height * [screen backingScaleFactor];
|
|
|
|
setGeom(pixelsPerPt, w, h);
|
|
}
|
|
|
|
- (void)drawRect:(NSRect)theRect {
|
|
// Called during resize. This gets rid of flicker when resizing.
|
|
drawgl();
|
|
}
|
|
|
|
- (void)mouseDown:(NSEvent *)theEvent {
|
|
double scale = [[NSScreen mainScreen] backingScaleFactor];
|
|
NSPoint p = [theEvent locationInWindow];
|
|
eventMouseDown(p.x * scale, p.y * scale);
|
|
}
|
|
|
|
- (void)mouseUp:(NSEvent *)theEvent {
|
|
double scale = [[NSScreen mainScreen] backingScaleFactor];
|
|
NSPoint p = [theEvent locationInWindow];
|
|
eventMouseEnd(p.x * scale, p.y * scale);
|
|
}
|
|
|
|
- (void)mouseDragged:(NSEvent *)theEvent {
|
|
double scale = [[NSScreen mainScreen] backingScaleFactor];
|
|
NSPoint p = [theEvent locationInWindow];
|
|
eventMouseDragged(p.x * scale, p.y * scale);
|
|
}
|
|
|
|
- (void)windowDidBecomeKey:(NSNotification *)notification {
|
|
lifecycleFocused();
|
|
}
|
|
|
|
- (void)windowDidResignKey:(NSNotification *)notification {
|
|
if (![NSApp isHidden]) {
|
|
lifecycleVisible();
|
|
}
|
|
}
|
|
|
|
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
|
|
lifecycleAlive();
|
|
[[NSRunningApplication currentApplication] activateWithOptions:(NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps)];
|
|
[self.window makeKeyAndOrderFront:self];
|
|
lifecycleVisible();
|
|
}
|
|
|
|
- (void)applicationWillTerminate:(NSNotification *)aNotification {
|
|
lifecycleDead();
|
|
}
|
|
|
|
- (void)applicationDidHide:(NSNotification *)aNotification {
|
|
lifecycleAlive();
|
|
}
|
|
|
|
- (void)applicationWillUnhide:(NSNotification *)notification {
|
|
lifecycleVisible();
|
|
}
|
|
|
|
- (void)windowWillClose:(NSNotification *)notification {
|
|
lifecycleAlive();
|
|
}
|
|
@end
|
|
|
|
@interface MobileResponder : NSResponder
|
|
{
|
|
}
|
|
@end
|
|
|
|
@implementation MobileResponder
|
|
- (void)keyDown:(NSEvent *)theEvent {
|
|
[self key:theEvent];
|
|
}
|
|
- (void)keyUp:(NSEvent *)theEvent {
|
|
[self key:theEvent];
|
|
}
|
|
- (void)key:(NSEvent *)theEvent {
|
|
NSRange range = [theEvent.characters rangeOfComposedCharacterSequenceAtIndex:0];
|
|
|
|
uint8_t buf[4] = {0, 0, 0, 0};
|
|
if (![theEvent.characters getBytes:buf
|
|
maxLength:4
|
|
usedLength:nil
|
|
encoding:NSUTF32LittleEndianStringEncoding
|
|
options:NSStringEncodingConversionAllowLossy
|
|
range:range
|
|
remainingRange:nil]) {
|
|
NSLog(@"failed to read key event %@", theEvent);
|
|
return;
|
|
}
|
|
|
|
uint32_t rune = (uint32_t)buf[0]<<0 | (uint32_t)buf[1]<<8 | (uint32_t)buf[2]<<16 | (uint32_t)buf[3]<<24;
|
|
|
|
uint8_t direction;
|
|
if ([theEvent isARepeat]) {
|
|
direction = 0;
|
|
} else if (theEvent.type == NSKeyDown) {
|
|
direction = 1;
|
|
} else {
|
|
direction = 2;
|
|
}
|
|
eventKey((int32_t)rune, direction, theEvent.keyCode, theEvent.modifierFlags);
|
|
}
|
|
|
|
- (void)flagsChanged:(NSEvent *)theEvent {
|
|
eventFlags(theEvent.modifierFlags);
|
|
}
|
|
@end
|
|
|
|
void
|
|
runApp(void) {
|
|
[NSAutoreleasePool new];
|
|
[NSApplication sharedApplication];
|
|
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
|
|
|
id menuBar = [[NSMenu new] autorelease];
|
|
id menuItem = [[NSMenuItem new] autorelease];
|
|
[menuBar addItem:menuItem];
|
|
[NSApp setMainMenu:menuBar];
|
|
|
|
id menu = [[NSMenu new] autorelease];
|
|
id name = [[NSProcessInfo processInfo] processName];
|
|
|
|
id hideMenuItem = [[[NSMenuItem alloc] initWithTitle:@"Hide"
|
|
action:@selector(hide:) keyEquivalent:@"h"]
|
|
autorelease];
|
|
[menu addItem:hideMenuItem];
|
|
|
|
id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:@"Quit"
|
|
action:@selector(terminate:) keyEquivalent:@"q"]
|
|
autorelease];
|
|
[menu addItem:quitMenuItem];
|
|
[menuItem setSubmenu:menu];
|
|
|
|
NSRect rect = NSMakeRect(0, 0, 400, 400);
|
|
|
|
NSWindow* window = [[[NSWindow alloc] initWithContentRect:rect
|
|
styleMask:NSTitledWindowMask
|
|
backing:NSBackingStoreBuffered
|
|
defer:NO]
|
|
autorelease];
|
|
window.styleMask |= NSResizableWindowMask;
|
|
window.styleMask |= NSMiniaturizableWindowMask ;
|
|
window.styleMask |= NSClosableWindowMask;
|
|
window.title = name;
|
|
[window cascadeTopLeftFromPoint:NSMakePoint(20,20)];
|
|
|
|
NSOpenGLPixelFormatAttribute attr[] = {
|
|
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
|
|
NSOpenGLPFAColorSize, 24,
|
|
NSOpenGLPFAAlphaSize, 8,
|
|
NSOpenGLPFADepthSize, 16,
|
|
NSOpenGLPFAAccelerated,
|
|
NSOpenGLPFADoubleBuffer,
|
|
NSOpenGLPFAAllowOfflineRenderers,
|
|
0
|
|
};
|
|
id pixFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
|
|
MobileGLView* view = [[MobileGLView alloc] initWithFrame:rect pixelFormat:pixFormat];
|
|
[window setContentView:view];
|
|
[window setDelegate:view];
|
|
[NSApp setDelegate:view];
|
|
|
|
window.nextResponder = [[[MobileResponder alloc] init] autorelease];
|
|
|
|
[NSApp run];
|
|
}
|
|
|
|
void stopApp(void) {
|
|
[NSApp terminate:nil];
|
|
}
|