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  }