view_controller.mm 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. /**************************************************************************/
  2. /* view_controller.mm */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. #import "view_controller.h"
  31. #import "display_server_ios.h"
  32. #import "godot_view.h"
  33. #import "godot_view_renderer.h"
  34. #import "key_mapping_ios.h"
  35. #import "keyboard_input_view.h"
  36. #import "os_ios.h"
  37. #include "core/config/project_settings.h"
  38. #import <AVFoundation/AVFoundation.h>
  39. #import <GameController/GameController.h>
  40. @interface ViewController () <GodotViewDelegate>
  41. @property(strong, nonatomic) GodotViewRenderer *renderer;
  42. @property(strong, nonatomic) GodotKeyboardInputView *keyboardView;
  43. @property(strong, nonatomic) UIView *godotLoadingOverlay;
  44. @end
  45. @implementation ViewController
  46. - (GodotView *)godotView {
  47. return (GodotView *)self.view;
  48. }
  49. - (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event {
  50. [super pressesBegan:presses withEvent:event];
  51. if (!DisplayServerIOS::get_singleton() || DisplayServerIOS::get_singleton()->is_keyboard_active()) {
  52. return;
  53. }
  54. if (@available(iOS 13.4, *)) {
  55. for (UIPress *press in presses) {
  56. String u32lbl = String::utf8([press.key.charactersIgnoringModifiers UTF8String]);
  57. String u32text = String::utf8([press.key.characters UTF8String]);
  58. Key key = KeyMappingIOS::remap_key(press.key.keyCode);
  59. if (press.key.keyCode == 0 && u32text.is_empty() && u32lbl.is_empty()) {
  60. continue;
  61. }
  62. char32_t us = 0;
  63. if (!u32lbl.is_empty() && !u32lbl.begins_with("UIKey")) {
  64. us = u32lbl[0];
  65. }
  66. KeyLocation location = KeyMappingIOS::key_location(press.key.keyCode);
  67. if (!u32text.is_empty() && !u32text.begins_with("UIKey")) {
  68. for (int i = 0; i < u32text.length(); i++) {
  69. const char32_t c = u32text[i];
  70. DisplayServerIOS::get_singleton()->key(fix_keycode(us, key), c, fix_key_label(us, key), key, press.key.modifierFlags, true, location);
  71. }
  72. } else {
  73. DisplayServerIOS::get_singleton()->key(fix_keycode(us, key), 0, fix_key_label(us, key), key, press.key.modifierFlags, true, location);
  74. }
  75. }
  76. }
  77. }
  78. - (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event {
  79. [super pressesEnded:presses withEvent:event];
  80. if (!DisplayServerIOS::get_singleton() || DisplayServerIOS::get_singleton()->is_keyboard_active()) {
  81. return;
  82. }
  83. if (@available(iOS 13.4, *)) {
  84. for (UIPress *press in presses) {
  85. String u32lbl = String::utf8([press.key.charactersIgnoringModifiers UTF8String]);
  86. Key key = KeyMappingIOS::remap_key(press.key.keyCode);
  87. if (press.key.keyCode == 0 && u32lbl.is_empty()) {
  88. continue;
  89. }
  90. char32_t us = 0;
  91. if (!u32lbl.is_empty() && !u32lbl.begins_with("UIKey")) {
  92. us = u32lbl[0];
  93. }
  94. KeyLocation location = KeyMappingIOS::key_location(press.key.keyCode);
  95. DisplayServerIOS::get_singleton()->key(fix_keycode(us, key), 0, fix_key_label(us, key), key, press.key.modifierFlags, false, location);
  96. }
  97. }
  98. }
  99. - (void)loadView {
  100. GodotView *view = [[GodotView alloc] init];
  101. GodotViewRenderer *renderer = [[GodotViewRenderer alloc] init];
  102. self.renderer = renderer;
  103. self.view = view;
  104. view.renderer = self.renderer;
  105. view.delegate = self;
  106. }
  107. - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
  108. self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
  109. if (self) {
  110. [self godot_commonInit];
  111. }
  112. return self;
  113. }
  114. - (instancetype)initWithCoder:(NSCoder *)coder {
  115. self = [super initWithCoder:coder];
  116. if (self) {
  117. [self godot_commonInit];
  118. }
  119. return self;
  120. }
  121. - (void)godot_commonInit {
  122. // Initialize view controller values.
  123. }
  124. - (void)didReceiveMemoryWarning {
  125. [super didReceiveMemoryWarning];
  126. print_verbose("Did receive memory warning!");
  127. }
  128. - (void)viewDidLoad {
  129. [super viewDidLoad];
  130. [self observeKeyboard];
  131. [self displayLoadingOverlay];
  132. [self setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
  133. }
  134. - (void)observeKeyboard {
  135. print_verbose("Setting up keyboard input view.");
  136. self.keyboardView = [GodotKeyboardInputView new];
  137. [self.view addSubview:self.keyboardView];
  138. print_verbose("Adding observer for keyboard show/hide.");
  139. [[NSNotificationCenter defaultCenter]
  140. addObserver:self
  141. selector:@selector(keyboardOnScreen:)
  142. name:UIKeyboardDidShowNotification
  143. object:nil];
  144. [[NSNotificationCenter defaultCenter]
  145. addObserver:self
  146. selector:@selector(keyboardHidden:)
  147. name:UIKeyboardDidHideNotification
  148. object:nil];
  149. }
  150. - (void)displayLoadingOverlay {
  151. NSBundle *bundle = [NSBundle mainBundle];
  152. NSString *storyboardName = @"Launch Screen";
  153. if ([bundle pathForResource:storyboardName ofType:@"storyboardc"] == nil) {
  154. return;
  155. }
  156. UIStoryboard *launchStoryboard = [UIStoryboard storyboardWithName:storyboardName bundle:bundle];
  157. UIViewController *controller = [launchStoryboard instantiateInitialViewController];
  158. self.godotLoadingOverlay = controller.view;
  159. self.godotLoadingOverlay.frame = self.view.bounds;
  160. self.godotLoadingOverlay.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
  161. [self.view addSubview:self.godotLoadingOverlay];
  162. }
  163. - (BOOL)godotViewFinishedSetup:(GodotView *)view {
  164. [self.godotLoadingOverlay removeFromSuperview];
  165. self.godotLoadingOverlay = nil;
  166. return YES;
  167. }
  168. - (void)dealloc {
  169. self.keyboardView = nil;
  170. self.renderer = nil;
  171. if (self.godotLoadingOverlay) {
  172. [self.godotLoadingOverlay removeFromSuperview];
  173. self.godotLoadingOverlay = nil;
  174. }
  175. [[NSNotificationCenter defaultCenter] removeObserver:self];
  176. }
  177. // MARK: Orientation
  178. - (UIRectEdge)preferredScreenEdgesDeferringSystemGestures {
  179. if (GLOBAL_GET("display/window/ios/suppress_ui_gesture")) {
  180. return UIRectEdgeAll;
  181. } else {
  182. return UIRectEdgeNone;
  183. }
  184. }
  185. - (BOOL)shouldAutorotate {
  186. if (!DisplayServerIOS::get_singleton()) {
  187. return NO;
  188. }
  189. switch (DisplayServerIOS::get_singleton()->screen_get_orientation(DisplayServer::SCREEN_OF_MAIN_WINDOW)) {
  190. case DisplayServer::SCREEN_SENSOR:
  191. case DisplayServer::SCREEN_SENSOR_LANDSCAPE:
  192. case DisplayServer::SCREEN_SENSOR_PORTRAIT:
  193. return YES;
  194. default:
  195. return NO;
  196. }
  197. }
  198. - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
  199. if (!DisplayServerIOS::get_singleton()) {
  200. return UIInterfaceOrientationMaskAll;
  201. }
  202. switch (DisplayServerIOS::get_singleton()->screen_get_orientation(DisplayServer::SCREEN_OF_MAIN_WINDOW)) {
  203. case DisplayServer::SCREEN_PORTRAIT:
  204. return UIInterfaceOrientationMaskPortrait;
  205. case DisplayServer::SCREEN_REVERSE_LANDSCAPE:
  206. if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
  207. return UIInterfaceOrientationMaskLandscapeLeft;
  208. } else {
  209. return UIInterfaceOrientationMaskLandscapeRight;
  210. }
  211. case DisplayServer::SCREEN_REVERSE_PORTRAIT:
  212. return UIInterfaceOrientationMaskPortraitUpsideDown;
  213. case DisplayServer::SCREEN_SENSOR_LANDSCAPE:
  214. return UIInterfaceOrientationMaskLandscape;
  215. case DisplayServer::SCREEN_SENSOR_PORTRAIT:
  216. return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
  217. case DisplayServer::SCREEN_SENSOR:
  218. return UIInterfaceOrientationMaskAll;
  219. case DisplayServer::SCREEN_LANDSCAPE:
  220. if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
  221. return UIInterfaceOrientationMaskLandscapeRight;
  222. } else {
  223. return UIInterfaceOrientationMaskLandscapeLeft;
  224. }
  225. }
  226. }
  227. - (BOOL)prefersStatusBarHidden {
  228. if (GLOBAL_GET("display/window/ios/hide_status_bar")) {
  229. return YES;
  230. } else {
  231. return NO;
  232. }
  233. }
  234. - (BOOL)prefersHomeIndicatorAutoHidden {
  235. if (GLOBAL_GET("display/window/ios/hide_home_indicator")) {
  236. return YES;
  237. } else {
  238. return NO;
  239. }
  240. }
  241. // MARK: Keyboard
  242. - (void)keyboardOnScreen:(NSNotification *)notification {
  243. NSDictionary *info = notification.userInfo;
  244. NSValue *value = info[UIKeyboardFrameEndUserInfoKey];
  245. CGRect rawFrame = [value CGRectValue];
  246. CGRect keyboardFrame = [self.view convertRect:rawFrame fromView:nil];
  247. if (DisplayServerIOS::get_singleton()) {
  248. DisplayServerIOS::get_singleton()->virtual_keyboard_set_height(keyboardFrame.size.height);
  249. }
  250. }
  251. - (void)keyboardHidden:(NSNotification *)notification {
  252. if (DisplayServerIOS::get_singleton()) {
  253. DisplayServerIOS::get_singleton()->virtual_keyboard_set_height(0);
  254. }
  255. }
  256. @end