mirror of https://github.com/miguelmota/cointop
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
328 lines
9.9 KiB
Objective-C
328 lines
9.9 KiB
Objective-C
// Copyright 2014 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// +build darwin
|
|
// +build 386 amd64
|
|
// +build !ios
|
|
|
|
#include "_cgo_export.h"
|
|
#include <pthread.h>
|
|
#include <stdio.h>
|
|
|
|
#import <Cocoa/Cocoa.h>
|
|
#import <Foundation/Foundation.h>
|
|
#import <OpenGL/gl3.h>
|
|
|
|
// The variables did not exist on older OS X releases,
|
|
// we use the old variables deprecated on macOS to define them.
|
|
#if __MAC_OS_X_VERSION_MAX_ALLOWED < 101200
|
|
enum
|
|
{
|
|
NSEventTypeScrollWheel = NSScrollWheel,
|
|
NSEventTypeKeyDown = NSKeyDown
|
|
};
|
|
enum
|
|
{
|
|
NSWindowStyleMaskTitled = NSTitledWindowMask,
|
|
NSWindowStyleMaskResizable = NSResizableWindowMask,
|
|
NSWindowStyleMaskMiniaturizable = NSMiniaturizableWindowMask,
|
|
NSWindowStyleMaskClosable = NSClosableWindowMask
|
|
};
|
|
#endif
|
|
|
|
void makeCurrentContext(uintptr_t context) {
|
|
NSOpenGLContext* ctx = (NSOpenGLContext*)context;
|
|
[ctx makeCurrentContext];
|
|
}
|
|
|
|
void flushContext(uintptr_t context) {
|
|
NSOpenGLContext* ctx = (NSOpenGLContext*)context;
|
|
[ctx flushBuffer];
|
|
}
|
|
|
|
uint64 threadID() {
|
|
uint64 id;
|
|
if (pthread_threadid_np(pthread_self(), &id)) {
|
|
abort();
|
|
}
|
|
return id;
|
|
}
|
|
|
|
@interface ScreenGLView : NSOpenGLView<NSWindowDelegate>
|
|
{
|
|
}
|
|
@end
|
|
|
|
@implementation ScreenGLView
|
|
- (void)prepareOpenGL {
|
|
[self setWantsBestResolutionOpenGLSurface:YES];
|
|
GLint swapInt = 1;
|
|
NSOpenGLContext *ctx = [self openGLContext];
|
|
[ctx setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
|
|
|
|
// Using attribute arrays in OpenGL 3.3 requires the use of a VBA.
|
|
// But VBAs don't exist in ES 2. So we bind a default one.
|
|
GLuint vba;
|
|
glGenVertexArrays(1, &vba);
|
|
glBindVertexArray(vba);
|
|
|
|
preparedOpenGL((GoUintptr)self, (GoUintptr)ctx, (GoUintptr)vba);
|
|
}
|
|
|
|
- (void)callSetGeom {
|
|
// Calculate screen PPI.
|
|
//
|
|
// Note that the backingScaleFactor converts from logical
|
|
// pixels to actual pixels, but both of these units vary
|
|
// independently from real world size. E.g.
|
|
//
|
|
// 13" Retina Macbook Pro, 2560x1600, 227ppi, backingScaleFactor=2, scale=3.15
|
|
// 15" Retina Macbook Pro, 2880x1800, 220ppi, backingScaleFactor=2, scale=3.06
|
|
// 27" iMac, 2560x1440, 109ppi, backingScaleFactor=1, scale=1.51
|
|
// 27" Retina iMac, 5120x2880, 218ppi, backingScaleFactor=2, scale=3.03
|
|
NSScreen *screen = self.window.screen;
|
|
double screenPixW = [screen frame].size.width * [screen backingScaleFactor];
|
|
|
|
CGDirectDisplayID display = (CGDirectDisplayID)[[[screen deviceDescription] valueForKey:@"NSScreenNumber"] intValue];
|
|
CGSize screenSizeMM = CGDisplayScreenSize(display); // in millimeters
|
|
float ppi = 25.4 * screenPixW / screenSizeMM.width;
|
|
float pixelsPerPt = ppi/72.0;
|
|
|
|
// The width and height reported to the geom package are the
|
|
// bounds of the OpenGL view. Several steps are necessary.
|
|
// First, [self bounds] gives us the number of logical pixels
|
|
// in the view. Multiplying this by the backingScaleFactor
|
|
// gives us the number of actual pixels.
|
|
NSRect r = [self bounds];
|
|
int w = r.size.width * [screen backingScaleFactor];
|
|
int h = r.size.height * [screen backingScaleFactor];
|
|
|
|
setGeom((GoUintptr)self, pixelsPerPt, w, h);
|
|
}
|
|
|
|
- (void)reshape {
|
|
[super reshape];
|
|
[self callSetGeom];
|
|
}
|
|
|
|
- (void)drawRect:(NSRect)theRect {
|
|
// Called during resize. Do an extra draw if we are visible.
|
|
// This gets rid of flicker when resizing.
|
|
drawgl((GoUintptr)self);
|
|
}
|
|
|
|
- (void)mouseEventNS:(NSEvent *)theEvent {
|
|
NSPoint p = [theEvent locationInWindow];
|
|
double h = self.frame.size.height;
|
|
|
|
// Both h and p are measured in Cocoa pixels, which are a fraction of
|
|
// physical pixels, so we multiply by backingScaleFactor.
|
|
double scale = [self.window.screen backingScaleFactor];
|
|
|
|
double x = p.x * scale;
|
|
double y = (h - p.y) * scale - 1; // flip origin from bottom-left to top-left.
|
|
|
|
double dx, dy;
|
|
if (theEvent.type == NSEventTypeScrollWheel) {
|
|
dx = theEvent.scrollingDeltaX;
|
|
dy = theEvent.scrollingDeltaY;
|
|
}
|
|
|
|
mouseEvent((GoUintptr)self, x, y, dx, dy, theEvent.type, theEvent.buttonNumber, theEvent.modifierFlags);
|
|
}
|
|
|
|
- (void)mouseMoved:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
|
|
- (void)mouseDown:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
|
|
- (void)mouseUp:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
|
|
- (void)mouseDragged:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
|
|
- (void)rightMouseDown:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
|
|
- (void)rightMouseUp:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
|
|
- (void)rightMouseDragged:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
|
|
- (void)otherMouseDown:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
|
|
- (void)otherMouseUp:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
|
|
- (void)otherMouseDragged:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
|
|
- (void)scrollWheel:(NSEvent *)theEvent { [self mouseEventNS:theEvent]; }
|
|
|
|
// raw modifier key presses
|
|
- (void)flagsChanged:(NSEvent *)theEvent {
|
|
flagEvent((GoUintptr)self, theEvent.modifierFlags);
|
|
}
|
|
|
|
// overrides special handling of escape and tab
|
|
- (BOOL)performKeyEquivalent:(NSEvent *)theEvent {
|
|
[self key:theEvent];
|
|
return YES;
|
|
}
|
|
|
|
- (void)keyDown:(NSEvent *)theEvent { [self key:theEvent]; }
|
|
- (void)keyUp:(NSEvent *)theEvent { [self key:theEvent]; }
|
|
|
|
- (void)key:(NSEvent *)theEvent {
|
|
NSRange range = [theEvent.characters rangeOfComposedCharacterSequenceAtIndex:0];
|
|
|
|
uint8_t buf[4] = {0, 0, 0, 0};
|
|
if (![theEvent.characters getBytes:buf
|
|
maxLength:4
|
|
usedLength:nil
|
|
encoding:NSUTF32LittleEndianStringEncoding
|
|
options:NSStringEncodingConversionAllowLossy
|
|
range:range
|
|
remainingRange:nil]) {
|
|
NSLog(@"failed to read key event %@", theEvent);
|
|
return;
|
|
}
|
|
|
|
uint32_t rune = (uint32_t)buf[0]<<0 | (uint32_t)buf[1]<<8 | (uint32_t)buf[2]<<16 | (uint32_t)buf[3]<<24;
|
|
|
|
uint8_t direction;
|
|
if ([theEvent isARepeat]) {
|
|
direction = 0;
|
|
} else if (theEvent.type == NSEventTypeKeyDown) {
|
|
direction = 1;
|
|
} else {
|
|
direction = 2;
|
|
}
|
|
keyEvent((GoUintptr)self, (int32_t)rune, direction, theEvent.keyCode, theEvent.modifierFlags);
|
|
}
|
|
|
|
- (void)windowDidChangeScreenProfile:(NSNotification *)notification {
|
|
[self callSetGeom];
|
|
}
|
|
|
|
// TODO: catch windowDidMiniaturize?
|
|
|
|
- (void)windowDidExpose:(NSNotification *)notification {
|
|
lifecycleVisible((GoUintptr)self, true);
|
|
}
|
|
|
|
- (void)windowDidBecomeKey:(NSNotification *)notification {
|
|
lifecycleFocused((GoUintptr)self, true);
|
|
}
|
|
|
|
- (void)windowDidResignKey:(NSNotification *)notification {
|
|
lifecycleFocused((GoUintptr)self, false);
|
|
if ([NSApp isHidden]) {
|
|
lifecycleVisible((GoUintptr)self, false);
|
|
}
|
|
}
|
|
|
|
- (void)windowWillClose:(NSNotification *)notification {
|
|
// TODO: is this right? Closing a window via the top-left red button
|
|
// seems to return early without ever calling windowClosing.
|
|
if (self.window.nextResponder == NULL) {
|
|
return; // already called close
|
|
}
|
|
|
|
windowClosing((GoUintptr)self);
|
|
[self.window.nextResponder release];
|
|
self.window.nextResponder = NULL;
|
|
}
|
|
@end
|
|
|
|
@interface AppDelegate : NSObject<NSApplicationDelegate>
|
|
{
|
|
}
|
|
@end
|
|
|
|
@implementation AppDelegate
|
|
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
|
|
driverStarted();
|
|
[[NSRunningApplication currentApplication] activateWithOptions:(NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps)];
|
|
}
|
|
|
|
- (void)applicationWillTerminate:(NSNotification *)aNotification {
|
|
lifecycleDeadAll();
|
|
}
|
|
|
|
- (void)applicationWillHide:(NSNotification *)aNotification {
|
|
lifecycleHideAll();
|
|
}
|
|
@end
|
|
|
|
uintptr_t doNewWindow(int width, int height, char* title) {
|
|
NSScreen *screen = [NSScreen mainScreen];
|
|
double w = (double)width / [screen backingScaleFactor];
|
|
double h = (double)height / [screen backingScaleFactor];
|
|
__block ScreenGLView* view = NULL;
|
|
|
|
dispatch_sync(dispatch_get_main_queue(), ^{
|
|
id menuBar = [NSMenu new];
|
|
id menuItem = [NSMenuItem new];
|
|
[menuBar addItem:menuItem];
|
|
[NSApp setMainMenu:menuBar];
|
|
|
|
id menu = [NSMenu new];
|
|
NSString* name = [[NSString alloc] initWithUTF8String:title];
|
|
|
|
id hideMenuItem = [[NSMenuItem alloc] initWithTitle:@"Hide"
|
|
action:@selector(hide:) keyEquivalent:@"h"];
|
|
[menu addItem:hideMenuItem];
|
|
|
|
id quitMenuItem = [[NSMenuItem alloc] initWithTitle:@"Quit"
|
|
action:@selector(terminate:) keyEquivalent:@"q"];
|
|
[menu addItem:quitMenuItem];
|
|
[menuItem setSubmenu:menu];
|
|
|
|
NSRect rect = NSMakeRect(0, 0, w, h);
|
|
|
|
NSWindow* window = [[NSWindow alloc] initWithContentRect:rect
|
|
styleMask:NSWindowStyleMaskTitled
|
|
backing:NSBackingStoreBuffered
|
|
defer:NO];
|
|
window.styleMask |= NSWindowStyleMaskResizable;
|
|
window.styleMask |= NSWindowStyleMaskMiniaturizable;
|
|
window.styleMask |= NSWindowStyleMaskClosable;
|
|
window.title = name;
|
|
window.displaysWhenScreenProfileChanges = YES;
|
|
[window cascadeTopLeftFromPoint:NSMakePoint(20,20)];
|
|
[window setAcceptsMouseMovedEvents:YES];
|
|
|
|
NSOpenGLPixelFormatAttribute attr[] = {
|
|
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
|
|
NSOpenGLPFAColorSize, 24,
|
|
NSOpenGLPFAAlphaSize, 8,
|
|
NSOpenGLPFADepthSize, 16,
|
|
NSOpenGLPFADoubleBuffer,
|
|
NSOpenGLPFAAllowOfflineRenderers,
|
|
0
|
|
};
|
|
id pixFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
|
|
view = [[ScreenGLView alloc] initWithFrame:rect pixelFormat:pixFormat];
|
|
[window setContentView:view];
|
|
[window setDelegate:view];
|
|
[window makeFirstResponder:view];
|
|
});
|
|
|
|
return (uintptr_t)view;
|
|
}
|
|
|
|
void doShowWindow(uintptr_t viewID) {
|
|
ScreenGLView* view = (ScreenGLView*)viewID;
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[view.window makeKeyAndOrderFront:view.window];
|
|
});
|
|
}
|
|
|
|
void doCloseWindow(uintptr_t viewID) {
|
|
ScreenGLView* view = (ScreenGLView*)viewID;
|
|
dispatch_sync(dispatch_get_main_queue(), ^{
|
|
[view.window performClose:view];
|
|
});
|
|
}
|
|
|
|
void startDriver() {
|
|
[NSAutoreleasePool new];
|
|
[NSApplication sharedApplication];
|
|
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
|
AppDelegate* delegate = [[AppDelegate alloc] init];
|
|
[NSApp setDelegate:delegate];
|
|
[NSApp run];
|
|
}
|
|
|
|
void stopDriver() {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[NSApp terminate:nil];
|
|
});
|
|
}
|