SDL  2.0
SDL_uikitappdelegate.m
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2016 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 
31 #import "SDL_uikitappdelegate.h"
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 
41 static int forward_argc;
42 static char **forward_argv;
43 static int exit_status;
44 
45 int main(int argc, char **argv)
46 {
47  int i;
48 
49  /* store arguments */
50  forward_argc = argc;
51  forward_argv = (char **)malloc((argc+1) * sizeof(char *));
52  for (i = 0; i < argc; i++) {
53  forward_argv[i] = malloc( (strlen(argv[i])+1) * sizeof(char));
54  strcpy(forward_argv[i], argv[i]);
55  }
56  forward_argv[i] = NULL;
57 
58  /* Give over control to run loop, SDLUIKitDelegate will handle most things from here */
59  @autoreleasepool {
60  UIApplicationMain(argc, argv, nil, [SDLUIKitDelegate getAppDelegateClassName]);
61  }
62 
63  /* free the memory we used to hold copies of argc and argv */
64  for (i = 0; i < forward_argc; i++) {
65  free(forward_argv[i]);
66  }
67  free(forward_argv);
68 
69  return exit_status;
70 }
71 
72 static void
73 SDL_IdleTimerDisabledChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
74 {
75  BOOL disable = (hint && *hint != '0');
76  [UIApplication sharedApplication].idleTimerDisabled = disable;
77 }
78 
79 /* Load a launch image using the old UILaunchImageFile-era naming rules. */
80 static UIImage *
81 SDL_LoadLaunchImageNamed(NSString *name, int screenh)
82 {
83  UIInterfaceOrientation curorient = [UIApplication sharedApplication].statusBarOrientation;
84  UIUserInterfaceIdiom idiom = [UIDevice currentDevice].userInterfaceIdiom;
85  UIImage *image = nil;
86 
87  if (idiom == UIUserInterfaceIdiomPhone && screenh == 568) {
88  /* The image name for the iPhone 5 uses its height as a suffix. */
89  image = [UIImage imageNamed:[NSString stringWithFormat:@"%@-568h", name]];
90  } else if (idiom == UIUserInterfaceIdiomPad) {
91  /* iPad apps can launch in any orientation. */
92  if (UIInterfaceOrientationIsLandscape(curorient)) {
93  if (curorient == UIInterfaceOrientationLandscapeLeft) {
94  image = [UIImage imageNamed:[NSString stringWithFormat:@"%@-LandscapeLeft", name]];
95  } else {
96  image = [UIImage imageNamed:[NSString stringWithFormat:@"%@-LandscapeRight", name]];
97  }
98  if (!image) {
99  image = [UIImage imageNamed:[NSString stringWithFormat:@"%@-Landscape", name]];
100  }
101  } else {
102  if (curorient == UIInterfaceOrientationPortraitUpsideDown) {
103  image = [UIImage imageNamed:[NSString stringWithFormat:@"%@-PortraitUpsideDown", name]];
104  }
105  if (!image) {
106  image = [UIImage imageNamed:[NSString stringWithFormat:@"%@-Portrait", name]];
107  }
108  }
109  }
110 
111  if (!image) {
112  image = [UIImage imageNamed:name];
113  }
114 
115  return image;
116 }
117 
118 @implementation SDLLaunchScreenController
119 
120 - (instancetype)init
121 {
122  if (!(self = [super initWithNibName:nil bundle:nil])) {
123  return nil;
124  }
125 
126  NSBundle *bundle = [NSBundle mainBundle];
127  NSString *screenname = [bundle objectForInfoDictionaryKey:@"UILaunchStoryboardName"];
128  BOOL atleastiOS8 = UIKit_IsSystemVersionAtLeast(8.0);
129 
130  /* Launch screens were added in iOS 8. Otherwise we use launch images. */
131  if (screenname && atleastiOS8) {
132  @try {
133  self.view = [bundle loadNibNamed:screenname owner:self options:nil][0];
134  }
135  @catch (NSException *exception) {
136  /* If a launch screen name is specified but it fails to load, iOS
137  * displays a blank screen rather than falling back to an image. */
138  return nil;
139  }
140  }
141 
142  if (!self.view) {
143  NSArray *launchimages = [bundle objectForInfoDictionaryKey:@"UILaunchImages"];
144  UIInterfaceOrientation curorient = [UIApplication sharedApplication].statusBarOrientation;
145  NSString *imagename = nil;
146  UIImage *image = nil;
147 
148  int screenw = (int)([UIScreen mainScreen].bounds.size.width + 0.5);
149  int screenh = (int)([UIScreen mainScreen].bounds.size.height + 0.5);
150 
151  /* We always want portrait-oriented size, to match UILaunchImageSize. */
152  if (screenw > screenh) {
153  int width = screenw;
154  screenw = screenh;
155  screenh = width;
156  }
157 
158  /* Xcode 5 introduced a dictionary of launch images in Info.plist. */
159  if (launchimages) {
160  for (NSDictionary *dict in launchimages) {
161  UIInterfaceOrientationMask orientmask = UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
162  NSString *minversion = dict[@"UILaunchImageMinimumOSVersion"];
163  NSString *sizestring = dict[@"UILaunchImageSize"];
164  NSString *orientstring = dict[@"UILaunchImageOrientation"];
165 
166  /* Ignore this image if the current version is too low. */
167  if (minversion && !UIKit_IsSystemVersionAtLeast(minversion.doubleValue)) {
168  continue;
169  }
170 
171  /* Ignore this image if the size doesn't match. */
172  if (sizestring) {
173  CGSize size = CGSizeFromString(sizestring);
174  if ((int)(size.width + 0.5) != screenw || (int)(size.height + 0.5) != screenh) {
175  continue;
176  }
177  }
178 
179  if (orientstring) {
180  if ([orientstring isEqualToString:@"PortraitUpsideDown"]) {
181  orientmask = UIInterfaceOrientationMaskPortraitUpsideDown;
182  } else if ([orientstring isEqualToString:@"Landscape"]) {
183  orientmask = UIInterfaceOrientationMaskLandscape;
184  } else if ([orientstring isEqualToString:@"LandscapeLeft"]) {
185  orientmask = UIInterfaceOrientationMaskLandscapeLeft;
186  } else if ([orientstring isEqualToString:@"LandscapeRight"]) {
187  orientmask = UIInterfaceOrientationMaskLandscapeRight;
188  }
189  }
190 
191  /* Ignore this image if the orientation doesn't match. */
192  if ((orientmask & (1 << curorient)) == 0) {
193  continue;
194  }
195 
196  imagename = dict[@"UILaunchImageName"];
197  }
198 
199  if (imagename) {
200  image = [UIImage imageNamed:imagename];
201  }
202  } else {
203  imagename = [bundle objectForInfoDictionaryKey:@"UILaunchImageFile"];
204 
205  if (imagename) {
206  image = SDL_LoadLaunchImageNamed(imagename, screenh);
207  }
208 
209  if (!image) {
210  image = SDL_LoadLaunchImageNamed(@"Default", screenh);
211  }
212  }
213 
214  if (image) {
215  UIImageView *view = [[UIImageView alloc] initWithFrame:[UIScreen mainScreen].bounds];
216  UIImageOrientation imageorient = UIImageOrientationUp;
217 
218  /* Bugs observed / workaround tested in iOS 8.3, 7.1, and 6.1. */
219  if (UIInterfaceOrientationIsLandscape(curorient)) {
220  if (atleastiOS8 && image.size.width < image.size.height) {
221  /* On iOS 8, portrait launch images displayed in forced-
222  * landscape mode (e.g. a standard Default.png on an iPhone
223  * when Info.plist only supports landscape orientations) need
224  * to be rotated to display in the expected orientation. */
225  if (curorient == UIInterfaceOrientationLandscapeLeft) {
226  imageorient = UIImageOrientationRight;
227  } else if (curorient == UIInterfaceOrientationLandscapeRight) {
228  imageorient = UIImageOrientationLeft;
229  }
230  } else if (!atleastiOS8 && image.size.width > image.size.height) {
231  /* On iOS 7 and below, landscape launch images displayed in
232  * landscape mode (e.g. landscape iPad launch images) need
233  * to be rotated to display in the expected orientation. */
234  if (curorient == UIInterfaceOrientationLandscapeLeft) {
235  imageorient = UIImageOrientationLeft;
236  } else if (curorient == UIInterfaceOrientationLandscapeRight) {
237  imageorient = UIImageOrientationRight;
238  }
239  }
240  }
241 
242  /* Create the properly oriented image. */
243  view.image = [[UIImage alloc] initWithCGImage:image.CGImage scale:image.scale orientation:imageorient];
244 
245  self.view = view;
246  }
247  }
248 
249  return self;
250 }
251 
252 - (void)loadView
253 {
254  /* Do nothing. */
255 }
256 
257 - (BOOL)shouldAutorotate
258 {
259  /* If YES, the launch image will be incorrectly rotated in some cases. */
260  return NO;
261 }
262 
263 - (NSUInteger)supportedInterfaceOrientations
264 {
265  /* We keep the supported orientations unrestricted to avoid the case where
266  * there are no common orientations between the ones set in Info.plist and
267  * the ones set here (it will cause an exception in that case.) */
268  return UIInterfaceOrientationMaskAll;
269 }
270 
271 @end
272 
273 @implementation SDLUIKitDelegate {
274  UIWindow *launchWindow;
275 }
276 
277 /* convenience method */
279 {
280  /* the delegate is set in UIApplicationMain(), which is guaranteed to be
281  * called before this method */
282  return [UIApplication sharedApplication].delegate;
283 }
284 
285 + (NSString *)getAppDelegateClassName
286 {
287  /* subclassing notice: when you subclass this appdelegate, make sure to add
288  * a category to override this method and return the actual name of the
289  * delegate */
290  return @"SDLUIKitDelegate";
291 }
292 
294 {
295  UIWindow *window = launchWindow;
296 
297  if (!window || window.hidden) {
298  return;
299  }
300 
301  launchWindow = nil;
302 
303  /* Do a nice animated fade-out (roughly matches the real launch behavior.) */
304  [UIView animateWithDuration:0.2 animations:^{
305  window.alpha = 0.0;
306  } completion:^(BOOL finished) {
307  window.hidden = YES;
308  }];
309 }
310 
311 - (void)postFinishLaunch
312 {
313  /* Hide the launch screen the next time the run loop is run. SDL apps will
314  * have a chance to load resources while the launch screen is still up. */
315  [self performSelector:@selector(hideLaunchScreen) withObject:nil afterDelay:0.0];
316 
317  /* run the user's application, passing argc and argv */
319  exit_status = SDL_main(forward_argc, forward_argv);
321 
322  if (launchWindow) {
323  launchWindow.hidden = YES;
324  launchWindow = nil;
325  }
326 
327  /* exit, passing the return status from the user's application */
328  /* We don't actually exit to support applications that do setup in their
329  * main function and then allow the Cocoa event loop to run. */
330  /* exit(exit_status); */
331 }
332 
333 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
334 {
335  NSBundle *bundle = [NSBundle mainBundle];
336  NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
337 
338 #if SDL_IPHONE_LAUNCHSCREEN
339  /* The normal launch screen is displayed until didFinishLaunching returns,
340  * but SDL_main is called after that happens and there may be a noticeable
341  * delay between the start of SDL_main and when the first real frame is
342  * displayed (e.g. if resources are loaded before SDL_GL_SwapWindow is
343  * called), so we show the launch screen programmatically until the first
344  * time events are pumped. */
345  UIViewController *viewcontroller = [[SDLLaunchScreenController alloc] init];
346 
347  if (viewcontroller.view) {
348  launchWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
349 
350  /* We don't want the launch window immediately hidden when a real SDL
351  * window is shown - we fade it out ourselves when we're ready. */
352  launchWindow.windowLevel = UIWindowLevelNormal + 1.0;
353 
354  /* Show the window but don't make it key. Events should always go to
355  * other windows when possible. */
356  launchWindow.hidden = NO;
357 
358  launchWindow.rootViewController = viewcontroller;
359  }
360 #endif
361 
362  /* Set working directory to resource path */
363  [[NSFileManager defaultManager] changeCurrentDirectoryPath:[bundle resourcePath]];
364 
365  /* register a callback for the idletimer hint */
367  SDL_IdleTimerDisabledChanged, NULL);
368 
370  [self performSelector:@selector(postFinishLaunch) withObject:nil afterDelay:0.0];
371 
372  return YES;
373 }
374 
375 - (void)applicationWillTerminate:(UIApplication *)application
376 {
378 }
379 
380 - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
381 {
383 }
384 
385 - (void)application:(UIApplication *)application didChangeStatusBarOrientation:(UIInterfaceOrientation)oldStatusBarOrientation
386 {
387  BOOL isLandscape = UIInterfaceOrientationIsLandscape(application.statusBarOrientation);
389 
390  if (_this && _this->num_displays > 0) {
391  SDL_DisplayMode *desktopmode = &_this->displays[0].desktop_mode;
392  SDL_DisplayMode *currentmode = &_this->displays[0].current_mode;
393 
394  /* The desktop display mode should be kept in sync with the screen
395  * orientation so that updating a window's fullscreen state to
396  * SDL_WINDOW_FULLSCREEN_DESKTOP keeps the window dimensions in the
397  * correct orientation. */
398  if (isLandscape != (desktopmode->w > desktopmode->h)) {
399  int height = desktopmode->w;
400  desktopmode->w = desktopmode->h;
401  desktopmode->h = height;
402  }
403 
404  /* Same deal with the current mode + SDL_GetCurrentDisplayMode. */
405  if (isLandscape != (currentmode->w > currentmode->h)) {
406  int height = currentmode->w;
407  currentmode->w = currentmode->h;
408  currentmode->h = height;
409  }
410  }
411 }
412 
413 - (void)applicationWillResignActive:(UIApplication*)application
414 {
416  if (_this) {
418  for (window = _this->windows; window != nil; window = window->next) {
421  }
422  }
424 }
425 
426 - (void)applicationDidEnterBackground:(UIApplication*)application
427 {
429 }
430 
431 - (void)applicationWillEnterForeground:(UIApplication*)application
432 {
434 }
435 
436 - (void)applicationDidBecomeActive:(UIApplication*)application
437 {
439 
441  if (_this) {
443  for (window = _this->windows; window != nil; window = window->next) {
446  }
447  }
448 }
449 
450 - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
451 {
452  NSURL *fileURL = url.filePathURL;
453  if (fileURL != nil) {
454  SDL_SendDropFile([fileURL.path UTF8String]);
455  } else {
456  SDL_SendDropFile([url.absoluteString UTF8String]);
457  }
458  return YES;
459 }
460 
461 @end
462 
463 #endif /* SDL_VIDEO_DRIVER_UIKIT */
464 
465 /* vi: set ts=4 sw=4 expandtab: */
SDL_Window * next
Definition: SDL_sysvideo.h:109
GLuint id
GLint GLint GLsizei width
Definition: SDL_opengl.h:1565
GLeglImageOES image
Definition: SDL_opengl.h:2141
#define SDL_SetMainReady
SDL_Window * window
SDL_EventEntry * free
Definition: SDL_events.c:81
BOOL UIKit_IsSystemVersionAtLeast(double version)
The structure that defines a display mode.
Definition: SDL_video.h:53
GLuint const GLchar * name
int SDL_SendWindowEvent(SDL_Window *window, Uint8 windowevent, int data1, int data2)
GLint GLint GLsizei GLsizei height
Definition: SDL_opengl.h:1565
GLsizeiptr size
static SDL_VideoDevice * _this
Definition: SDL_video.c:114
NSUInteger supportedInterfaceOrientations()
NSString * getAppDelegateClassName()
SDL_DisplayMode current_mode
Definition: SDL_sysvideo.h:127
SDL_VideoDisplay * displays
Definition: SDL_sysvideo.h:280
SDL_Window * windows
Definition: SDL_sysvideo.h:281
#define SDL_HINT_IDLE_TIMER_DISABLED
A variable controlling whether the idle timer is disabled on iOS.
Definition: SDL_hints.h:267
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:42
#define NULL
Definition: begin_code.h:143
SDL_DisplayMode desktop_mode
Definition: SDL_sysvideo.h:126
#define malloc
Definition: SDL_malloc.c:641
C_LINKAGE int SDL_main(int argc, char *argv[])
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
int SDL_SendDropFile(const char *file)
The type used to identify a window.
Definition: SDL_sysvideo.h:71
#define SDL_AddHintCallback
SDL_VideoDevice * SDL_GetVideoDevice(void)
Definition: SDL_video.c:573
#define main
Definition: SDL_main.h:104
int SDL_SendAppEvent(SDL_EventType eventType)
Definition: SDL_events.c:622
GLuint in
#define SDL_iPhoneSetEventPump