zorldo

Goofing around with Ebiten
git clone git://bsandro.tech/zorldo
Log | Files | Refs | README

cocoa.m (10306B)


      1 // Copyright 2014 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // +build darwin
      6 // +build 386 amd64
      7 // +build !ios
      8 
      9 #include "_cgo_export.h"
     10 #include <pthread.h>
     11 #include <stdio.h>
     12 
     13 #import <Cocoa/Cocoa.h>
     14 #import <Foundation/Foundation.h>
     15 #import <OpenGL/gl3.h>
     16 
     17 // The variables did not exist on older OS X releases,
     18 // we use the old variables deprecated on macOS to define them.
     19 #if __MAC_OS_X_VERSION_MAX_ALLOWED < 101200
     20 enum
     21 {
     22     NSEventTypeScrollWheel = NSScrollWheel,
     23     NSEventTypeKeyDown = NSKeyDown
     24 };
     25 enum
     26 {
     27     NSWindowStyleMaskTitled = NSTitledWindowMask,
     28     NSWindowStyleMaskResizable = NSResizableWindowMask,
     29     NSWindowStyleMaskMiniaturizable = NSMiniaturizableWindowMask,
     30     NSWindowStyleMaskClosable = NSClosableWindowMask
     31 };
     32 #endif
     33 
     34 void makeCurrentContext(uintptr_t context) {
     35 	NSOpenGLContext* ctx = (NSOpenGLContext*)context;
     36 	[ctx makeCurrentContext];
     37 	[ctx update];
     38 }
     39 
     40 void flushContext(uintptr_t context) {
     41 	NSOpenGLContext* ctx = (NSOpenGLContext*)context;
     42 	[ctx flushBuffer];
     43 }
     44 
     45 uint64 threadID() {
     46 	uint64 id;
     47 	if (pthread_threadid_np(pthread_self(), &id)) {
     48 		abort();
     49 	}
     50 	return id;
     51 }
     52 
     53 @interface ScreenGLView : NSOpenGLView<NSWindowDelegate>
     54 {
     55 }
     56 @end
     57 
     58 @implementation ScreenGLView
     59 - (void)prepareOpenGL {
     60 	[super prepareOpenGL];
     61 
     62 	[self setWantsBestResolutionOpenGLSurface:YES];
     63 	GLint swapInt = 1;
     64 	NSOpenGLContext *ctx = [self openGLContext];
     65 
     66 #pragma clang diagnostic push
     67 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
     68 	[ctx setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
     69 #pragma clang diagnostic pop
     70 
     71 	// Using attribute arrays in OpenGL 3.3 requires the use of a VBA.
     72 	// But VBAs don't exist in ES 2. So we bind a default one.
     73 	GLuint vba;
     74 	glGenVertexArrays(1, &vba);
     75 	glBindVertexArray(vba);
     76 
     77 	preparedOpenGL((GoUintptr)self, (GoUintptr)ctx, (GoUintptr)vba);
     78 }
     79 
     80 - (void)callSetGeom {
     81 	// Calculate screen PPI.
     82 	//
     83 	// Note that the backingScaleFactor converts from logical
     84 	// pixels to actual pixels, but both of these units vary
     85 	// independently from real world size. E.g.
     86 	//
     87 	// 13" Retina Macbook Pro, 2560x1600, 227ppi, backingScaleFactor=2, scale=3.15
     88 	// 15" Retina Macbook Pro, 2880x1800, 220ppi, backingScaleFactor=2, scale=3.06
     89 	// 27" iMac,               2560x1440, 109ppi, backingScaleFactor=1, scale=1.51
     90 	// 27" Retina iMac,        5120x2880, 218ppi, backingScaleFactor=2, scale=3.03
     91 	NSScreen *screen = self.window.screen;
     92 	double screenPixW = [screen frame].size.width * [screen backingScaleFactor];
     93 
     94 	CGDirectDisplayID display = (CGDirectDisplayID)[[[screen deviceDescription] valueForKey:@"NSScreenNumber"] intValue];
     95 	CGSize screenSizeMM = CGDisplayScreenSize(display); // in millimeters
     96 	float ppi = 25.4 * screenPixW / screenSizeMM.width;
     97 	float pixelsPerPt = ppi/72.0;
     98 
     99 	// The width and height reported to the geom package are the
    100 	// bounds of the OpenGL view. Several steps are necessary.
    101 	// First, [self bounds] gives us the number of logical pixels
    102 	// in the view. Multiplying this by the backingScaleFactor
    103 	// gives us the number of actual pixels.
    104 	NSRect r = [self bounds];
    105 	int w = r.size.width * [screen backingScaleFactor];
    106 	int h = r.size.height * [screen backingScaleFactor];
    107 
    108 	setGeom((GoUintptr)self, pixelsPerPt, w, h);
    109 }
    110 
    111 - (void)reshape {
    112 	[super reshape];
    113 	[self callSetGeom];
    114 }
    115 
    116 - (void)drawRect:(NSRect)theRect {
    117 	// Called during resize. Do an extra draw if we are visible.
    118 	// This gets rid of flicker when resizing.
    119 	drawgl((GoUintptr)self);
    120 }
    121 
    122 - (void)mouseEventNS:(NSEvent *)theEvent {
    123 	NSPoint p = [theEvent locationInWindow];
    124 	double h = self.frame.size.height;
    125 
    126 	// Both h and p are measured in Cocoa pixels, which are a fraction of
    127 	// physical pixels, so we multiply by backingScaleFactor.
    128 	double scale = [self.window.screen backingScaleFactor];
    129 
    130 	double x = p.x * scale;
    131 	double y = (h - p.y) * scale - 1; // flip origin from bottom-left to top-left.
    132 
    133 	double dx, dy;
    134 	if (theEvent.type == NSEventTypeScrollWheel) {
    135 		dx = theEvent.scrollingDeltaX;
    136 		dy = theEvent.scrollingDeltaY;
    137 	}
    138 
    139 	mouseEvent((GoUintptr)self, x, y, dx, dy, theEvent.type, theEvent.buttonNumber, theEvent.modifierFlags);
    140 }
    141 
    142 - (void)mouseMoved:(NSEvent *)theEvent        { [self mouseEventNS:theEvent]; }
    143 - (void)mouseDown:(NSEvent *)theEvent         { [self mouseEventNS:theEvent]; }
    144 - (void)mouseUp:(NSEvent *)theEvent           { [self mouseEventNS:theEvent]; }
    145 - (void)mouseDragged:(NSEvent *)theEvent      { [self mouseEventNS:theEvent]; }
    146 - (void)rightMouseDown:(NSEvent *)theEvent    { [self mouseEventNS:theEvent]; }
    147 - (void)rightMouseUp:(NSEvent *)theEvent      { [self mouseEventNS:theEvent]; }
    148 - (void)rightMouseDragged:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
    149 - (void)otherMouseDown:(NSEvent *)theEvent    { [self mouseEventNS:theEvent]; }
    150 - (void)otherMouseUp:(NSEvent *)theEvent      { [self mouseEventNS:theEvent]; }
    151 - (void)otherMouseDragged:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
    152 - (void)scrollWheel:(NSEvent *)theEvent       { [self mouseEventNS:theEvent]; }
    153 
    154 // raw modifier key presses
    155 - (void)flagsChanged:(NSEvent *)theEvent {
    156 	flagEvent((GoUintptr)self, theEvent.modifierFlags);
    157 }
    158 
    159 // overrides special handling of escape and tab
    160 - (BOOL)performKeyEquivalent:(NSEvent *)theEvent {
    161 	[self key:theEvent];
    162 	return YES;
    163 }
    164 
    165 - (void)keyDown:(NSEvent *)theEvent { [self key:theEvent]; }
    166 - (void)keyUp:(NSEvent *)theEvent   { [self key:theEvent]; }
    167 
    168 - (void)key:(NSEvent *)theEvent {
    169 	NSRange range = [theEvent.characters rangeOfComposedCharacterSequenceAtIndex:0];
    170 
    171 	uint8_t buf[4] = {0, 0, 0, 0};
    172 	if (![theEvent.characters getBytes:buf
    173 			maxLength:4
    174 			usedLength:nil
    175 			encoding:NSUTF32LittleEndianStringEncoding
    176 			options:NSStringEncodingConversionAllowLossy
    177 			range:range
    178 			remainingRange:nil]) {
    179 		NSLog(@"failed to read key event %@", theEvent);
    180 		return;
    181 	}
    182 
    183 	uint32_t rune = (uint32_t)buf[0]<<0 | (uint32_t)buf[1]<<8 | (uint32_t)buf[2]<<16 | (uint32_t)buf[3]<<24;
    184 
    185 	uint8_t direction;
    186 	if ([theEvent isARepeat]) {
    187 		direction = 0;
    188 	} else if (theEvent.type == NSEventTypeKeyDown) {
    189 		direction = 1;
    190 	} else {
    191 		direction = 2;
    192 	}
    193 	keyEvent((GoUintptr)self, (int32_t)rune, direction, theEvent.keyCode, theEvent.modifierFlags);
    194 }
    195 
    196 - (void)windowDidChangeScreenProfile:(NSNotification *)notification {
    197 	[self callSetGeom];
    198 }
    199 
    200 // TODO: catch windowDidMiniaturize?
    201 
    202 - (void)windowDidExpose:(NSNotification *)notification {
    203 	lifecycleVisible((GoUintptr)self, true);
    204 }
    205 
    206 - (void)windowDidBecomeKey:(NSNotification *)notification {
    207 	lifecycleFocused((GoUintptr)self, true);
    208 }
    209 
    210 - (void)windowDidResignKey:(NSNotification *)notification {
    211 	lifecycleFocused((GoUintptr)self, false);
    212 	if ([NSApp isHidden]) {
    213 		lifecycleVisible((GoUintptr)self, false);
    214 	}
    215 }
    216 
    217 - (void)windowWillClose:(NSNotification *)notification {
    218 	// TODO: is this right? Closing a window via the top-left red button
    219 	// seems to return early without ever calling windowClosing.
    220 	if (self.window.nextResponder == NULL) {
    221 		return; // already called close
    222 	}
    223 
    224 	windowClosing((GoUintptr)self);
    225 	[self.window.nextResponder release];
    226 	self.window.nextResponder = NULL;
    227 }
    228 @end
    229 
    230 @interface AppDelegate : NSObject<NSApplicationDelegate>
    231 {
    232 }
    233 @end
    234 
    235 @implementation AppDelegate
    236 - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    237 	driverStarted();
    238 	[[NSRunningApplication currentApplication] activateWithOptions:(NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps)];
    239 }
    240 
    241 - (void)applicationWillTerminate:(NSNotification *)aNotification {
    242 	lifecycleDeadAll();
    243 }
    244 
    245 - (void)applicationWillHide:(NSNotification *)aNotification {
    246 	lifecycleHideAll();
    247 }
    248 @end
    249 
    250 uintptr_t doNewWindow(int width, int height, char* title) {
    251 	NSScreen *screen = [NSScreen mainScreen];
    252 	double w = (double)width / [screen backingScaleFactor];
    253 	double h = (double)height / [screen backingScaleFactor];
    254 	__block ScreenGLView* view = NULL;
    255 
    256 	dispatch_sync(dispatch_get_main_queue(), ^{
    257 		id menuBar = [NSMenu new];
    258 		id menuItem = [NSMenuItem new];
    259 		[menuBar addItem:menuItem];
    260 		[NSApp setMainMenu:menuBar];
    261 
    262 		id menu = [NSMenu new];
    263 		NSString* name = [[NSString alloc] initWithUTF8String:title];
    264 
    265 		id hideMenuItem = [[NSMenuItem alloc] initWithTitle:@"Hide"
    266 			action:@selector(hide:) keyEquivalent:@"h"];
    267 		[menu addItem:hideMenuItem];
    268 
    269 		id quitMenuItem = [[NSMenuItem alloc] initWithTitle:@"Quit"
    270 			action:@selector(terminate:) keyEquivalent:@"q"];
    271 		[menu addItem:quitMenuItem];
    272 		[menuItem setSubmenu:menu];
    273 
    274 		NSRect rect = NSMakeRect(0, 0, w, h);
    275 
    276 		NSWindow* window = [[NSWindow alloc] initWithContentRect:rect
    277 				styleMask:NSWindowStyleMaskTitled
    278 				backing:NSBackingStoreBuffered
    279 				defer:NO];
    280 		window.styleMask |= NSWindowStyleMaskResizable;
    281 		window.styleMask |= NSWindowStyleMaskMiniaturizable;
    282 		window.styleMask |= NSWindowStyleMaskClosable;
    283 		window.title = name;
    284 		window.displaysWhenScreenProfileChanges = YES;
    285 		[window cascadeTopLeftFromPoint:NSMakePoint(20,20)];
    286 		[window setAcceptsMouseMovedEvents:YES];
    287 
    288 		NSOpenGLPixelFormatAttribute attr[] = {
    289 			NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
    290 			NSOpenGLPFAColorSize,     24,
    291 			NSOpenGLPFAAlphaSize,     8,
    292 			NSOpenGLPFADepthSize,     16,
    293 			NSOpenGLPFADoubleBuffer,
    294 			NSOpenGLPFAAllowOfflineRenderers,
    295 			0
    296 		};
    297 		id pixFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
    298 		view = [[ScreenGLView alloc] initWithFrame:rect pixelFormat:pixFormat];
    299 		[window setContentView:view];
    300 		[window setDelegate:view];
    301 		[window makeFirstResponder:view];
    302 	});
    303 
    304 	return (uintptr_t)view;
    305 }
    306 
    307 void doShowWindow(uintptr_t viewID) {
    308 	ScreenGLView* view = (ScreenGLView*)viewID;
    309 	dispatch_async(dispatch_get_main_queue(), ^{
    310 		[view.window makeKeyAndOrderFront:view.window];
    311 	});
    312 }
    313 
    314 void doCloseWindow(uintptr_t viewID) {
    315 	ScreenGLView* view = (ScreenGLView*)viewID;
    316 	dispatch_sync(dispatch_get_main_queue(), ^{
    317 		[view.window performClose:view];
    318 	});
    319 }
    320 
    321 void startDriver() {
    322 	[NSAutoreleasePool new];
    323 	[NSApplication sharedApplication];
    324 	[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
    325 	AppDelegate* delegate = [[AppDelegate alloc] init];
    326 	[NSApp setDelegate:delegate];
    327 	[NSApp run];
    328 }
    329 
    330 void stopDriver() {
    331 	dispatch_async(dispatch_get_main_queue(), ^{
    332 		[NSApp terminate:nil];
    333 	});
    334 }