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