github.com/shranet/mobile@v0.0.0-20200814083559-5702cdcd481b/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  
    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  }