sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_uikitappdelegate.m (17439B)


      1 /*
      2   Simple DirectMedia Layer
      3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
      4 
      5   This software is provided 'as-is', without any express or implied
      6   warranty.  In no event will the authors be held liable for any damages
      7   arising from the use of this software.
      8 
      9   Permission is granted to anyone to use this software for any purpose,
     10   including commercial applications, and to alter it and redistribute it
     11   freely, subject to the following restrictions:
     12 
     13   1. The origin of this software must not be misrepresented; you must not
     14      claim that you wrote the original software. If you use this software
     15      in a product, an acknowledgment in the product documentation would be
     16      appreciated but is not required.
     17   2. Altered source versions must be plainly marked as such, and must not be
     18      misrepresented as being the original software.
     19   3. This notice may not be removed or altered from any source distribution.
     20 */
     21 #include "../../SDL_internal.h"
     22 
     23 #if SDL_VIDEO_DRIVER_UIKIT
     24 
     25 #include "../SDL_sysvideo.h"
     26 #include "SDL_hints.h"
     27 #include "SDL_system.h"
     28 #include "SDL_main.h"
     29 
     30 #import "SDL_uikitappdelegate.h"
     31 #import "SDL_uikitmodes.h"
     32 #import "SDL_uikitwindow.h"
     33 
     34 #include "../../events/SDL_events_c.h"
     35 
     36 #ifdef main
     37 #undef main
     38 #endif
     39 
     40 static SDL_main_func forward_main;
     41 static int forward_argc;
     42 static char **forward_argv;
     43 static int exit_status;
     44 
     45 int SDL_UIKitRunApp(int argc, char *argv[], SDL_main_func mainFunction)
     46 {
     47     int i;
     48 
     49     /* store arguments */
     50     forward_main = mainFunction;
     51     forward_argc = argc;
     52     forward_argv = (char **)malloc((argc+1) * sizeof(char *));
     53     for (i = 0; i < argc; i++) {
     54         forward_argv[i] = malloc( (strlen(argv[i])+1) * sizeof(char));
     55         strcpy(forward_argv[i], argv[i]);
     56     }
     57     forward_argv[i] = NULL;
     58 
     59     /* Give over control to run loop, SDLUIKitDelegate will handle most things from here */
     60     @autoreleasepool {
     61         UIApplicationMain(argc, argv, nil, [SDLUIKitDelegate getAppDelegateClassName]);
     62     }
     63 
     64     /* free the memory we used to hold copies of argc and argv */
     65     for (i = 0; i < forward_argc; i++) {
     66         free(forward_argv[i]);
     67     }
     68     free(forward_argv);
     69 
     70     return exit_status;
     71 }
     72 
     73 static void SDLCALL
     74 SDL_IdleTimerDisabledChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
     75 {
     76     BOOL disable = (hint && *hint != '0');
     77     [UIApplication sharedApplication].idleTimerDisabled = disable;
     78 }
     79 
     80 #if !TARGET_OS_TV
     81 /* Load a launch image using the old UILaunchImageFile-era naming rules. */
     82 static UIImage *
     83 SDL_LoadLaunchImageNamed(NSString *name, int screenh)
     84 {
     85     UIInterfaceOrientation curorient = [UIApplication sharedApplication].statusBarOrientation;
     86     UIUserInterfaceIdiom idiom = [UIDevice currentDevice].userInterfaceIdiom;
     87     UIImage *image = nil;
     88 
     89     if (idiom == UIUserInterfaceIdiomPhone && screenh == 568) {
     90         /* The image name for the iPhone 5 uses its height as a suffix. */
     91         image = [UIImage imageNamed:[NSString stringWithFormat:@"%@-568h", name]];
     92     } else if (idiom == UIUserInterfaceIdiomPad) {
     93         /* iPad apps can launch in any orientation. */
     94         if (UIInterfaceOrientationIsLandscape(curorient)) {
     95             if (curorient == UIInterfaceOrientationLandscapeLeft) {
     96                 image = [UIImage imageNamed:[NSString stringWithFormat:@"%@-LandscapeLeft", name]];
     97             } else {
     98                 image = [UIImage imageNamed:[NSString stringWithFormat:@"%@-LandscapeRight", name]];
     99             }
    100             if (!image) {
    101                 image = [UIImage imageNamed:[NSString stringWithFormat:@"%@-Landscape", name]];
    102             }
    103         } else {
    104             if (curorient == UIInterfaceOrientationPortraitUpsideDown) {
    105                 image = [UIImage imageNamed:[NSString stringWithFormat:@"%@-PortraitUpsideDown", name]];
    106             }
    107             if (!image) {
    108                 image = [UIImage imageNamed:[NSString stringWithFormat:@"%@-Portrait", name]];
    109             }
    110         }
    111     }
    112 
    113     if (!image) {
    114         image = [UIImage imageNamed:name];
    115     }
    116 
    117     return image;
    118 }
    119 #endif /* !TARGET_OS_TV */
    120 
    121 @interface SDLLaunchScreenController ()
    122 
    123 #if !TARGET_OS_TV
    124 - (NSUInteger)supportedInterfaceOrientations;
    125 #endif
    126 
    127 @end
    128 
    129 @implementation SDLLaunchScreenController
    130 
    131 - (instancetype)init
    132 {
    133     return [self initWithNibName:nil bundle:[NSBundle mainBundle]];
    134 }
    135 
    136 - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    137 {
    138     if (!(self = [super initWithNibName:nil bundle:nil])) {
    139         return nil;
    140     }
    141 
    142     NSString *screenname = nibNameOrNil;
    143     NSBundle *bundle = nibBundleOrNil;
    144     BOOL atleastiOS8 = UIKit_IsSystemVersionAtLeast(8.0);
    145 
    146     /* Launch screens were added in iOS 8. Otherwise we use launch images. */
    147     if (screenname && atleastiOS8) {
    148         @try {
    149             self.view = [bundle loadNibNamed:screenname owner:self options:nil][0];
    150         }
    151         @catch (NSException *exception) {
    152             /* If a launch screen name is specified but it fails to load, iOS
    153              * displays a blank screen rather than falling back to an image. */
    154             return nil;
    155         }
    156     }
    157 
    158     if (!self.view) {
    159         NSArray *launchimages = [bundle objectForInfoDictionaryKey:@"UILaunchImages"];
    160         NSString *imagename = nil;
    161         UIImage *image = nil;
    162 
    163         int screenw = (int)([UIScreen mainScreen].bounds.size.width + 0.5);
    164         int screenh = (int)([UIScreen mainScreen].bounds.size.height + 0.5);
    165 
    166 #if !TARGET_OS_TV
    167         UIInterfaceOrientation curorient = [UIApplication sharedApplication].statusBarOrientation;
    168 
    169         /* We always want portrait-oriented size, to match UILaunchImageSize. */
    170         if (screenw > screenh) {
    171             int width = screenw;
    172             screenw = screenh;
    173             screenh = width;
    174         }
    175 #endif
    176 
    177         /* Xcode 5 introduced a dictionary of launch images in Info.plist. */
    178         if (launchimages) {
    179             for (NSDictionary *dict in launchimages) {
    180                 NSString *minversion = dict[@"UILaunchImageMinimumOSVersion"];
    181                 NSString *sizestring = dict[@"UILaunchImageSize"];
    182 
    183                 /* Ignore this image if the current version is too low. */
    184                 if (minversion && !UIKit_IsSystemVersionAtLeast(minversion.doubleValue)) {
    185                     continue;
    186                 }
    187 
    188                 /* Ignore this image if the size doesn't match. */
    189                 if (sizestring) {
    190                     CGSize size = CGSizeFromString(sizestring);
    191                     if ((int)(size.width + 0.5) != screenw || (int)(size.height + 0.5) != screenh) {
    192                         continue;
    193                     }
    194                 }
    195 
    196 #if !TARGET_OS_TV
    197                 UIInterfaceOrientationMask orientmask = UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
    198                 NSString *orientstring = dict[@"UILaunchImageOrientation"];
    199 
    200                 if (orientstring) {
    201                     if ([orientstring isEqualToString:@"PortraitUpsideDown"]) {
    202                         orientmask = UIInterfaceOrientationMaskPortraitUpsideDown;
    203                     } else if ([orientstring isEqualToString:@"Landscape"]) {
    204                         orientmask = UIInterfaceOrientationMaskLandscape;
    205                     } else if ([orientstring isEqualToString:@"LandscapeLeft"]) {
    206                         orientmask = UIInterfaceOrientationMaskLandscapeLeft;
    207                     } else if ([orientstring isEqualToString:@"LandscapeRight"]) {
    208                         orientmask = UIInterfaceOrientationMaskLandscapeRight;
    209                     }
    210                 }
    211 
    212                 /* Ignore this image if the orientation doesn't match. */
    213                 if ((orientmask & (1 << curorient)) == 0) {
    214                     continue;
    215                 }
    216 #endif
    217 
    218                 imagename = dict[@"UILaunchImageName"];
    219             }
    220 
    221             if (imagename) {
    222                 image = [UIImage imageNamed:imagename];
    223             }
    224         }
    225 #if !TARGET_OS_TV
    226         else {
    227             imagename = [bundle objectForInfoDictionaryKey:@"UILaunchImageFile"];
    228 
    229             if (imagename) {
    230                 image = SDL_LoadLaunchImageNamed(imagename, screenh);
    231             }
    232 
    233             if (!image) {
    234                 image = SDL_LoadLaunchImageNamed(@"Default", screenh);
    235             }
    236         }
    237 #endif
    238 
    239         if (image) {
    240             UIImageView *view = [[UIImageView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    241             UIImageOrientation imageorient = UIImageOrientationUp;
    242 
    243 #if !TARGET_OS_TV
    244             /* Bugs observed / workaround tested in iOS 8.3, 7.1, and 6.1. */
    245             if (UIInterfaceOrientationIsLandscape(curorient)) {
    246                 if (atleastiOS8 && image.size.width < image.size.height) {
    247                     /* On iOS 8, portrait launch images displayed in forced-
    248                      * landscape mode (e.g. a standard Default.png on an iPhone
    249                      * when Info.plist only supports landscape orientations) need
    250                      * to be rotated to display in the expected orientation. */
    251                     if (curorient == UIInterfaceOrientationLandscapeLeft) {
    252                         imageorient = UIImageOrientationRight;
    253                     } else if (curorient == UIInterfaceOrientationLandscapeRight) {
    254                         imageorient = UIImageOrientationLeft;
    255                     }
    256                 } else if (!atleastiOS8 && image.size.width > image.size.height) {
    257                     /* On iOS 7 and below, landscape launch images displayed in
    258                      * landscape mode (e.g. landscape iPad launch images) need
    259                      * to be rotated to display in the expected orientation. */
    260                     if (curorient == UIInterfaceOrientationLandscapeLeft) {
    261                         imageorient = UIImageOrientationLeft;
    262                     } else if (curorient == UIInterfaceOrientationLandscapeRight) {
    263                         imageorient = UIImageOrientationRight;
    264                     }
    265                 }
    266             }
    267 #endif
    268 
    269             /* Create the properly oriented image. */
    270             view.image = [[UIImage alloc] initWithCGImage:image.CGImage scale:image.scale orientation:imageorient];
    271 
    272             self.view = view;
    273         }
    274     }
    275 
    276     return self;
    277 }
    278 
    279 - (void)loadView
    280 {
    281     /* Do nothing. */
    282 }
    283 
    284 #if !TARGET_OS_TV
    285 - (BOOL)shouldAutorotate
    286 {
    287     /* If YES, the launch image will be incorrectly rotated in some cases. */
    288     return NO;
    289 }
    290 
    291 - (NSUInteger)supportedInterfaceOrientations
    292 {
    293     /* We keep the supported orientations unrestricted to avoid the case where
    294      * there are no common orientations between the ones set in Info.plist and
    295      * the ones set here (it will cause an exception in that case.) */
    296     return UIInterfaceOrientationMaskAll;
    297 }
    298 #endif /* !TARGET_OS_TV */
    299 
    300 @end
    301 
    302 @implementation SDLUIKitDelegate {
    303     UIWindow *launchWindow;
    304 }
    305 
    306 /* convenience method */
    307 + (id)sharedAppDelegate
    308 {
    309     /* the delegate is set in UIApplicationMain(), which is guaranteed to be
    310      * called before this method */
    311     return [UIApplication sharedApplication].delegate;
    312 }
    313 
    314 + (NSString *)getAppDelegateClassName
    315 {
    316     /* subclassing notice: when you subclass this appdelegate, make sure to add
    317      * a category to override this method and return the actual name of the
    318      * delegate */
    319     return @"SDLUIKitDelegate";
    320 }
    321 
    322 - (void)hideLaunchScreen
    323 {
    324     UIWindow *window = launchWindow;
    325 
    326     if (!window || window.hidden) {
    327         return;
    328     }
    329 
    330     launchWindow = nil;
    331 
    332     /* Do a nice animated fade-out (roughly matches the real launch behavior.) */
    333     [UIView animateWithDuration:0.2 animations:^{
    334         window.alpha = 0.0;
    335     } completion:^(BOOL finished) {
    336         window.hidden = YES;
    337         UIKit_ForceUpdateHomeIndicator(); /* Wait for launch screen to hide so settings are applied to the actual view controller. */
    338     }];
    339 }
    340 
    341 - (void)postFinishLaunch
    342 {
    343     /* Hide the launch screen the next time the run loop is run. SDL apps will
    344      * have a chance to load resources while the launch screen is still up. */
    345     [self performSelector:@selector(hideLaunchScreen) withObject:nil afterDelay:0.0];
    346 
    347     /* run the user's application, passing argc and argv */
    348     SDL_iPhoneSetEventPump(SDL_TRUE);
    349     exit_status = forward_main(forward_argc, forward_argv);
    350     SDL_iPhoneSetEventPump(SDL_FALSE);
    351 
    352     if (launchWindow) {
    353         launchWindow.hidden = YES;
    354         launchWindow = nil;
    355     }
    356 
    357     /* exit, passing the return status from the user's application */
    358     /* We don't actually exit to support applications that do setup in their
    359      * main function and then allow the Cocoa event loop to run. */
    360     /* exit(exit_status); */
    361 }
    362 
    363 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    364 {
    365     NSBundle *bundle = [NSBundle mainBundle];
    366 
    367 #if SDL_IPHONE_LAUNCHSCREEN
    368     /* The normal launch screen is displayed until didFinishLaunching returns,
    369      * but SDL_main is called after that happens and there may be a noticeable
    370      * delay between the start of SDL_main and when the first real frame is
    371      * displayed (e.g. if resources are loaded before SDL_GL_SwapWindow is
    372      * called), so we show the launch screen programmatically until the first
    373      * time events are pumped. */
    374     UIViewController *vc = nil;
    375     NSString *screenname = nil;
    376 
    377     /* tvOS only uses a plain launch image. */
    378 #if !TARGET_OS_TV
    379     screenname = [bundle objectForInfoDictionaryKey:@"UILaunchStoryboardName"];
    380 
    381     if (screenname && UIKit_IsSystemVersionAtLeast(8.0)) {
    382         @try {
    383             /* The launch storyboard is actually a nib in some older versions of
    384              * Xcode. We'll try to load it as a storyboard first, as it's more
    385              * modern. */
    386             UIStoryboard *storyboard = [UIStoryboard storyboardWithName:screenname bundle:bundle];
    387             vc = [storyboard instantiateInitialViewController];
    388         }
    389         @catch (NSException *exception) {
    390             /* Do nothing (there's more code to execute below). */
    391         }
    392     }
    393 #endif
    394 
    395     if (vc == nil) {
    396         vc = [[SDLLaunchScreenController alloc] initWithNibName:screenname bundle:bundle];
    397     }
    398 
    399     if (vc.view) {
    400         launchWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    401 
    402         /* We don't want the launch window immediately hidden when a real SDL
    403          * window is shown - we fade it out ourselves when we're ready. */
    404         launchWindow.windowLevel = UIWindowLevelNormal + 1.0;
    405 
    406         /* Show the window but don't make it key. Events should always go to
    407          * other windows when possible. */
    408         launchWindow.hidden = NO;
    409 
    410         launchWindow.rootViewController = vc;
    411     }
    412 #endif
    413 
    414     /* Set working directory to resource path */
    415     [[NSFileManager defaultManager] changeCurrentDirectoryPath:[bundle resourcePath]];
    416 
    417     /* register a callback for the idletimer hint */
    418     SDL_AddHintCallback(SDL_HINT_IDLE_TIMER_DISABLED,
    419                         SDL_IdleTimerDisabledChanged, NULL);
    420 
    421     SDL_SetMainReady();
    422     [self performSelector:@selector(postFinishLaunch) withObject:nil afterDelay:0.0];
    423 
    424     return YES;
    425 }
    426 
    427 - (UIWindow *)window
    428 {
    429     SDL_VideoDevice *_this = SDL_GetVideoDevice();
    430     if (_this) {
    431         SDL_Window *window = NULL;
    432         for (window = _this->windows; window != NULL; window = window->next) {
    433             SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
    434             if (data != nil) {
    435                 return data.uiwindow;
    436             }
    437         }
    438     }
    439     return nil;
    440 }
    441 
    442 - (void)setWindow:(UIWindow *)window
    443 {
    444     /* Do nothing. */
    445 }
    446 
    447 #if !TARGET_OS_TV
    448 - (void)application:(UIApplication *)application didChangeStatusBarOrientation:(UIInterfaceOrientation)oldStatusBarOrientation
    449 {
    450     SDL_OnApplicationDidChangeStatusBarOrientation();
    451 }
    452 #endif
    453 
    454 - (void)applicationWillTerminate:(UIApplication *)application
    455 {
    456     SDL_OnApplicationWillTerminate();
    457 }
    458 
    459 - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
    460 {
    461     SDL_OnApplicationDidReceiveMemoryWarning();
    462 }
    463 
    464 - (void)applicationWillResignActive:(UIApplication*)application
    465 {
    466     SDL_OnApplicationWillResignActive();
    467 }
    468 
    469 - (void)applicationDidEnterBackground:(UIApplication*)application
    470 {
    471     SDL_OnApplicationDidEnterBackground();
    472 }
    473 
    474 - (void)applicationWillEnterForeground:(UIApplication*)application
    475 {
    476     SDL_OnApplicationWillEnterForeground();
    477 }
    478 
    479 - (void)applicationDidBecomeActive:(UIApplication*)application
    480 {
    481     SDL_OnApplicationDidBecomeActive();
    482 }
    483 
    484 - (void)sendDropFileForURL:(NSURL *)url
    485 {
    486     NSURL *fileURL = url.filePathURL;
    487     if (fileURL != nil) {
    488         SDL_SendDropFile(NULL, fileURL.path.UTF8String);
    489     } else {
    490         SDL_SendDropFile(NULL, url.absoluteString.UTF8String);
    491     }
    492     SDL_SendDropComplete(NULL);
    493 }
    494 
    495 #if TARGET_OS_TV || (defined(__IPHONE_9_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_9_0)
    496 
    497 - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
    498 {
    499     /* TODO: Handle options */
    500     [self sendDropFileForURL:url];
    501     return YES;
    502 }
    503 
    504 #else
    505 
    506 - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
    507 {
    508     [self sendDropFileForURL:url];
    509     return YES;
    510 }
    511 
    512 #endif
    513 
    514 @end
    515 
    516 #endif /* SDL_VIDEO_DRIVER_UIKIT */
    517 
    518 /* vi: set ts=4 sw=4 expandtab: */