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 }