| // Copyright 2015 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 ios |
| |
| #include "_cgo_export.h" |
| #include <pthread.h> |
| #include <stdio.h> |
| #include <sys/utsname.h> |
| |
| #import <UIKit/UIKit.h> |
| #import <GLKit/GLKit.h> |
| |
| struct utsname sysInfo; |
| |
| @interface GoAppAppController : GLKViewController<UIContentContainer, GLKViewDelegate> |
| @end |
| |
| @interface GoAppAppDelegate : UIResponder<UIApplicationDelegate> |
| @property (strong, nonatomic) UIWindow *window; |
| @property (strong, nonatomic) GoAppAppController *controller; |
| @end |
| |
| @implementation GoAppAppDelegate |
| - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { |
| lifecycleAlive(); |
| self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; |
| self.controller = [[GoAppAppController alloc] initWithNibName:nil bundle:nil]; |
| self.window.rootViewController = self.controller; |
| [self.window makeKeyAndVisible]; |
| return YES; |
| } |
| |
| - (void)applicationDidBecomeActive:(UIApplication * )application { |
| lifecycleFocused(); |
| } |
| |
| - (void)applicationWillResignActive:(UIApplication *)application { |
| lifecycleVisible(); |
| } |
| |
| - (void)applicationDidEnterBackground:(UIApplication *)application { |
| lifecycleAlive(); |
| } |
| |
| - (void)applicationWillTerminate:(UIApplication *)application { |
| lifecycleDead(); |
| } |
| @end |
| |
| @interface GoAppAppController () |
| @property (strong, nonatomic) EAGLContext *context; |
| @property (strong, nonatomic) GLKView *glview; |
| @end |
| |
| @implementation GoAppAppController |
| - (void)viewWillAppear:(BOOL)animated |
| { |
| // TODO: replace by swapping out GLKViewController for a UIVIewController. |
| [super viewWillAppear:animated]; |
| self.paused = YES; |
| } |
| |
| - (void)viewDidLoad { |
| [super viewDidLoad]; |
| self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; |
| self.glview = (GLKView*)self.view; |
| self.glview.drawableDepthFormat = GLKViewDrawableDepthFormat24; |
| self.glview.multipleTouchEnabled = true; // TODO expose setting to user. |
| self.glview.context = self.context; |
| self.glview.userInteractionEnabled = YES; |
| self.glview.enableSetNeedsDisplay = YES; // only invoked once |
| |
| // Do not use the GLKViewController draw loop. |
| self.paused = YES; |
| self.resumeOnDidBecomeActive = NO; |
| self.preferredFramesPerSecond = 0; |
| |
| int scale = 1; |
| if ([[UIScreen mainScreen] respondsToSelector:@selector(displayLinkWithTarget:selector:)]) { |
| scale = (int)[UIScreen mainScreen].scale; // either 1.0, 2.0, or 3.0. |
| } |
| setScreen(scale); |
| |
| CGSize size = [UIScreen mainScreen].bounds.size; |
| UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; |
| updateConfig((int)size.width, (int)size.height, orientation); |
| } |
| |
| - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator { |
| [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) { |
| // TODO(crawshaw): come up with a plan to handle animations. |
| } completion:^(id<UIViewControllerTransitionCoordinatorContext> context) { |
| UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; |
| updateConfig((int)size.width, (int)size.height, orientation); |
| }]; |
| } |
| |
| - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect { |
| // Now that we have been asked to do the first draw, disable any |
| // future draw and hand control over to the Go paint.Event cycle. |
| self.glview.enableSetNeedsDisplay = NO; |
| startloop((GLintptr)self.context); |
| } |
| |
| #define TOUCH_TYPE_BEGIN 0 // touch.TypeBegin |
| #define TOUCH_TYPE_MOVE 1 // touch.TypeMove |
| #define TOUCH_TYPE_END 2 // touch.TypeEnd |
| |
| static void sendTouches(int change, NSSet* touches) { |
| CGFloat scale = [UIScreen mainScreen].scale; |
| for (UITouch* touch in touches) { |
| CGPoint p = [touch locationInView:touch.view]; |
| sendTouch((GoUintptr)touch, (GoUintptr)change, p.x*scale, p.y*scale); |
| } |
| } |
| |
| - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { |
| sendTouches(TOUCH_TYPE_BEGIN, touches); |
| } |
| |
| - (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event { |
| sendTouches(TOUCH_TYPE_MOVE, touches); |
| } |
| |
| - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { |
| sendTouches(TOUCH_TYPE_END, touches); |
| } |
| |
| - (void)touchesCanceled:(NSSet*)touches withEvent:(UIEvent*)event { |
| sendTouches(TOUCH_TYPE_END, touches); |
| } |
| @end |
| |
| void runApp(void) { |
| @autoreleasepool { |
| UIApplicationMain(0, nil, nil, NSStringFromClass([GoAppAppDelegate class])); |
| } |
| } |
| |
| void makeCurrentContext(GLintptr context) { |
| EAGLContext* ctx = (EAGLContext*)context; |
| if (![EAGLContext setCurrentContext:ctx]) { |
| // TODO(crawshaw): determine how terrible this is. Exit? |
| NSLog(@"failed to set current context"); |
| } |
| } |
| |
| void swapBuffers(GLintptr context) { |
| __block EAGLContext* ctx = (EAGLContext*)context; |
| dispatch_sync(dispatch_get_main_queue(), ^{ |
| [EAGLContext setCurrentContext:ctx]; |
| [ctx presentRenderbuffer:GL_RENDERBUFFER]; |
| }); |
| } |
| |
| uint64_t threadID() { |
| uint64_t id; |
| if (pthread_threadid_np(pthread_self(), &id)) { |
| abort(); |
| } |
| return id; |
| } |