github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/exp/shiny/driver/gldriver/cocoa.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 386 amd64
     7  // +build !ios
     8  
     9  #include "_cgo_export.h"
    10  #include <pthread.h>
    11  #include <stdio.h>
    12  
    13  #import <Cocoa/Cocoa.h>
    14  #import <Foundation/Foundation.h>
    15  #import <OpenGL/gl3.h>
    16  
    17  void makeCurrentContext(uintptr_t context) {
    18  	NSOpenGLContext* ctx = (NSOpenGLContext*)context;
    19  	[ctx makeCurrentContext];
    20  }
    21  
    22  void flushContext(uintptr_t context) {
    23  	NSOpenGLContext* ctx = (NSOpenGLContext*)context;
    24  	[ctx flushBuffer];
    25  }
    26  
    27  uint64 threadID() {
    28  	uint64 id;
    29  	if (pthread_threadid_np(pthread_self(), &id)) {
    30  		abort();
    31  	}
    32  	return id;
    33  }
    34  
    35  @interface ScreenGLView : NSOpenGLView<NSWindowDelegate>
    36  {
    37  }
    38  @end
    39  
    40  @implementation ScreenGLView
    41  - (void)prepareOpenGL {
    42  	[self setWantsBestResolutionOpenGLSurface:YES];
    43  	GLint swapInt = 1;
    44  	NSOpenGLContext *ctx = [self openGLContext];
    45  	[ctx setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
    46  
    47  	// Using attribute arrays in OpenGL 3.3 requires the use of a VBA.
    48  	// But VBAs don't exist in ES 2. So we bind a default one.
    49  	GLuint vba;
    50  	glGenVertexArrays(1, &vba);
    51  	glBindVertexArray(vba);
    52  
    53  	preparedOpenGL((GoUintptr)self, (GoUintptr)ctx, (GoUintptr)vba);
    54  }
    55  
    56  - (void)callSetGeom {
    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 = self.window.screen;
    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((GoUintptr)self, pixelsPerPt, w, h);
    85  }
    86  
    87  - (void)reshape {
    88  	[super reshape];
    89  	[self callSetGeom];
    90  }
    91  
    92  - (void)drawRect:(NSRect)theRect {
    93  	// Called during resize. Do an extra draw if we are visible.
    94  	// This gets rid of flicker when resizing.
    95  	drawgl((GoUintptr)self);
    96  }
    97  
    98  - (void)mouseEventNS:(NSEvent *)theEvent {
    99  	NSPoint p = [theEvent locationInWindow];
   100  	double h = self.frame.size.height;
   101  
   102  	// Both h and p are measured in Cocoa pixels, which are a fraction of
   103  	// physical pixels, so we multiply by backingScaleFactor.
   104  	double scale = [self.window.screen backingScaleFactor];
   105  
   106  	double x = p.x * scale;
   107  	double y = (h - p.y) * scale - 1; // flip origin from bottom-left to top-left.
   108  
   109  	mouseEvent((GoUintptr)self, x, y, theEvent.type, theEvent.buttonNumber, theEvent.modifierFlags);
   110  }
   111  
   112  - (void)mouseDown:(NSEvent *)theEvent         { [self mouseEventNS:theEvent]; }
   113  - (void)mouseUp:(NSEvent *)theEvent           { [self mouseEventNS:theEvent]; }
   114  - (void)mouseDragged:(NSEvent *)theEvent      { [self mouseEventNS:theEvent]; }
   115  - (void)rightMouseDown:(NSEvent *)theEvent    { [self mouseEventNS:theEvent]; }
   116  - (void)rightMouseUp:(NSEvent *)theEvent      { [self mouseEventNS:theEvent]; }
   117  - (void)rightMouseDragged:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
   118  - (void)otherMouseDown:(NSEvent *)theEvent    { [self mouseEventNS:theEvent]; }
   119  - (void)otherMouseUp:(NSEvent *)theEvent      { [self mouseEventNS:theEvent]; }
   120  - (void)otherMouseDragged:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
   121  
   122  - (void)windowDidChangeScreenProfile:(NSNotification *)notification {
   123  	[self callSetGeom];
   124  }
   125  
   126  - (void)windowDidExpose:(NSNotification *)notification {
   127  	lifecycleVisible((GoUintptr)self);
   128  }
   129  
   130  - (void)windowDidBecomeKey:(NSNotification *)notification {
   131  	lifecycleFocused((GoUintptr)self);
   132  }
   133  
   134  - (void)windowDidResignKey:(NSNotification *)notification {
   135  	if (![NSApp isHidden]) {
   136  		lifecycleVisible((GoUintptr)self);
   137  	}
   138  }
   139  
   140  - (void)windowWillClose:(NSNotification *)notification {
   141  	if (self.window.nextResponder == NULL) {
   142  		return; // already called close
   143  	}
   144  	windowClosing((GoUintptr)self);
   145  	[self.window.nextResponder release];
   146  	self.window.nextResponder = NULL;
   147  }
   148  @end
   149  
   150  @interface WindowResponder : NSResponder
   151  {
   152  }
   153  @end
   154  
   155  @implementation WindowResponder
   156  {
   157  	uintptr_t windowID;
   158  }
   159  
   160  - (id)initWithWindowID:(uintptr_t)id {
   161      windowID = id;
   162      return self;
   163  }
   164  
   165  - (void)keyDown:(NSEvent *)theEvent {
   166  	[self key:theEvent];
   167  }
   168  - (void)keyUp:(NSEvent *)theEvent {
   169  	[self key:theEvent];
   170  }
   171  - (void)key:(NSEvent *)theEvent {
   172  	NSRange range = [theEvent.characters rangeOfComposedCharacterSequenceAtIndex:0];
   173  
   174  	uint8_t buf[4] = {0, 0, 0, 0};
   175  	if (![theEvent.characters getBytes:buf
   176  			maxLength:4
   177  			usedLength:nil
   178  			encoding:NSUTF32LittleEndianStringEncoding
   179  			options:NSStringEncodingConversionAllowLossy
   180  			range:range
   181  			remainingRange:nil]) {
   182  		NSLog(@"failed to read key event %@", theEvent);
   183  		return;
   184  	}
   185  
   186  	uint32_t rune = (uint32_t)buf[0]<<0 | (uint32_t)buf[1]<<8 | (uint32_t)buf[2]<<16 | (uint32_t)buf[3]<<24;
   187  
   188  	uint8_t direction;
   189  	if ([theEvent isARepeat]) {
   190  		direction = 0;
   191  	} else if (theEvent.type == NSKeyDown) {
   192  		direction = 1;
   193  	} else {
   194  		direction = 2;
   195  	}
   196  	keyEvent((GoUintptr)windowID, (int32_t)rune, direction, theEvent.keyCode, theEvent.modifierFlags);
   197  }
   198  
   199  - (void)flagsChanged:(NSEvent *)theEvent {
   200  	flagEvent((GoUintptr)windowID, theEvent.modifierFlags);
   201  }
   202  @end
   203  
   204  @interface AppDelegate : NSObject<NSApplicationDelegate>
   205  {
   206  }
   207  @end
   208  
   209  @implementation AppDelegate
   210  - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
   211  	driverStarted();
   212  	[[NSRunningApplication currentApplication] activateWithOptions:(NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps)];
   213  }
   214  
   215  - (void)applicationWillTerminate:(NSNotification *)aNotification {
   216  	lifecycleDeadAll();
   217  }
   218  
   219  - (void)applicationWillHide:(NSNotification *)aNotification {
   220  	lifecycleAliveAll();
   221  }
   222  @end
   223  
   224  uintptr_t doNewWindow(int width, int height) {
   225  	NSScreen *screen = [NSScreen mainScreen];
   226  	double w = (double)width / [screen backingScaleFactor];
   227  	double h = (double)height / [screen backingScaleFactor];
   228  	__block ScreenGLView* view = NULL;
   229  
   230  	dispatch_sync(dispatch_get_main_queue(), ^{
   231  		id menuBar = [NSMenu new];
   232  		id menuItem = [NSMenuItem new];
   233  		[menuBar addItem:menuItem];
   234  		[NSApp setMainMenu:menuBar];
   235  
   236  		id menu = [NSMenu new];
   237  		NSString* name = [[NSProcessInfo processInfo] processName];
   238  
   239  		id hideMenuItem = [[NSMenuItem alloc] initWithTitle:@"Hide"
   240  			action:@selector(hide:) keyEquivalent:@"h"];
   241  		[menu addItem:hideMenuItem];
   242  
   243  		id quitMenuItem = [[NSMenuItem alloc] initWithTitle:@"Quit"
   244  			action:@selector(terminate:) keyEquivalent:@"q"];
   245  		[menu addItem:quitMenuItem];
   246  		[menuItem setSubmenu:menu];
   247  
   248  		NSRect rect = NSMakeRect(0, 0, w, h);
   249  
   250  		NSWindow* window = [[NSWindow alloc] initWithContentRect:rect
   251  				styleMask:NSTitledWindowMask
   252  				backing:NSBackingStoreBuffered
   253  				defer:NO];
   254  		window.styleMask |= NSResizableWindowMask;
   255  		window.styleMask |= NSMiniaturizableWindowMask ;
   256  		window.styleMask |= NSClosableWindowMask;
   257  		window.title = name;
   258  		window.displaysWhenScreenProfileChanges = YES;
   259  		[window cascadeTopLeftFromPoint:NSMakePoint(20,20)];
   260  
   261  		NSOpenGLPixelFormatAttribute attr[] = {
   262  			NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
   263  			NSOpenGLPFAColorSize,     24,
   264  			NSOpenGLPFAAlphaSize,     8,
   265  			NSOpenGLPFADepthSize,     16,
   266  			NSOpenGLPFAAccelerated,
   267  			NSOpenGLPFADoubleBuffer,
   268  			NSOpenGLPFAAllowOfflineRenderers,
   269  			0
   270  		};
   271  		id pixFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
   272  		view = [[ScreenGLView alloc] initWithFrame:rect pixelFormat:pixFormat];
   273  		[window setContentView:view];
   274  		[window setDelegate:view];
   275  
   276  		window.nextResponder = [[WindowResponder alloc] initWithWindowID:(uintptr_t)view];
   277  	});
   278  
   279  	return (uintptr_t)view;
   280  }
   281  
   282  void doShowWindow(uintptr_t viewID) {
   283  	ScreenGLView* view = (ScreenGLView*)viewID;
   284  	dispatch_async(dispatch_get_main_queue(), ^{
   285  		[view.window makeKeyAndOrderFront:view.window];
   286  	});
   287  }
   288  
   289  void doCloseWindow(uintptr_t viewID) {
   290  	ScreenGLView* view = (ScreenGLView*)viewID;
   291  	dispatch_sync(dispatch_get_main_queue(), ^{
   292  		[view.window performClose:view];
   293  	});
   294  }
   295  
   296  void startDriver() {
   297  	[NSAutoreleasePool new];
   298  	[NSApplication sharedApplication];
   299  	[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
   300  	AppDelegate* delegate = [[AppDelegate alloc] init];
   301  	[NSApp setDelegate:delegate];
   302  	[NSApp run];
   303  }
   304  
   305  void stopDriver() {
   306  	dispatch_async(dispatch_get_main_queue(), ^{
   307  		[NSApp terminate:nil];
   308  	});
   309  }