zorldo

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

darwin_desktop.m (7070B)


      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 !ios
      7 
      8 #include "_cgo_export.h"
      9 #include <pthread.h>
     10 #include <stdio.h>
     11 
     12 #import <Cocoa/Cocoa.h>
     13 #import <Foundation/Foundation.h>
     14 #import <OpenGL/gl3.h>
     15 
     16 void makeCurrentContext(GLintptr context) {
     17 	NSOpenGLContext* ctx = (NSOpenGLContext*)context;
     18 	[ctx makeCurrentContext];
     19 }
     20 
     21 uint64 threadID() {
     22 	uint64 id;
     23 	if (pthread_threadid_np(pthread_self(), &id)) {
     24 		abort();
     25 	}
     26 	return id;
     27 }
     28 
     29 @interface MobileGLView : NSOpenGLView<NSApplicationDelegate, NSWindowDelegate>
     30 {
     31 }
     32 @end
     33 
     34 @implementation MobileGLView
     35 - (void)prepareOpenGL {
     36 	[self setWantsBestResolutionOpenGLSurface:YES];
     37 	GLint swapInt = 1;
     38 
     39 #pragma clang diagnostic push
     40 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
     41 	[[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
     42 #pragma clang diagnostic pop
     43 
     44 	// Using attribute arrays in OpenGL 3.3 requires the use of a VBA.
     45 	// But VBAs don't exist in ES 2. So we bind a default one.
     46 	GLuint vba;
     47 	glGenVertexArrays(1, &vba);
     48 	glBindVertexArray(vba);
     49 
     50 	startloop((GLintptr)[self openGLContext]);
     51 }
     52 
     53 - (void)reshape {
     54 	[super reshape];
     55 
     56 	// Calculate screen PPI.
     57 	//
     58 	// Note that the backingScaleFactor converts from logical
     59 	// pixels to actual pixels, but both of these units vary
     60 	// independently from real world size. E.g.
     61 	//
     62 	// 13" Retina Macbook Pro, 2560x1600, 227ppi, backingScaleFactor=2, scale=3.15
     63 	// 15" Retina Macbook Pro, 2880x1800, 220ppi, backingScaleFactor=2, scale=3.06
     64 	// 27" iMac,               2560x1440, 109ppi, backingScaleFactor=1, scale=1.51
     65 	// 27" Retina iMac,        5120x2880, 218ppi, backingScaleFactor=2, scale=3.03
     66 	NSScreen *screen = [NSScreen mainScreen];
     67 	double screenPixW = [screen frame].size.width * [screen backingScaleFactor];
     68 
     69 	CGDirectDisplayID display = (CGDirectDisplayID)[[[screen deviceDescription] valueForKey:@"NSScreenNumber"] intValue];
     70 	CGSize screenSizeMM = CGDisplayScreenSize(display); // in millimeters
     71 	float ppi = 25.4 * screenPixW / screenSizeMM.width;
     72 	float pixelsPerPt = ppi/72.0;
     73 
     74 	// The width and height reported to the geom package are the
     75 	// bounds of the OpenGL view. Several steps are necessary.
     76 	// First, [self bounds] gives us the number of logical pixels
     77 	// in the view. Multiplying this by the backingScaleFactor
     78 	// gives us the number of actual pixels.
     79 	NSRect r = [self bounds];
     80 	int w = r.size.width * [screen backingScaleFactor];
     81 	int h = r.size.height * [screen backingScaleFactor];
     82 
     83 	setGeom(pixelsPerPt, w, h);
     84 }
     85 
     86 - (void)drawRect:(NSRect)theRect {
     87 	// Called during resize. This gets rid of flicker when resizing.
     88 	drawgl();
     89 }
     90 
     91 - (void)mouseDown:(NSEvent *)theEvent {
     92 	double scale = [[NSScreen mainScreen] backingScaleFactor];
     93 	NSPoint p = [theEvent locationInWindow];
     94 	eventMouseDown(p.x * scale, p.y * scale);
     95 }
     96 
     97 - (void)mouseUp:(NSEvent *)theEvent {
     98 	double scale = [[NSScreen mainScreen] backingScaleFactor];
     99 	NSPoint p = [theEvent locationInWindow];
    100 	eventMouseEnd(p.x * scale, p.y * scale);
    101 }
    102 
    103 - (void)mouseDragged:(NSEvent *)theEvent {
    104 	double scale = [[NSScreen mainScreen] backingScaleFactor];
    105 	NSPoint p = [theEvent locationInWindow];
    106 	eventMouseDragged(p.x * scale, p.y * scale);
    107 }
    108 
    109 - (void)windowDidBecomeKey:(NSNotification *)notification {
    110 	lifecycleFocused();
    111 }
    112 
    113 - (void)windowDidResignKey:(NSNotification *)notification {
    114 	if (![NSApp isHidden]) {
    115 		lifecycleVisible();
    116 	}
    117 }
    118 
    119 - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    120 	lifecycleAlive();
    121 	[[NSRunningApplication currentApplication] activateWithOptions:(NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps)];
    122 	[self.window makeKeyAndOrderFront:self];
    123 	lifecycleVisible();
    124 }
    125 
    126 - (void)applicationWillTerminate:(NSNotification *)aNotification {
    127 	lifecycleDead();
    128 }
    129 
    130 - (void)applicationDidHide:(NSNotification *)aNotification {
    131 	lifecycleAlive();
    132 }
    133 
    134 - (void)applicationWillUnhide:(NSNotification *)notification {
    135 	lifecycleVisible();
    136 }
    137 
    138 - (void)windowWillClose:(NSNotification *)notification {
    139 	lifecycleAlive();
    140 }
    141 @end
    142 
    143 @interface MobileResponder : NSResponder
    144 {
    145 }
    146 @end
    147 
    148 @implementation MobileResponder
    149 - (void)keyDown:(NSEvent *)theEvent {
    150 	[self key:theEvent];
    151 }
    152 - (void)keyUp:(NSEvent *)theEvent {
    153 	[self key:theEvent];
    154 }
    155 - (void)key:(NSEvent *)theEvent {
    156 	NSRange range = [theEvent.characters rangeOfComposedCharacterSequenceAtIndex:0];
    157 
    158 	uint8_t buf[4] = {0, 0, 0, 0};
    159 	if (![theEvent.characters getBytes:buf
    160 			maxLength:4
    161 			usedLength:nil
    162 			encoding:NSUTF32LittleEndianStringEncoding
    163 			options:NSStringEncodingConversionAllowLossy
    164 			range:range
    165 			remainingRange:nil]) {
    166 		NSLog(@"failed to read key event %@", theEvent);
    167 		return;
    168 	}
    169 
    170 	uint32_t rune = (uint32_t)buf[0]<<0 | (uint32_t)buf[1]<<8 | (uint32_t)buf[2]<<16 | (uint32_t)buf[3]<<24;
    171 
    172 	uint8_t direction;
    173 	if ([theEvent isARepeat]) {
    174 		direction = 0;
    175 	} else if (theEvent.type == NSEventTypeKeyDown) {
    176 		direction = 1;
    177 	} else {
    178 		direction = 2;
    179 	}
    180 	eventKey((int32_t)rune, direction, theEvent.keyCode, theEvent.modifierFlags);
    181 }
    182 
    183 - (void)flagsChanged:(NSEvent *)theEvent {
    184 	eventFlags(theEvent.modifierFlags);
    185 }
    186 @end
    187 
    188 void
    189 runApp(void) {
    190 	[NSAutoreleasePool new];
    191 	[NSApplication sharedApplication];
    192 	[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
    193 
    194 	id menuBar = [[NSMenu new] autorelease];
    195 	id menuItem = [[NSMenuItem new] autorelease];
    196 	[menuBar addItem:menuItem];
    197 	[NSApp setMainMenu:menuBar];
    198 
    199 	id menu = [[NSMenu new] autorelease];
    200 	id name = [[NSProcessInfo processInfo] processName];
    201 
    202 	id hideMenuItem = [[[NSMenuItem alloc] initWithTitle:@"Hide"
    203 		action:@selector(hide:) keyEquivalent:@"h"]
    204 		autorelease];
    205 	[menu addItem:hideMenuItem];
    206 
    207 	id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:@"Quit"
    208 		action:@selector(terminate:) keyEquivalent:@"q"]
    209 		autorelease];
    210 	[menu addItem:quitMenuItem];
    211 	[menuItem setSubmenu:menu];
    212 
    213 	NSRect rect = NSMakeRect(0, 0, 600, 800);
    214 
    215 	NSWindow* window = [[[NSWindow alloc] initWithContentRect:rect
    216 			styleMask:NSWindowStyleMaskTitled
    217 			backing:NSBackingStoreBuffered
    218 			defer:NO]
    219 		autorelease];
    220 	window.styleMask |= NSWindowStyleMaskResizable;
    221 	window.styleMask |= NSWindowStyleMaskMiniaturizable;
    222 	window.styleMask |= NSWindowStyleMaskClosable;
    223 	window.title = name;
    224 	[window cascadeTopLeftFromPoint:NSMakePoint(20,20)];
    225 
    226 	NSOpenGLPixelFormatAttribute attr[] = {
    227 		NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
    228 		NSOpenGLPFAColorSize,     24,
    229 		NSOpenGLPFAAlphaSize,     8,
    230 		NSOpenGLPFADepthSize,     16,
    231 		NSOpenGLPFAAccelerated,
    232 		NSOpenGLPFADoubleBuffer,
    233 		NSOpenGLPFAAllowOfflineRenderers,
    234 		0
    235 	};
    236 	id pixFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
    237 	MobileGLView* view = [[MobileGLView alloc] initWithFrame:rect pixelFormat:pixFormat];
    238 	[window setContentView:view];
    239 	[window setDelegate:view];
    240 	[NSApp setDelegate:view];
    241 
    242 	window.nextResponder = [[[MobileResponder alloc] init] autorelease];
    243 
    244 	[NSApp run];
    245 }
    246 
    247 void stopApp(void) {
    248 	[NSApp terminate:nil];
    249 }