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