github.com/gop9/olt@v0.0.0-20200202132135-d956aad50b08/gio/app/internal/window/os_ios.m (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  // +build darwin,ios
     4  
     5  @import UIKit;
     6  
     7  #include <stdint.h>
     8  #include "_cgo_export.h"
     9  #include "os_ios.h"
    10  #include "framework_ios.h"
    11  
    12  @interface GioView: UIView <UIKeyInput>
    13  - (void)setAnimating:(BOOL)anim;
    14  @end
    15  
    16  @interface GioViewController : UIViewController
    17  @property(weak) UIScreen *screen;
    18  @end
    19  
    20  static void redraw(CFTypeRef viewRef, BOOL sync) {
    21  	UIView *v = (__bridge UIView *)viewRef;
    22  	CGFloat scale = v.layer.contentsScale;
    23  	// Use 163 as the standard ppi on iOS.
    24  	CGFloat dpi = 163*scale;
    25  	CGFloat sdpi = dpi;
    26  	UIEdgeInsets insets = v.layoutMargins;
    27  	if (@available(iOS 11.0, tvOS 11.0, *)) {
    28  		UIFontMetrics *metrics = [UIFontMetrics defaultMetrics];
    29  		sdpi = [metrics scaledValueForValue:sdpi];
    30  		insets = v.safeAreaInsets;
    31  	}
    32  	onDraw(viewRef, dpi, sdpi, v.bounds.size.width*scale, v.bounds.size.height*scale, sync,
    33  			insets.top*scale, insets.right*scale, insets.bottom*scale, insets.left*scale);
    34  }
    35  
    36  @implementation GioAppDelegate
    37  - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    38  	gio_runMain();
    39  
    40  	self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    41  	GioViewController *controller = [[GioViewController alloc] initWithNibName:nil bundle:nil];
    42  	controller.screen = self.window.screen;
    43      self.window.rootViewController = controller;
    44      [self.window makeKeyAndVisible];
    45  	return YES;
    46  }
    47  
    48  - (void)applicationWillResignActive:(UIApplication *)application {
    49  }
    50  
    51  - (void)applicationDidEnterBackground:(UIApplication *)application {
    52  	GioViewController *vc = (GioViewController *)self.window.rootViewController;
    53  	UIView *drawView = vc.view.subviews[0];
    54  	if (drawView != nil) {
    55  		onStop((__bridge CFTypeRef)drawView);
    56  	}
    57  }
    58  
    59  - (void)applicationWillEnterForeground:(UIApplication *)application {
    60  	GioViewController *c = (GioViewController*)self.window.rootViewController;
    61  	UIView *drawView = c.view.subviews[0];
    62  	if (drawView != nil) {
    63  		CFTypeRef viewRef = (__bridge CFTypeRef)drawView;
    64  		redraw(viewRef, YES);
    65  	}
    66  }
    67  @end
    68  
    69  @implementation GioViewController
    70  CGFloat _keyboardHeight;
    71  
    72  - (void)loadView {
    73  	CGRect zeroFrame = CGRectMake(0, 0, 0, 0);
    74  	self.view = [[UIView alloc] initWithFrame:zeroFrame];
    75  	self.view.layoutMargins = UIEdgeInsetsMake(0, 0, 0, 0);
    76  	UIView *drawView = [[GioView alloc] initWithFrame:zeroFrame];
    77  	[self.view addSubview: drawView];
    78  #ifndef TARGET_OS_TV
    79  	drawView.multipleTouchEnabled = YES;
    80  #endif
    81  	drawView.contentScaleFactor = self.screen.nativeScale;
    82  	drawView.preservesSuperviewLayoutMargins = YES;
    83  	drawView.layoutMargins = UIEdgeInsetsMake(0, 0, 0, 0);
    84  	onCreate((__bridge CFTypeRef)drawView);
    85  	[[NSNotificationCenter defaultCenter] addObserver:self
    86  											 selector:@selector(keyboardWillChange:)
    87  												 name:UIKeyboardWillShowNotification
    88  											   object:nil];
    89  	[[NSNotificationCenter defaultCenter] addObserver:self
    90  											 selector:@selector(keyboardWillChange:)
    91  												 name:UIKeyboardWillChangeFrameNotification
    92  											   object:nil];
    93  	[[NSNotificationCenter defaultCenter] addObserver:self
    94  											 selector:@selector(keyboardWillHide:)
    95  												 name:UIKeyboardWillHideNotification
    96  											   object:nil];
    97  }
    98  
    99  - (void)viewDidDisappear:(BOOL)animated {
   100  	[super viewDidDisappear:animated];
   101  	CFTypeRef viewRef = (__bridge CFTypeRef)self.view.subviews[0];
   102  	onDestroy(viewRef);
   103  }
   104  
   105  - (void)viewDidLayoutSubviews {
   106  	[super viewDidLayoutSubviews];
   107  	UIView *view = self.view.subviews[0];
   108  	CGRect frame = self.view.bounds;
   109  	// Adjust view bounds to make room for the keyboard.
   110  	frame.size.height -= _keyboardHeight;
   111  	view.frame = frame;
   112  	redraw((__bridge CFTypeRef)view, YES);
   113  }
   114  
   115  - (void)didReceiveMemoryWarning {
   116  	onLowMemory();
   117  	[super didReceiveMemoryWarning];
   118  }
   119  
   120  - (void)keyboardWillChange:(NSNotification *)note {
   121  	NSDictionary *userInfo = note.userInfo;
   122  	CGRect f = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
   123  	_keyboardHeight = f.size.height;
   124  	[self.view setNeedsLayout];
   125  }
   126  
   127  - (void)keyboardWillHide:(NSNotification *)note {
   128  	_keyboardHeight = 0.0;
   129  	[self.view setNeedsLayout];
   130  }
   131  @end
   132  
   133  static void handleTouches(int last, UIView *view, NSSet<UITouch *> *touches, UIEvent *event) {
   134  	CGFloat scale = view.contentScaleFactor;
   135  	NSUInteger i = 0;
   136  	NSUInteger n = [touches count];
   137  	CFTypeRef viewRef = (__bridge CFTypeRef)view;
   138  	for (UITouch *touch in touches) {
   139  		CFTypeRef touchRef = (__bridge CFTypeRef)touch;
   140  		i++;
   141  		NSArray<UITouch *> *coalescedTouches = [event coalescedTouchesForTouch:touch];
   142  		NSUInteger j = 0;
   143  		NSUInteger m = [coalescedTouches count];
   144  		for (UITouch *coalescedTouch in [event coalescedTouchesForTouch:touch]) {
   145  			CGPoint loc = [coalescedTouch locationInView:view];
   146  			j++;
   147  			int lastTouch = last && i == n && j == m;
   148  			onTouch(lastTouch, viewRef, touchRef, touch.phase, loc.x*scale, loc.y*scale, [coalescedTouch timestamp]);
   149  		}
   150  	}
   151  }
   152  
   153  @implementation GioView
   154  CADisplayLink *displayLink;
   155  NSArray<UIKeyCommand *> *_keyCommands;
   156  
   157  - (void)onFrameCallback:(CADisplayLink *)link {
   158  	redraw((__bridge CFTypeRef)self, NO);
   159  }
   160  
   161  - (instancetype)initWithFrame:(CGRect)frame {
   162  	self = [super initWithFrame:frame];
   163  	if (self) {
   164  		__weak id weakSelf = self;
   165  		displayLink = [CADisplayLink displayLinkWithTarget:weakSelf selector:@selector(onFrameCallback:)];
   166  	}
   167  	return self;
   168  }
   169  
   170  - (void)willMoveToWindow:(UIWindow *)newWindow {
   171  	if (self.window != nil) {
   172  		[[NSNotificationCenter defaultCenter] removeObserver:self
   173  														name:UIWindowDidBecomeKeyNotification
   174  													  object:self.window];
   175  		[[NSNotificationCenter defaultCenter] removeObserver:self
   176  														name:UIWindowDidResignKeyNotification
   177  													  object:self.window];
   178  	}
   179  	[[NSNotificationCenter defaultCenter] addObserver:self
   180  											 selector:@selector(onWindowDidBecomeKey:)
   181  												 name:UIWindowDidBecomeKeyNotification
   182  											   object:newWindow];
   183  	[[NSNotificationCenter defaultCenter] addObserver:self
   184  											 selector:@selector(onWindowDidResignKey:)
   185  												 name:UIWindowDidResignKeyNotification
   186  											   object:newWindow];
   187  }
   188  
   189  - (void)onWindowDidBecomeKey:(NSNotification *)note {
   190  	if (self.isFirstResponder) {
   191  		onFocus((__bridge CFTypeRef)self, YES);
   192  	}
   193  }
   194  
   195  - (void)onWindowDidResignKey:(NSNotification *)note {
   196  	if (self.isFirstResponder) {
   197  		onFocus((__bridge CFTypeRef)self, NO);
   198  	}
   199  }
   200  
   201  - (void)dealloc {
   202  	[displayLink invalidate];
   203  }
   204  
   205  - (void)setAnimating:(BOOL)anim {
   206  	NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
   207  	if (anim) {
   208  		[displayLink addToRunLoop:runLoop forMode:[runLoop currentMode]];
   209  	} else {
   210  		[displayLink removeFromRunLoop:runLoop forMode:[runLoop currentMode]];
   211  	}
   212  }
   213  
   214  - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
   215  	handleTouches(0, self, touches, event);
   216  }
   217  
   218  - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
   219  	handleTouches(0, self, touches, event);
   220  }
   221  
   222  - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
   223  	handleTouches(1, self, touches, event);
   224  }
   225  
   226  - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
   227  	handleTouches(1, self, touches, event);
   228  }
   229  
   230  - (void)insertText:(NSString *)text {
   231  	onText((__bridge CFTypeRef)self, (char *)text.UTF8String);
   232  }
   233  
   234  - (BOOL)canBecomeFirstResponder {
   235  	return YES;
   236  }
   237  
   238  - (BOOL)hasText {
   239  	return YES;
   240  }
   241  
   242  - (void)deleteBackward {
   243  	onDeleteBackward((__bridge CFTypeRef)self);
   244  }
   245  
   246  - (void)onUpArrow {
   247  	onUpArrow((__bridge CFTypeRef)self);
   248  }
   249  
   250  - (void)onDownArrow {
   251  	onDownArrow((__bridge CFTypeRef)self);
   252  }
   253  
   254  - (void)onLeftArrow {
   255  	onLeftArrow((__bridge CFTypeRef)self);
   256  }
   257  
   258  - (void)onRightArrow {
   259  	onRightArrow((__bridge CFTypeRef)self);
   260  }
   261  
   262  - (NSArray<UIKeyCommand *> *)keyCommands {
   263  	if (_keyCommands == nil) {
   264  		_keyCommands = @[
   265  			[UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow
   266  								modifierFlags:0
   267  									   action:@selector(onUpArrow)],
   268  			[UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow
   269  								modifierFlags:0
   270  									   action:@selector(onDownArrow)],
   271  			[UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow
   272  								modifierFlags:0
   273  									   action:@selector(onLeftArrow)],
   274  			[UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow
   275  								modifierFlags:0
   276  									   action:@selector(onRightArrow)]
   277  		];
   278  	}
   279  	return _keyCommands;
   280  }
   281  @end
   282  
   283  void gio_setAnimating(CFTypeRef viewRef, int anim) {
   284  	GioView *view = (__bridge GioView *)viewRef;
   285  	dispatch_async(dispatch_get_main_queue(), ^{
   286  		[view setAnimating:(anim ? YES : NO)];
   287  	});
   288  }
   289  
   290  void gio_showTextInput(CFTypeRef viewRef) {
   291  	UIView *view = (__bridge UIView *)viewRef;
   292  	dispatch_async(dispatch_get_main_queue(), ^{
   293  		[view becomeFirstResponder];
   294  	});
   295  }
   296  
   297  void gio_hideTextInput(CFTypeRef viewRef) {
   298  	UIView *view = (__bridge UIView *)viewRef;
   299  	dispatch_async(dispatch_get_main_queue(), ^{
   300  		[view resignFirstResponder];
   301  	});
   302  }
   303  
   304  void gio_addLayerToView(CFTypeRef viewRef, CFTypeRef layerRef) {
   305  	UIView *view = (__bridge UIView *)viewRef;
   306  	CALayer *layer = (__bridge CALayer *)layerRef;
   307  	[view.layer addSublayer:layer];
   308  }
   309  
   310  void gio_updateView(CFTypeRef viewRef, CFTypeRef layerRef) {
   311  	UIView *view = (__bridge UIView *)viewRef;
   312  	CAEAGLLayer *layer = (__bridge CAEAGLLayer *)layerRef;
   313  	layer.contentsScale = view.contentScaleFactor;
   314  	layer.bounds = view.bounds;
   315  }
   316  
   317  void gio_removeLayer(CFTypeRef layerRef) {
   318  	CALayer *layer = (__bridge CALayer *)layerRef;
   319  	[layer removeFromSuperlayer];
   320  }