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