diff --git a/.hgignore b/.hgignore index c16de7595..029dfafc7 100644 --- a/.hgignore +++ b/.hgignore @@ -8,6 +8,9 @@ Makefile sdl-config SDL2.spec build +Build +*xcuserdata* +*xcworkspacedata* # for Xcode *.orig diff --git a/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj b/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj index 44055cae4..747f150bf 100755 --- a/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj +++ b/Xcode-iOS/SDL/SDL.xcodeproj/project.pbxproj @@ -1274,8 +1274,14 @@ FD6526640DE8FCCB002AD96B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES; COPY_PHASE_STRIP = NO; IPHONEOS_DEPLOYMENT_TARGET = 5.1.1; + GCC_WARN_MULTIPLE_DEFINITION_TYPES_FOR_SELECTOR = YES; + GCC_WARN_STRICT_SELECTOR_MATCH = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; PRODUCT_NAME = SDL2; SKIP_INSTALL = YES; }; @@ -1284,8 +1290,14 @@ FD6526650DE8FCCB002AD96B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES; COPY_PHASE_STRIP = YES; IPHONEOS_DEPLOYMENT_TARGET = 5.1.1; + GCC_WARN_MULTIPLE_DEFINITION_TYPES_FOR_SELECTOR = YES; + GCC_WARN_STRICT_SELECTOR_MATCH = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; PRODUCT_NAME = SDL2; SKIP_INSTALL = YES; }; diff --git a/include/SDL_hints.h b/include/SDL_hints.h index e0fce669f..92f31cdbc 100644 --- a/include/SDL_hints.h +++ b/include/SDL_hints.h @@ -261,8 +261,9 @@ extern "C" { #define SDL_HINT_ORIENTATIONS "SDL_IOS_ORIENTATIONS" /** - * \brief A variable controlling whether an Android built-in accelerometer should be - * listed as a joystick device, rather than listing actual joysticks only. + * \brief A variable controlling whether the Android / iOS built-in + * accelerometer should be listed as a joystick device, rather than listing + * actual joysticks only. * * This variable can be set to the following values: * "0" - List only real joysticks and accept input from them @@ -345,7 +346,7 @@ extern "C" { /** - * \brief If set to 1, then do not allow high-DPI windows. ("Retina" on Mac) + * \brief If set to 1, then do not allow high-DPI windows. ("Retina" on Mac and iOS) */ #define SDL_HINT_VIDEO_HIGHDPI_DISABLED "SDL_VIDEO_HIGHDPI_DISABLED" diff --git a/include/SDL_syswm.h b/include/SDL_syswm.h index c81cd48de..5359b5dd4 100644 --- a/include/SDL_syswm.h +++ b/include/SDL_syswm.h @@ -209,9 +209,9 @@ struct SDL_SysWMinfo struct { #if defined(__OBJC__) && __has_feature(objc_arc) - NSWindow __unsafe_unretained *window; /* The Cocoa window */ + NSWindow * __unsafe_unretained window; /* The Cocoa window */ #else - NSWindow *window; /* The Cocoa window */ + NSWindow *window; /* The Cocoa window */ #endif } cocoa; #endif @@ -219,9 +219,9 @@ struct SDL_SysWMinfo struct { #if defined(__OBJC__) && __has_feature(objc_arc) - UIWindow __unsafe_unretained *window; /* The UIKit window */ + UIWindow * __unsafe_unretained window; /* The UIKit window */ #else - UIWindow *window; /* The UIKit window */ + UIWindow *window; /* The UIKit window */ #endif } uikit; #endif diff --git a/src/file/cocoa/SDL_rwopsbundlesupport.m b/src/file/cocoa/SDL_rwopsbundlesupport.m index f6a65a99a..648aaffa4 100644 --- a/src/file/cocoa/SDL_rwopsbundlesupport.m +++ b/src/file/cocoa/SDL_rwopsbundlesupport.m @@ -41,27 +41,23 @@ FILE* SDL_OpenFPFromBundleOrFallback(const char *file, const char *mode) return fopen(file, mode); } - NSAutoreleasePool* autorelease_pool = [[NSAutoreleasePool alloc] init]; + @autoreleasepool { + NSFileManager* file_manager = [NSFileManager defaultManager]; + NSString* resource_path = [[NSBundle mainBundle] resourcePath]; + NSString* ns_string_file_component = [file_manager stringWithFileSystemRepresentation:file length:strlen(file)]; - NSFileManager* file_manager = [NSFileManager defaultManager]; - NSString* resource_path = [[NSBundle mainBundle] resourcePath]; - - NSString* ns_string_file_component = [file_manager stringWithFileSystemRepresentation:file length:strlen(file)]; - - NSString* full_path_with_file_to_try = [resource_path stringByAppendingPathComponent:ns_string_file_component]; - if([file_manager fileExistsAtPath:full_path_with_file_to_try]) { - fp = fopen([full_path_with_file_to_try fileSystemRepresentation], mode); + NSString* full_path_with_file_to_try = [resource_path stringByAppendingPathComponent:ns_string_file_component]; + if([file_manager fileExistsAtPath:full_path_with_file_to_try]) { + fp = fopen([full_path_with_file_to_try fileSystemRepresentation], mode); + } else { + fp = fopen(file, mode); + } } - else { - fp = fopen(file, mode); - } - - [autorelease_pool drain]; return fp; } -#endif /* __MACOSX__ */ +#endif /* __APPLE__ */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/filesystem/cocoa/SDL_sysfilesystem.m b/src/filesystem/cocoa/SDL_sysfilesystem.m index adcebd68b..d11739eb9 100644 --- a/src/filesystem/cocoa/SDL_sysfilesystem.m +++ b/src/filesystem/cocoa/SDL_sysfilesystem.m @@ -36,67 +36,69 @@ char * SDL_GetBasePath(void) { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSBundle *bundle = [NSBundle mainBundle]; - const char* baseType = [[[bundle infoDictionary] objectForKey:@"SDL_FILESYSTEM_BASE_DIR_TYPE"] UTF8String]; - const char *base = NULL; char *retval = NULL; - if (baseType == NULL) { - baseType = "resource"; - } - if (SDL_strcasecmp(baseType, "bundle")==0) { - base = [[bundle bundlePath] fileSystemRepresentation]; - } else if (SDL_strcasecmp(baseType, "parent")==0) { - base = [[[bundle bundlePath] stringByDeletingLastPathComponent] fileSystemRepresentation]; - } else { - /* this returns the exedir for non-bundled and the resourceDir for bundled apps */ - base = [[bundle resourcePath] fileSystemRepresentation]; - } - if (base) { - const size_t len = SDL_strlen(base) + 2; - retval = (char *) SDL_malloc(len); - if (retval == NULL) { - SDL_OutOfMemory(); + + @autoreleasepool { + const char *base = NULL; + NSBundle *bundle = [NSBundle mainBundle]; + const char* baseType = [[[bundle infoDictionary] objectForKey:@"SDL_FILESYSTEM_BASE_DIR_TYPE"] UTF8String]; + if (baseType == NULL) { + baseType = "resource"; + } + if (SDL_strcasecmp(baseType, "bundle")==0) { + base = [[bundle bundlePath] fileSystemRepresentation]; + } else if (SDL_strcasecmp(baseType, "parent")==0) { + base = [[[bundle bundlePath] stringByDeletingLastPathComponent] fileSystemRepresentation]; } else { - SDL_snprintf(retval, len, "%s/", base); + /* this returns the exedir for non-bundled and the resourceDir for bundled apps */ + base = [[bundle resourcePath] fileSystemRepresentation]; + } + + if (base) { + const size_t len = SDL_strlen(base) + 2; + retval = (char *) SDL_malloc(len); + if (retval == NULL) { + SDL_OutOfMemory(); + } else { + SDL_snprintf(retval, len, "%s/", base); + } } } - [pool release]; return retval; } char * SDL_GetPrefPath(const char *org, const char *app) { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSArray *array = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); char *retval = NULL; - if ([array count] > 0) { /* we only want the first item in the list. */ - NSString *str = [array objectAtIndex:0]; - const char *base = [str fileSystemRepresentation]; - if (base) { - const size_t len = SDL_strlen(base) + SDL_strlen(org) + SDL_strlen(app) + 4; - retval = (char *) SDL_malloc(len); - if (retval == NULL) { - SDL_OutOfMemory(); - } else { - char *ptr; - SDL_snprintf(retval, len, "%s/%s/%s/", base, org, app); - for (ptr = retval+1; *ptr; ptr++) { - if (*ptr == '/') { - *ptr = '\0'; - mkdir(retval, 0700); - *ptr = '/'; + @autoreleasepool { + NSArray *array = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); + + if ([array count] > 0) { /* we only want the first item in the list. */ + NSString *str = [array objectAtIndex:0]; + const char *base = [str fileSystemRepresentation]; + if (base) { + const size_t len = SDL_strlen(base) + SDL_strlen(org) + SDL_strlen(app) + 4; + retval = (char *) SDL_malloc(len); + if (retval == NULL) { + SDL_OutOfMemory(); + } else { + char *ptr; + SDL_snprintf(retval, len, "%s/%s/%s/", base, org, app); + for (ptr = retval+1; *ptr; ptr++) { + if (*ptr == '/') { + *ptr = '\0'; + mkdir(retval, 0700); + *ptr = '/'; + } } + mkdir(retval, 0700); } - mkdir(retval, 0700); } } } - - [pool release]; return retval; } diff --git a/src/joystick/iphoneos/SDL_sysjoystick.m b/src/joystick/iphoneos/SDL_sysjoystick.m index a4027599e..65e8ba791 100644 --- a/src/joystick/iphoneos/SDL_sysjoystick.m +++ b/src/joystick/iphoneos/SDL_sysjoystick.m @@ -23,6 +23,7 @@ /* This is the iOS implementation of the SDL joystick API */ #include "SDL_joystick.h" +#include "SDL_hints.h" #include "SDL_stdinc.h" #include "../SDL_sysjoystick.h" #include "../SDL_joystick_c.h" @@ -32,9 +33,10 @@ /* needed for SDL_IPHONE_MAX_GFORCE macro */ #import "SDL_config_iphoneos.h" -const char *accelerometerName = "iOS accelerometer"; +const char *accelerometerName = "iOS Accelerometer"; static CMMotionManager *motionManager = nil; +static int numjoysticks = 0; /* Function to scan the system for joysticks. * This function should set SDL_numjoysticks to the number of available @@ -44,12 +46,18 @@ static CMMotionManager *motionManager = nil; int SDL_SYS_JoystickInit(void) { - return (1); + const char *hint = SDL_GetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK); + if (!hint || SDL_atoi(hint)) { + /* Default behavior, accelerometer as joystick */ + numjoysticks = 1; + } + + return numjoysticks; } int SDL_SYS_NumJoysticks() { - return 1; + return numjoysticks; } void SDL_SYS_JoystickDetect() @@ -82,13 +90,15 @@ SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index) joystick->nballs = 0; joystick->nbuttons = 0; - if (motionManager == nil) { - motionManager = [[CMMotionManager alloc] init]; - } + @autoreleasepool { + if (motionManager == nil) { + motionManager = [[CMMotionManager alloc] init]; + } - /* Shorter times between updates can significantly increase CPU usage. */ - motionManager.accelerometerUpdateInterval = 0.1; - [motionManager startAccelerometerUpdates]; + /* Shorter times between updates can significantly increase CPU usage. */ + motionManager.accelerometerUpdateInterval = 0.1; + [motionManager startAccelerometerUpdates]; + } return 0; } @@ -105,11 +115,13 @@ static void SDL_SYS_AccelerometerUpdate(SDL_Joystick * joystick) const SInt16 maxsint16 = 0x7FFF; CMAcceleration accel; - if (!motionManager.accelerometerActive) { - return; - } + @autoreleasepool { + if (!motionManager.accelerometerActive) { + return; + } - accel = [[motionManager accelerometerData] acceleration]; + accel = motionManager.accelerometerData.acceleration; + } /* Convert accelerometer data from floating point to Sint16, which is what @@ -153,7 +165,9 @@ SDL_SYS_JoystickUpdate(SDL_Joystick * joystick) void SDL_SYS_JoystickClose(SDL_Joystick * joystick) { - [motionManager stopAccelerometerUpdates]; + @autoreleasepool { + [motionManager stopAccelerometerUpdates]; + } joystick->closed = 1; } @@ -161,10 +175,11 @@ SDL_SYS_JoystickClose(SDL_Joystick * joystick) void SDL_SYS_JoystickQuit(void) { - if (motionManager != nil) { - [motionManager release]; + @autoreleasepool { motionManager = nil; } + + numjoysticks = 0; } SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index ) diff --git a/src/power/uikit/SDL_syspower.m b/src/power/uikit/SDL_syspower.m index 60c42745a..4984b78c4 100644 --- a/src/power/uikit/SDL_syspower.m +++ b/src/power/uikit/SDL_syspower.m @@ -50,24 +50,24 @@ SDL_UIKit_UpdateBatteryMonitoring(void) SDL_bool SDL_GetPowerInfo_UIKit(SDL_PowerState * state, int *seconds, int *percent) { - UIDevice *uidev = [UIDevice currentDevice]; + @autoreleasepool { + UIDevice *uidev = [UIDevice currentDevice]; - if (!SDL_UIKitLastPowerInfoQuery) { - SDL_assert([uidev isBatteryMonitoringEnabled] == NO); - [uidev setBatteryMonitoringEnabled:YES]; - } + if (!SDL_UIKitLastPowerInfoQuery) { + SDL_assert(uidev.isBatteryMonitoringEnabled == NO); + uidev.batteryMonitoringEnabled = YES; + } - /* UIKit_GL_SwapWindow() (etc) will check this and disable the battery - * monitoring if the app hasn't queried it in the last X seconds. - * Apparently monitoring the battery burns battery life. :) - * Apple's docs say not to monitor the battery unless you need it. - */ - SDL_UIKitLastPowerInfoQuery = SDL_GetTicks(); + /* UIKit_GL_SwapWindow() (etc) will check this and disable the battery + * monitoring if the app hasn't queried it in the last X seconds. + * Apparently monitoring the battery burns battery life. :) + * Apple's docs say not to monitor the battery unless you need it. + */ + SDL_UIKitLastPowerInfoQuery = SDL_GetTicks(); - *seconds = -1; /* no API to estimate this in UIKit. */ + *seconds = -1; /* no API to estimate this in UIKit. */ - switch ([uidev batteryState]) - { + switch (uidev.batteryState) { case UIDeviceBatteryStateCharging: *state = SDL_POWERSTATE_CHARGING; break; @@ -84,11 +84,12 @@ SDL_GetPowerInfo_UIKit(SDL_PowerState * state, int *seconds, int *percent) default: *state = SDL_POWERSTATE_UNKNOWN; break; - } + } - const float level = [uidev batteryLevel]; - *percent = ( (level < 0.0f) ? -1 : ((int) ((level * 100) + 0.5f)) ); - return SDL_TRUE; /* always the definitive answer on iOS. */ + const float level = uidev.batteryLevel; + *percent = ( (level < 0.0f) ? -1 : ((int) ((level * 100) + 0.5f)) ); + return SDL_TRUE; /* always the definitive answer on iOS. */ + } } #endif /* SDL_POWER_UIKIT */ diff --git a/src/video/uikit/SDL_uikitappdelegate.m b/src/video/uikit/SDL_uikitappdelegate.m index a9924fbca..c4a199915 100644 --- a/src/video/uikit/SDL_uikitappdelegate.m +++ b/src/video/uikit/SDL_uikitappdelegate.m @@ -44,7 +44,6 @@ static UIWindow *launch_window; int main(int argc, char **argv) { int i; - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; /* store arguments */ forward_argc = argc; @@ -56,7 +55,9 @@ int main(int argc, char **argv) forward_argv[i] = NULL; /* Give over control to run loop, SDLUIKitDelegate will handle most things from here */ - UIApplicationMain(argc, argv, NULL, [SDLUIKitDelegate getAppDelegateClassName]); + @autoreleasepool { + UIApplicationMain(argc, argv, nil, [SDLUIKitDelegate getAppDelegateClassName]); + } /* free the memory we used to hold copies of argc and argv */ for (i = 0; i < forward_argc; i++) { @@ -64,7 +65,6 @@ int main(int argc, char **argv) } free(forward_argv); - [pool release]; return exit_status; } @@ -75,17 +75,18 @@ SDL_IdleTimerDisabledChanged(void *userdata, const char *name, const char *oldVa [UIApplication sharedApplication].idleTimerDisabled = disable; } -@interface SDL_splashviewcontroller : UIViewController { +@interface SDL_splashviewcontroller : UIViewController + +- (void)updateSplashImage:(UIInterfaceOrientation)interfaceOrientation; + +@end + +@implementation SDL_splashviewcontroller { UIImageView *splash; UIImage *splashPortrait; UIImage *splashLandscape; } -- (void)updateSplashImage:(UIInterfaceOrientation)interfaceOrientation; -@end - -@implementation SDL_splashviewcontroller - - (id)init { self = [super init]; @@ -93,26 +94,20 @@ SDL_IdleTimerDisabledChanged(void *userdata, const char *name, const char *oldVa return nil; } - self->splash = [[UIImageView alloc] init]; - [self setView:self->splash]; + splash = [[UIImageView alloc] init]; + self.view = splash; CGSize size = [UIScreen mainScreen].bounds.size; float height = SDL_max(size.width, size.height); - self->splashPortrait = [UIImage imageNamed:[NSString stringWithFormat:@"Default-%dh.png", (int)height]]; - if (!self->splashPortrait) { - self->splashPortrait = [UIImage imageNamed:@"Default.png"]; + splashPortrait = [UIImage imageNamed:[NSString stringWithFormat:@"Default-%dh.png", (int)height]]; + if (!splashPortrait) { + splashPortrait = [UIImage imageNamed:@"Default.png"]; } - self->splashLandscape = [UIImage imageNamed:@"Default-Landscape.png"]; - if (!self->splashLandscape && self->splashPortrait) { - self->splashLandscape = [[UIImage alloc] initWithCGImage: self->splashPortrait.CGImage - scale: 1.0 - orientation: UIImageOrientationRight]; - } - if (self->splashPortrait) { - [self->splashPortrait retain]; - } - if (self->splashLandscape) { - [self->splashLandscape retain]; + splashLandscape = [UIImage imageNamed:@"Default-Landscape.png"]; + if (!splashLandscape && splashPortrait) { + splashLandscape = [[UIImage alloc] initWithCGImage: splashPortrait.CGImage + scale: 1.0 + orientation: UIImageOrientationRight]; } [self updateSplashImage:[[UIApplication sharedApplication] statusBarOrientation]]; @@ -147,12 +142,12 @@ SDL_IdleTimerDisabledChanged(void *userdata, const char *name, const char *oldVa UIImage *image; if (UIInterfaceOrientationIsLandscape(interfaceOrientation)) { - image = self->splashLandscape; + image = splashLandscape; } else { - image = self->splashPortrait; + image = splashPortrait; } - if (image) - { + + if (image) { splash.image = image; } } @@ -165,7 +160,7 @@ SDL_IdleTimerDisabledChanged(void *userdata, const char *name, const char *oldVa /* convenience method */ + (id) sharedAppDelegate { - /* the delegate is set in UIApplicationMain(), which is garaunteed to be called before this method */ + /* the delegate is set in UIApplicationMain(), which is guaranteed to be called before this method */ return [[UIApplication sharedApplication] delegate]; } @@ -191,8 +186,7 @@ SDL_IdleTimerDisabledChanged(void *userdata, const char *name, const char *oldVa /* If we showed a splash image, clean it up */ if (launch_window) { - [launch_window release]; - launch_window = NULL; + launch_window = nil; } /* exit, passing the return status from the user's application */ @@ -205,12 +199,18 @@ SDL_IdleTimerDisabledChanged(void *userdata, const char *name, const char *oldVa - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { /* Keep the launch image up until we set a video mode */ - launch_window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + + /* This is disabled temporarily because the splash viewcontroller is + * interfering with rotation once a regular window is created: the view's + * orientations are incorrect and the status bar rotates without the view. + * Additionally, the splash viewcontroller doesn't load the correct launch + * images on iOS 7 and modern devices. */ + /*launch_window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; UIViewController *splashViewController = [[SDL_splashviewcontroller alloc] init]; launch_window.rootViewController = splashViewController; [launch_window addSubview:splashViewController.view]; - [launch_window makeKeyAndVisible]; + [launch_window makeKeyAndVisible];*/ /* Set working directory to resource path */ [[NSFileManager defaultManager] changeCurrentDirectoryPath: [[NSBundle mainBundle] resourcePath]]; @@ -235,6 +235,35 @@ SDL_IdleTimerDisabledChanged(void *userdata, const char *name, const char *oldVa SDL_SendAppEvent(SDL_APP_LOWMEMORY); } +- (void)application:(UIApplication *)application didChangeStatusBarOrientation:(UIInterfaceOrientation)oldStatusBarOrientation +{ + BOOL isLandscape = UIInterfaceOrientationIsLandscape(application.statusBarOrientation); + SDL_VideoDevice *_this = SDL_GetVideoDevice(); + + if (_this && _this->num_displays > 0) { + SDL_DisplayMode *desktopmode = &_this->displays[0].desktop_mode; + SDL_DisplayMode *currentmode = &_this->displays[0].current_mode; + + /* The desktop display mode should be kept in sync with the screen + * orientation so that updating a window's fullscreen state to + * SDL_WINDOW_FULLSCREEN_DESKTOP keeps the window dimensions in the + * correct orientation. + */ + if (isLandscape != (desktopmode->w > desktopmode->h)) { + int height = desktopmode->w; + desktopmode->w = desktopmode->h; + desktopmode->h = height; + } + + /* Same deal with the current mode + SDL_GetCurrentDisplayMode. */ + if (isLandscape != (currentmode->w > currentmode->h)) { + int height = currentmode->w; + currentmode->w = currentmode->h; + currentmode->h = height; + } + } +} + - (void) applicationWillResignActive:(UIApplication*)application { SDL_VideoDevice *_this = SDL_GetVideoDevice(); diff --git a/src/video/uikit/SDL_uikitevents.m b/src/video/uikit/SDL_uikitevents.m index 94fba878b..48bb1bd56 100644 --- a/src/video/uikit/SDL_uikitevents.m +++ b/src/video/uikit/SDL_uikitevents.m @@ -40,8 +40,9 @@ SDL_iPhoneSetEventPump(SDL_bool enabled) void UIKit_PumpEvents(_THIS) { - if (!UIKit_EventPumpEnabled) + if (!UIKit_EventPumpEnabled) { return; + } /* Let the run loop run for a short amount of time: long enough for touch events to get processed (which is important to get certain diff --git a/src/video/uikit/SDL_uikitmessagebox.m b/src/video/uikit/SDL_uikitmessagebox.m index c24f3ff8b..9a99db170 100644 --- a/src/video/uikit/SDL_uikitmessagebox.m +++ b/src/video/uikit/SDL_uikitmessagebox.m @@ -30,17 +30,16 @@ static SDL_bool s_showingMessageBox = SDL_FALSE; -@interface UIKit_UIAlertViewDelegate : NSObject { -@private - int *clickedButtonIndex; -} +@interface UIKit_UIAlertViewDelegate : NSObject -- (id)initWithButtonIndex:(int *)_buttonIndex; +- (id)initWithButtonIndex:(int *)buttonIndex; - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex; @end -@implementation UIKit_UIAlertViewDelegate +@implementation UIKit_UIAlertViewDelegate { + int *clickedButtonIndex; +} - (id)initWithButtonIndex:(int *)buttonIndex { @@ -48,12 +47,13 @@ static SDL_bool s_showingMessageBox = SDL_FALSE; if (self == nil) { return nil; } - self->clickedButtonIndex = buttonIndex; + + clickedButtonIndex = buttonIndex; return self; } -- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex; +- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { *clickedButtonIndex = (int)buttonIndex; } @@ -71,41 +71,39 @@ int UIKit_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid) { int clicked; - - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - UIAlertView* alert = [[UIAlertView alloc] init]; - - alert.title = [NSString stringWithUTF8String:messageboxdata->title]; - alert.message = [NSString stringWithUTF8String:messageboxdata->message]; - alert.delegate = [[UIKit_UIAlertViewDelegate alloc] initWithButtonIndex:&clicked]; - - const SDL_MessageBoxButtonData *buttons = messageboxdata->buttons; int i; - for (i = 0; i < messageboxdata->numbuttons; ++i) { - [alert addButtonWithTitle:[[NSString alloc] initWithUTF8String:buttons[i].text]]; + const SDL_MessageBoxButtonData *buttons = messageboxdata->buttons; + + @autoreleasepool { + UIAlertView* alert = [[UIAlertView alloc] init]; + UIKit_UIAlertViewDelegate *delegate = [[UIKit_UIAlertViewDelegate alloc] initWithButtonIndex:&clicked]; + + alert.title = @(messageboxdata->title); + alert.message = @(messageboxdata->message); + alert.delegate = delegate; + + for (i = 0; i < messageboxdata->numbuttons; ++i) { + [alert addButtonWithTitle:@(buttons[i].text)]; + } + + /* Set up for showing the alert */ + clicked = messageboxdata->numbuttons; + + [alert show]; + + /* Run the main event loop until the alert has finished */ + /* Note that this needs to be done on the main thread */ + s_showingMessageBox = SDL_TRUE; + while (clicked == messageboxdata->numbuttons) { + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; + } + s_showingMessageBox = SDL_FALSE; + + *buttonid = messageboxdata->buttons[clicked].buttonid; + + alert.delegate = nil; } - /* Set up for showing the alert */ - clicked = messageboxdata->numbuttons; - - [alert show]; - - /* Run the main event loop until the alert has finished */ - /* Note that this needs to be done on the main thread */ - s_showingMessageBox = SDL_TRUE; - while (clicked == messageboxdata->numbuttons) { - [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; - } - s_showingMessageBox = SDL_FALSE; - - *buttonid = messageboxdata->buttons[clicked].buttonid; - - [alert.delegate release]; - [alert release]; - - [pool release]; - return 0; } diff --git a/src/video/uikit/SDL_uikitmodes.h b/src/video/uikit/SDL_uikitmodes.h index 9f2e9dd34..65c185ff8 100644 --- a/src/video/uikit/SDL_uikitmodes.h +++ b/src/video/uikit/SDL_uikitmodes.h @@ -25,17 +25,17 @@ #include "SDL_uikitvideo.h" -typedef struct -{ - UIScreen *uiscreen; - CGFloat scale; -} SDL_DisplayData; +@interface SDL_DisplayData : NSObject -typedef struct -{ - UIScreenMode *uiscreenmode; - CGFloat scale; -} SDL_DisplayModeData; +@property (nonatomic, strong) UIScreen *uiscreen; + +@end + +@interface SDL_DisplayModeData : NSObject + +@property (nonatomic, strong) UIScreenMode *uiscreenmode; + +@end extern SDL_bool UIKit_IsDisplayLandscape(UIScreen *uiscreen); diff --git a/src/video/uikit/SDL_uikitmodes.m b/src/video/uikit/SDL_uikitmodes.m index 05e33d9c9..37792f1af 100644 --- a/src/video/uikit/SDL_uikitmodes.m +++ b/src/video/uikit/SDL_uikitmodes.m @@ -25,27 +25,36 @@ #include "SDL_assert.h" #include "SDL_uikitmodes.h" +@implementation SDL_DisplayData + +@synthesize uiscreen; + +@end + +@implementation SDL_DisplayModeData + +@synthesize uiscreenmode; + +@end + static int UIKit_AllocateDisplayModeData(SDL_DisplayMode * mode, - UIScreenMode * uiscreenmode, CGFloat scale) + UIScreenMode * uiscreenmode) { - SDL_DisplayModeData *data = NULL; + SDL_DisplayModeData *data = nil; if (uiscreenmode != nil) { /* Allocate the display mode data */ - data = (SDL_DisplayModeData *) SDL_malloc(sizeof(*data)); + data = [[SDL_DisplayModeData alloc] init]; if (!data) { return SDL_OutOfMemory(); } - data->uiscreenmode = uiscreenmode; - [data->uiscreenmode retain]; - - data->scale = scale; + data.uiscreenmode = uiscreenmode; } - mode->driverdata = data; + mode->driverdata = (void *) CFBridgingRetain(data); return 0; } @@ -54,23 +63,21 @@ static void UIKit_FreeDisplayModeData(SDL_DisplayMode * mode) { if (mode->driverdata != NULL) { - SDL_DisplayModeData *data = (SDL_DisplayModeData *)mode->driverdata; - [data->uiscreenmode release]; - SDL_free(data); + CFRelease(mode->driverdata); mode->driverdata = NULL; } } static int UIKit_AddSingleDisplayMode(SDL_VideoDisplay * display, int w, int h, - UIScreenMode * uiscreenmode, CGFloat scale) + UIScreenMode * uiscreenmode) { SDL_DisplayMode mode; SDL_zero(mode); mode.format = SDL_PIXELFORMAT_ABGR8888; mode.refresh_rate = 0; - if (UIKit_AllocateDisplayModeData(&mode, uiscreenmode, scale) < 0) { + if (UIKit_AllocateDisplayModeData(&mode, uiscreenmode) < 0) { return -1; } @@ -85,16 +92,16 @@ UIKit_AddSingleDisplayMode(SDL_VideoDisplay * display, int w, int h, } static int -UIKit_AddDisplayMode(SDL_VideoDisplay * display, int w, int h, CGFloat scale, +UIKit_AddDisplayMode(SDL_VideoDisplay * display, int w, int h, UIScreenMode * uiscreenmode, SDL_bool addRotation) { - if (UIKit_AddSingleDisplayMode(display, w, h, uiscreenmode, scale) < 0) { + if (UIKit_AddSingleDisplayMode(display, w, h, uiscreenmode) < 0) { return -1; } if (addRotation) { /* Add the rotated version */ - if (UIKit_AddSingleDisplayMode(display, h, w, uiscreenmode, scale) < 0) { + if (UIKit_AddSingleDisplayMode(display, h, w, uiscreenmode) < 0) { return -1; } } @@ -114,24 +121,16 @@ UIKit_AddDisplay(UIScreen *uiscreen) size.height = height; } - /* When dealing with UIKit all coordinates are specified in terms of - * what Apple refers to as points. [UIScreen scale] indicates the - * relationship between points and pixels. Since SDL has no notion - * of points, we must compensate in all cases where dealing with such - * units. - */ - CGFloat scale = [uiscreen scale]; - SDL_VideoDisplay display; SDL_DisplayMode mode; SDL_zero(mode); mode.format = SDL_PIXELFORMAT_ABGR8888; - mode.w = (int)(size.width * scale); - mode.h = (int)(size.height * scale); + mode.w = (int) size.width; + mode.h = (int) size.height; - UIScreenMode * uiscreenmode = [uiscreen currentMode]; + UIScreenMode *uiscreenmode = [uiscreen currentMode]; - if (UIKit_AllocateDisplayModeData(&mode, uiscreenmode, scale) < 0) { + if (UIKit_AllocateDisplayModeData(&mode, uiscreenmode) < 0) { return -1; } @@ -140,19 +139,18 @@ UIKit_AddDisplay(UIScreen *uiscreen) display.current_mode = mode; /* Allocate the display data */ - SDL_DisplayData *data = (SDL_DisplayData *) SDL_malloc(sizeof(*data)); + SDL_DisplayData *data = [[SDL_DisplayData alloc] init]; if (!data) { UIKit_FreeDisplayModeData(&display.desktop_mode); return SDL_OutOfMemory(); } - [uiscreen retain]; - data->uiscreen = uiscreen; - data->scale = scale; + data.uiscreen = uiscreen; - display.driverdata = data; + display.driverdata = (void *) CFBridgingRetain(data); SDL_AddVideoDisplay(&display); + return 0; } @@ -170,9 +168,11 @@ UIKit_IsDisplayLandscape(UIScreen *uiscreen) int UIKit_InitModes(_THIS) { - for (UIScreen *uiscreen in [UIScreen screens]) { - if (UIKit_AddDisplay(uiscreen) < 0) { - return -1; + @autoreleasepool { + for (UIScreen *uiscreen in [UIScreen screens]) { + if (UIKit_AddDisplay(uiscreen) < 0) { + return -1; + } } } @@ -182,34 +182,29 @@ UIKit_InitModes(_THIS) void UIKit_GetDisplayModes(_THIS, SDL_VideoDisplay * display) { - SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata; + @autoreleasepool { + SDL_DisplayData *data = (__bridge SDL_DisplayData *) display->driverdata; - SDL_bool isLandscape = UIKit_IsDisplayLandscape(data->uiscreen); - SDL_bool addRotation = (data->uiscreen == [UIScreen mainScreen]); + SDL_bool isLandscape = UIKit_IsDisplayLandscape(data.uiscreen); + SDL_bool addRotation = (data.uiscreen == [UIScreen mainScreen]); + CGFloat scale = data.uiscreen.scale; - for (UIScreenMode *uimode in [data->uiscreen availableModes]) { - CGSize size = [uimode size]; - int w = (int)size.width; - int h = (int)size.height; + for (UIScreenMode *uimode in [data.uiscreen availableModes]) { + /* The size of a UIScreenMode is in pixels, but we deal exclusively in + * points (except in SDL_GL_GetDrawableSize.) */ + CGSize size = [uimode size]; + int w = (int)(size.width / scale); + int h = (int)(size.height / scale); - /* Make sure the width/height are oriented correctly */ - if (isLandscape != (w > h)) { - int tmp = w; - w = h; - h = tmp; - } + /* Make sure the width/height are oriented correctly */ + if (isLandscape != (w > h)) { + int tmp = w; + w = h; + h = tmp; + } - /* Add the native screen resolution. */ - UIKit_AddDisplayMode(display, w, h, data->scale, uimode, addRotation); - - if (data->scale != 1.0f) { - /* Add the native screen resolution divided by its scale. - * This is so devices capable of e.g. 640x960 also advertise 320x480. - */ - UIKit_AddDisplayMode(display, - (int)(size.width / data->scale), - (int)(size.height / data->scale), - 1.0f, uimode, addRotation); + /* Add the native screen resolution. */ + UIKit_AddDisplayMode(display, w, h, uimode, addRotation); } } } @@ -217,19 +212,21 @@ UIKit_GetDisplayModes(_THIS, SDL_VideoDisplay * display) int UIKit_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode) { - SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata; - SDL_DisplayModeData *modedata = (SDL_DisplayModeData *)mode->driverdata; + @autoreleasepool { + SDL_DisplayData *data = (__bridge SDL_DisplayData *) display->driverdata; + SDL_DisplayModeData *modedata = (__bridge SDL_DisplayModeData *)mode->driverdata; - [data->uiscreen setCurrentMode:modedata->uiscreenmode]; + [data.uiscreen setCurrentMode:modedata.uiscreenmode]; - if (data->uiscreen == [UIScreen mainScreen]) { - if (mode->w > mode->h) { - if (!UIKit_IsDisplayLandscape(data->uiscreen)) { - [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:NO]; - } - } else if (mode->w < mode->h) { - if (UIKit_IsDisplayLandscape(data->uiscreen)) { - [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait animated:NO]; + if (data.uiscreen == [UIScreen mainScreen]) { + if (mode->w > mode->h) { + if (!UIKit_IsDisplayLandscape(data.uiscreen)) { + [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:NO]; + } + } else if (mode->w < mode->h) { + if (UIKit_IsDisplayLandscape(data.uiscreen)) { + [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait animated:NO]; + } } } } @@ -242,19 +239,21 @@ UIKit_QuitModes(_THIS) { /* Release Objective-C objects, so higher level doesn't free() them. */ int i, j; - for (i = 0; i < _this->num_displays; i++) { - SDL_VideoDisplay *display = &_this->displays[i]; + @autoreleasepool { + for (i = 0; i < _this->num_displays; i++) { + SDL_VideoDisplay *display = &_this->displays[i]; - UIKit_FreeDisplayModeData(&display->desktop_mode); - for (j = 0; j < display->num_display_modes; j++) { - SDL_DisplayMode *mode = &display->display_modes[j]; - UIKit_FreeDisplayModeData(mode); + UIKit_FreeDisplayModeData(&display->desktop_mode); + for (j = 0; j < display->num_display_modes; j++) { + SDL_DisplayMode *mode = &display->display_modes[j]; + UIKit_FreeDisplayModeData(mode); + } + + if (display->driverdata != NULL) { + CFRelease(display->driverdata); + display->driverdata = NULL; + } } - - SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata; - [data->uiscreen release]; - SDL_free(data); - display->driverdata = NULL; } } diff --git a/src/video/uikit/SDL_uikitopengles.h b/src/video/uikit/SDL_uikitopengles.h index 947678cae..f16918935 100644 --- a/src/video/uikit/SDL_uikitopengles.h +++ b/src/video/uikit/SDL_uikitopengles.h @@ -25,6 +25,8 @@ extern int UIKit_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context); +extern void UIKit_GL_GetDrawableSize(_THIS, SDL_Window * window, + int * w, int * h); extern void UIKit_GL_SwapWindow(_THIS, SDL_Window * window); extern SDL_GLContext UIKit_GL_CreateContext(_THIS, SDL_Window * window); extern void UIKit_GL_DeleteContext(_THIS, SDL_GLContext context); diff --git a/src/video/uikit/SDL_uikitopengles.m b/src/video/uikit/SDL_uikitopengles.m index 42303e3cb..50bb6a3f8 100644 --- a/src/video/uikit/SDL_uikitopengles.m +++ b/src/video/uikit/SDL_uikitopengles.m @@ -49,19 +49,37 @@ UIKit_GL_GetProcAddress(_THIS, const char *proc) /* note that SDL_GL_Delete context makes it current without passing the window */ -int UIKit_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context) +int +UIKit_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context) { - if (context) { - SDL_WindowData *data = (SDL_WindowData *)window->driverdata; - [data->view setCurrentContext]; - } - else { - [EAGLContext setCurrentContext: nil]; + @autoreleasepool { + if (context) { + SDL_WindowData *data = (__bridge SDL_WindowData *)window->driverdata; + [data.view setCurrentContext]; + } + else { + [EAGLContext setCurrentContext: nil]; + } } return 0; } +void UIKit_GL_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h) +{ + @autoreleasepool { + SDL_WindowData *data = (__bridge SDL_WindowData *)window->driverdata; + + if (w) { + *w = data.view.backingWidth; + } + if (h) { + *h = data.view.backingHeight; + } + } +} + + int UIKit_GL_LoadLibrary(_THIS, const char *path) { @@ -78,102 +96,109 @@ UIKit_GL_LoadLibrary(_THIS, const char *path) void UIKit_GL_SwapWindow(_THIS, SDL_Window * window) { + @autoreleasepool { + SDL_WindowData *data = (__bridge SDL_WindowData *)window->driverdata; + #if SDL_POWER_UIKIT - /* Check once a frame to see if we should turn off the battery monitor. */ - SDL_UIKit_UpdateBatteryMonitoring(); + /* Check once a frame to see if we should turn off the battery monitor. */ + SDL_UIKit_UpdateBatteryMonitoring(); #endif - SDL_WindowData *data = (SDL_WindowData *)window->driverdata; - - if (nil == data->view) { - return; - } - [data->view swapBuffers]; - - /* You need to pump events in order for the OS to make changes visible. - We don't pump events here because we don't want iOS application events - (low memory, terminate, etc.) to happen inside low level rendering. - */ -} - -SDL_GLContext UIKit_GL_CreateContext(_THIS, SDL_Window * window) -{ - SDL_uikitopenglview *view; - SDL_WindowData *data = (SDL_WindowData *) window->driverdata; - SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); - SDL_DisplayData *displaydata = display->driverdata; - SDL_DisplayModeData *displaymodedata = display->current_mode.driverdata; - UIWindow *uiwindow = data->uiwindow; - EAGLSharegroup *share_group = nil; - - if (_this->gl_config.share_with_current_context) { - SDL_uikitopenglview *view = (SDL_uikitopenglview *) SDL_GL_GetCurrentContext(); - share_group = [view.context sharegroup]; - } - - /* construct our view, passing in SDL's OpenGL configuration data */ - CGRect frame; - if (window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_BORDERLESS)) { - frame = [displaydata->uiscreen bounds]; - } else { - frame = [displaydata->uiscreen applicationFrame]; - } - view = [[SDL_uikitopenglview alloc] initWithFrame: frame - scale: displaymodedata->scale - retainBacking: _this->gl_config.retained_backing - rBits: _this->gl_config.red_size - gBits: _this->gl_config.green_size - bBits: _this->gl_config.blue_size - aBits: _this->gl_config.alpha_size - depthBits: _this->gl_config.depth_size - stencilBits: _this->gl_config.stencil_size - majorVersion: _this->gl_config.major_version - shareGroup: share_group]; - if (!view) { - return NULL; - } - - data->view = view; - view->viewcontroller = data->viewcontroller; - if (view->viewcontroller != nil) { - [view->viewcontroller setView:view]; - [view->viewcontroller retain]; - } - [uiwindow addSubview: view]; - - /* The view controller needs to be the root in order to control rotation on iOS 6.0 */ - if (uiwindow.rootViewController == nil) { - uiwindow.rootViewController = view->viewcontroller; - } - - if (UIKit_GL_MakeCurrent(_this, window, view) < 0) { - UIKit_GL_DeleteContext(_this, view); - return NULL; - } - - /* Make this window the current mouse focus for touch input */ - if (displaydata->uiscreen == [UIScreen mainScreen]) { - SDL_SetMouseFocus(window); - SDL_SetKeyboardFocus(window); - } - - return view; -} - -void UIKit_GL_DeleteContext(_THIS, SDL_GLContext context) -{ - /* the delegate has retained the view, this will release him */ - SDL_uikitopenglview *view = (SDL_uikitopenglview *)context; - if (view->viewcontroller) { - UIWindow *uiwindow = (UIWindow *)view.superview; - if (uiwindow.rootViewController == view->viewcontroller) { - uiwindow.rootViewController = nil; + if (data.view == nil) { + return; } - [view->viewcontroller setView:nil]; - [view->viewcontroller release]; + [data.view swapBuffers]; + + /* You need to pump events in order for the OS to make changes visible. + We don't pump events here because we don't want iOS application events + (low memory, terminate, etc.) to happen inside low level rendering. + */ + } +} + +SDL_GLContext +UIKit_GL_CreateContext(_THIS, SDL_Window * window) +{ + @autoreleasepool { + SDL_uikitopenglview *view; + SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; + UIWindow *uiwindow = data.uiwindow; + CGRect frame = UIKit_ComputeViewFrame(window, uiwindow.screen); + EAGLSharegroup *share_group = nil; + CGFloat scale = 1.0; + + if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) { + /* Set the scale to the natural scale factor of the screen - the backing + dimensions of the OpenGL view will match the pixel dimensions of the + screen rather than the dimensions in points. + */ + scale = uiwindow.screen.scale; + } + + if (_this->gl_config.share_with_current_context) { + SDL_uikitopenglview *view = (__bridge SDL_uikitopenglview *) SDL_GL_GetCurrentContext(); + share_group = [view.context sharegroup]; + } + + /* construct our view, passing in SDL's OpenGL configuration data */ + view = [[SDL_uikitopenglview alloc] initWithFrame: frame + scale: scale + retainBacking: _this->gl_config.retained_backing + rBits: _this->gl_config.red_size + gBits: _this->gl_config.green_size + bBits: _this->gl_config.blue_size + aBits: _this->gl_config.alpha_size + depthBits: _this->gl_config.depth_size + stencilBits: _this->gl_config.stencil_size + sRGB: _this->gl_config.framebuffer_srgb_capable + majorVersion: _this->gl_config.major_version + shareGroup: share_group]; + if (!view) { + return NULL; + } + + data.view = view; + view.viewcontroller = data.viewcontroller; + if (view.viewcontroller != nil) { + view.viewcontroller.view = view; + } + [uiwindow addSubview: view]; + + /* The view controller needs to be the root in order to control rotation on iOS 6.0 */ + if (uiwindow.rootViewController == nil) { + uiwindow.rootViewController = view.viewcontroller; + } + + if (UIKit_GL_MakeCurrent(_this, window, (__bridge SDL_GLContext)(view)) < 0) { + UIKit_GL_DeleteContext(_this, (SDL_GLContext) CFBridgingRetain(view)); + return NULL; + } + + /* Make this window the current mouse focus for touch input */ + if (uiwindow.screen == [UIScreen mainScreen]) { + SDL_SetMouseFocus(window); + SDL_SetKeyboardFocus(window); + } + + return (SDL_GLContext) CFBridgingRetain(view); + } +} + +void +UIKit_GL_DeleteContext(_THIS, SDL_GLContext context) +{ + @autoreleasepool { + /* the delegate has retained the view, this will release him */ + SDL_uikitopenglview *view = (SDL_uikitopenglview *)CFBridgingRelease(context); + if (view.viewcontroller) { + UIWindow *uiwindow = (UIWindow *)view.superview; + if (uiwindow.rootViewController == view.viewcontroller) { + uiwindow.rootViewController = nil; + } + view.viewcontroller.view = nil; + } + [view removeFromSuperview]; } - [view removeFromSuperview]; - [view release]; } #endif /* SDL_VIDEO_DRIVER_UIKIT */ diff --git a/src/video/uikit/SDL_uikitopenglview.h b/src/video/uikit/SDL_uikitopenglview.h index eed2e5b70..9bb8f0c7f 100644 --- a/src/video/uikit/SDL_uikitopenglview.h +++ b/src/video/uikit/SDL_uikitopenglview.h @@ -29,52 +29,35 @@ The view content is basically an EAGL surface you render your OpenGL scene into. Note that setting the view non-opaque will only work if the EAGL surface has an alpha channel. */ -@interface SDL_uikitopenglview : SDL_uikitview { +@interface SDL_uikitopenglview : SDL_uikitview -@private - /* The pixel dimensions of the backbuffer */ - GLint backingWidth; - GLint backingHeight; +- (id)initWithFrame:(CGRect)frame + scale:(CGFloat)scale + retainBacking:(BOOL)retained + rBits:(int)rBits + gBits:(int)gBits + bBits:(int)bBits + aBits:(int)aBits + depthBits:(int)depthBits + stencilBits:(int)stencilBits + sRGB:(BOOL)sRGB + majorVersion:(int)majorVersion + shareGroup:(EAGLSharegroup*)shareGroup; - EAGLContext *context; +@property (nonatomic, strong, readonly) EAGLContext *context; - /* OpenGL names for the renderbuffer and framebuffers used to render to this view */ - GLuint viewRenderbuffer, viewFramebuffer; - - /* OpenGL name for the depth buffer that is attached to viewFramebuffer, if it exists (0 if it does not exist) */ - GLuint depthRenderbuffer; - - /* format of depthRenderbuffer */ - GLenum depthBufferFormat; - - id displayLink; - int animationInterval; - void (*animationCallback)(void*); - void *animationCallbackParam; -} - -@property (nonatomic, retain, readonly) EAGLContext *context; +/* The width and height of the drawable in pixels (as opposed to points.) */ +@property (nonatomic, readonly) int backingWidth; +@property (nonatomic, readonly) int backingHeight; - (void)swapBuffers; - (void)setCurrentContext; -- (id)initWithFrame:(CGRect)frame - scale:(CGFloat)scale - retainBacking:(BOOL)retained - rBits:(int)rBits - gBits:(int)gBits - bBits:(int)bBits - aBits:(int)aBits - depthBits:(int)depthBits - stencilBits:(int)stencilBits - majorVersion:(int)majorVersion - shareGroup:(EAGLSharegroup*)shareGroup; - - (void)updateFrame; - (void)setAnimationCallback:(int)interval - callback:(void (*)(void*))callback - callbackParam:(void*)callbackParam; + callback:(void (*)(void*))callback + callbackParam:(void*)callbackParam; - (void)startAnimation; - (void)stopAnimation; diff --git a/src/video/uikit/SDL_uikitopenglview.m b/src/video/uikit/SDL_uikitopenglview.m index 144100bf7..da724dce7 100644 --- a/src/video/uikit/SDL_uikitopenglview.m +++ b/src/video/uikit/SDL_uikitopenglview.m @@ -26,42 +26,73 @@ #include #include "SDL_uikitopenglview.h" #include "SDL_uikitmessagebox.h" +#include "SDL_uikitvideo.h" -@implementation SDL_uikitopenglview +@implementation SDL_uikitopenglview { + + /* OpenGL names for the renderbuffer and framebuffers used to render to this view */ + GLuint viewRenderbuffer, viewFramebuffer; + + /* OpenGL name for the depth buffer that is attached to viewFramebuffer, if it exists (0 if it does not exist) */ + GLuint depthRenderbuffer; + + /* format of depthRenderbuffer */ + GLenum depthBufferFormat; + + id displayLink; + int animationInterval; + void (*animationCallback)(void*); + void *animationCallbackParam; + +} @synthesize context; +@synthesize backingWidth; +@synthesize backingHeight; + + (Class)layerClass { return [CAEAGLLayer class]; } - (id)initWithFrame:(CGRect)frame - scale:(CGFloat)scale + scale:(CGFloat)scale retainBacking:(BOOL)retained - rBits:(int)rBits - gBits:(int)gBits - bBits:(int)bBits - aBits:(int)aBits - depthBits:(int)depthBits - stencilBits:(int)stencilBits - majorVersion:(int)majorVersion - shareGroup:(EAGLSharegroup*)shareGroup + rBits:(int)rBits + gBits:(int)gBits + bBits:(int)bBits + aBits:(int)aBits + depthBits:(int)depthBits + stencilBits:(int)stencilBits + sRGB:(BOOL)sRGB + majorVersion:(int)majorVersion + shareGroup:(EAGLSharegroup*)shareGroup { - depthBufferFormat = 0; - if ((self = [super initWithFrame:frame])) { const BOOL useStencilBuffer = (stencilBits != 0); const BOOL useDepthBuffer = (depthBits != 0); NSString *colorFormat = nil; + self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + self.autoresizesSubviews = YES; + /* The EAGLRenderingAPI enum values currently map 1:1 to major GLES versions, and this allows us to handle future OpenGL ES versions. */ EAGLRenderingAPI api = majorVersion; - if (rBits == 8 && gBits == 8 && bBits == 8) { + context = [[EAGLContext alloc] initWithAPI:api sharegroup:shareGroup]; + if (!context || ![EAGLContext setCurrentContext:context]) { + SDL_SetError("OpenGL ES %d not supported", majorVersion); + return nil; + } + + if (sRGB && UIKit_IsSystemVersionAtLeast(@"7.0")) { + /* sRGB EAGL drawable support was added in iOS 7 */ + colorFormat = kEAGLColorFormatSRGBA8; + } else if (rBits >= 8 && gBits >= 8 && bBits >= 8) { /* if user specifically requests rbg888 or some color format higher than 16bpp */ colorFormat = kEAGLColorFormatRGBA8; } else { @@ -73,26 +104,28 @@ CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer; eaglLayer.opaque = YES; - eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithBool: retained], kEAGLDrawablePropertyRetainedBacking, colorFormat, kEAGLDrawablePropertyColorFormat, nil]; - - context = [[EAGLContext alloc] initWithAPI:api sharegroup:shareGroup]; - if (!context || ![EAGLContext setCurrentContext:context]) { - [self release]; - SDL_SetError("OpenGL ES %d not supported", majorVersion); - return nil; - } + eaglLayer.drawableProperties = @{ + kEAGLDrawablePropertyRetainedBacking: @(retained), + kEAGLDrawablePropertyColorFormat: colorFormat + }; /* Set the appropriate scale (for retina display support) */ self.contentScaleFactor = scale; - /* create the buffers */ - glGenFramebuffersOES(1, &viewFramebuffer); + /* Create the color Renderbuffer Object */ glGenRenderbuffersOES(1, &viewRenderbuffer); - - glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); - [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer]; + + if (![context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:eaglLayer]) { + SDL_SetError("Failed creating OpenGL ES drawable"); + return nil; + } + + /* Create the Framebuffer Object */ + glGenFramebuffersOES(1, &viewFramebuffer); + glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); + + /* attach the color renderbuffer to the FBO */ glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer); glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth); @@ -119,15 +152,13 @@ } if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) { - return NO; + SDL_SetError("Failed creating OpenGL ES framebuffer"); + return nil; } glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); - /* end create buffers */ - - self.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); - self.autoresizesSubviews = YES; } + return self; } @@ -164,8 +195,9 @@ animationCallback = callback; animationCallbackParam = callbackParam; - if (animationCallback) + if (animationCallback) { [self startAnimation]; + } } - (void)startAnimation @@ -206,18 +238,25 @@ - (void)layoutSubviews { + [super layoutSubviews]; + [EAGLContext setCurrentContext:context]; [self updateFrame]; } - (void)destroyFramebuffer { - glDeleteFramebuffersOES(1, &viewFramebuffer); - viewFramebuffer = 0; - glDeleteRenderbuffersOES(1, &viewRenderbuffer); - viewRenderbuffer = 0; + if (viewFramebuffer != 0) { + glDeleteFramebuffersOES(1, &viewFramebuffer); + viewFramebuffer = 0; + } - if (depthRenderbuffer) { + if (viewRenderbuffer != 0) { + glDeleteRenderbuffersOES(1, &viewRenderbuffer); + viewRenderbuffer = 0; + } + + if (depthRenderbuffer != 0) { glDeleteRenderbuffersOES(1, &depthRenderbuffer); depthRenderbuffer = 0; } @@ -226,12 +265,10 @@ - (void)dealloc { - [self destroyFramebuffer]; if ([EAGLContext currentContext] == context) { + [self destroyFramebuffer]; [EAGLContext setCurrentContext:nil]; } - [context release]; - [super dealloc]; } @end diff --git a/src/video/uikit/SDL_uikitvideo.h b/src/video/uikit/SDL_uikitvideo.h index ef6298257..dec49cd28 100644 --- a/src/video/uikit/SDL_uikitvideo.h +++ b/src/video/uikit/SDL_uikitvideo.h @@ -25,20 +25,8 @@ #include "../SDL_sysvideo.h" -#ifndef __IPHONE_6_0 -/* This enum isn't available in older SDKs, but we use it for our own purposes on iOS 5.1 and for the system on iOS 6.0 */ -enum UIInterfaceOrientationMask -{ - UIInterfaceOrientationMaskPortrait = (1 << UIInterfaceOrientationPortrait), - UIInterfaceOrientationMaskLandscapeLeft = (1 << UIInterfaceOrientationLandscapeLeft), - UIInterfaceOrientationMaskLandscapeRight = (1 << UIInterfaceOrientationLandscapeRight), - UIInterfaceOrientationMaskPortraitUpsideDown = (1 << UIInterfaceOrientationPortraitUpsideDown), - UIInterfaceOrientationMaskLandscape = (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight), - UIInterfaceOrientationMaskAll = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskPortraitUpsideDown), - UIInterfaceOrientationMaskAllButUpsideDown = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight), -}; -#endif /* !__IPHONE_6_0 */ - +BOOL UIKit_IsSystemVersionAtLeast(NSString *version); +CGRect UIKit_ComputeViewFrame(SDL_Window *window, UIScreen *screen); #endif /* _SDL_uikitvideo_h */ diff --git a/src/video/uikit/SDL_uikitvideo.m b/src/video/uikit/SDL_uikitvideo.m index 4893bc163..5d4e81252 100644 --- a/src/video/uikit/SDL_uikitvideo.m +++ b/src/video/uikit/SDL_uikitvideo.m @@ -78,12 +78,11 @@ UIKit_CreateDevice(int devindex) device->ShowWindow = UIKit_ShowWindow; device->HideWindow = UIKit_HideWindow; device->RaiseWindow = UIKit_RaiseWindow; + device->SetWindowBordered = UIKit_SetWindowBordered; device->SetWindowFullscreen = UIKit_SetWindowFullscreen; device->DestroyWindow = UIKit_DestroyWindow; device->GetWindowWMInfo = UIKit_GetWindowWMInfo; - /* !!! FIXME: implement SetWindowBordered */ - #if SDL_IPHONE_KEYBOARD device->HasScreenKeyboardSupport = UIKit_HasScreenKeyboardSupport; device->ShowScreenKeyboard = UIKit_ShowScreenKeyboard; @@ -93,12 +92,13 @@ UIKit_CreateDevice(int devindex) #endif /* OpenGL (ES) functions */ - device->GL_MakeCurrent = UIKit_GL_MakeCurrent; - device->GL_SwapWindow = UIKit_GL_SwapWindow; + device->GL_MakeCurrent = UIKit_GL_MakeCurrent; + device->GL_GetDrawableSize = UIKit_GL_GetDrawableSize; + device->GL_SwapWindow = UIKit_GL_SwapWindow; device->GL_CreateContext = UIKit_GL_CreateContext; device->GL_DeleteContext = UIKit_GL_DeleteContext; device->GL_GetProcAddress = UIKit_GL_GetProcAddress; - device->GL_LoadLibrary = UIKit_GL_LoadLibrary; + device->GL_LoadLibrary = UIKit_GL_LoadLibrary; device->free = UIKit_DeleteDevice; device->gl_config.accelerated = 1; @@ -129,6 +129,26 @@ UIKit_VideoQuit(_THIS) UIKit_QuitModes(_this); } +BOOL +UIKit_IsSystemVersionAtLeast(NSString *version) +{ + NSString *sysversion = [UIDevice currentDevice].systemVersion; + return [sysversion compare:version options:NSNumericSearch] != NSOrderedAscending; +} + +CGRect +UIKit_ComputeViewFrame(SDL_Window *window, UIScreen *screen) +{ + BOOL hasiOS7 = UIKit_IsSystemVersionAtLeast(@"7.0"); + + if (hasiOS7 || (window->flags & (SDL_WINDOW_BORDERLESS|SDL_WINDOW_FULLSCREEN))) { + /* The view should always show behind the status bar in iOS 7+. */ + return screen.bounds; + } else { + return screen.applicationFrame; + } +} + /* * iOS log support. * diff --git a/src/video/uikit/SDL_uikitview.h b/src/video/uikit/SDL_uikitview.h index ce616c07e..9824e7866 100644 --- a/src/video/uikit/SDL_uikitview.h +++ b/src/video/uikit/SDL_uikitview.h @@ -31,27 +31,13 @@ #endif #if SDL_IPHONE_KEYBOARD -@interface SDL_uikitview : UIView { +@interface SDL_uikitview : UIView #else -@interface SDL_uikitview : UIView { +@interface SDL_uikitview : UIView #endif - SDL_TouchID touchId; - UITouch *leftFingerDown; -#ifndef IPHONE_TOUCH_EFFICIENT_DANGEROUS - UITouch *finger[MAX_SIMULTANEOUS_TOUCHES]; -#endif +@property (nonatomic, weak) SDL_uikitviewcontroller *viewcontroller; -#if SDL_IPHONE_KEYBOARD - UITextField *textField; - BOOL keyboardVisible; - SDL_Rect textInputRect; - int keyboardHeight; -#endif - -@public - SDL_uikitviewcontroller *viewcontroller; -} - (CGPoint)touchLocation:(UITouch *)touch shouldNormalize:(BOOL)normalize; - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; @@ -61,9 +47,10 @@ - (void)showKeyboard; - (void)hideKeyboard; - (void)initializeKeyboard; -@property (readonly) BOOL keyboardVisible; -@property (nonatomic,assign) SDL_Rect textInputRect; -@property (nonatomic,assign) int keyboardHeight; + +@property (nonatomic, assign, getter=isKeyboardVisible) BOOL keyboardVisible; +@property (nonatomic, assign) SDL_Rect textInputRect; +@property (nonatomic, assign) int keyboardHeight; SDL_bool UIKit_HasScreenKeyboardSupport(_THIS); void UIKit_ShowScreenKeyboard(_THIS, SDL_Window *window); diff --git a/src/video/uikit/SDL_uikitview.m b/src/video/uikit/SDL_uikitview.m index c51a3ba71..c7a77de3b 100644 --- a/src/video/uikit/SDL_uikitview.m +++ b/src/video/uikit/SDL_uikitview.m @@ -35,27 +35,36 @@ #include "SDL_uikitmodes.h" #include "SDL_uikitwindow.h" -void _uikit_keyboard_init() ; +void _uikit_keyboard_init(); -@implementation SDL_uikitview +@implementation SDL_uikitview { + + SDL_TouchID touchId; + UITouch *leftFingerDown; +#ifndef IPHONE_TOUCH_EFFICIENT_DANGEROUS + UITouch *finger[MAX_SIMULTANEOUS_TOUCHES]; +#endif + +#if SDL_IPHONE_KEYBOARD + UITextField *textField; +#endif -- (void)dealloc -{ - [super dealloc]; } +@synthesize viewcontroller; + - (id)initWithFrame:(CGRect)frame { - self = [super initWithFrame: frame]; - + if (self = [super initWithFrame: frame]) { #if SDL_IPHONE_KEYBOARD - [self initializeKeyboard]; + [self initializeKeyboard]; #endif - self.multipleTouchEnabled = YES; + self.multipleTouchEnabled = YES; - touchId = 1; - SDL_AddTouch(touchId, ""); + touchId = 1; + SDL_AddTouch(touchId, ""); + } return self; @@ -65,36 +74,26 @@ void _uikit_keyboard_init() ; { CGPoint point = [touch locationInView: self]; - /* Get the display scale and apply that to the input coordinates */ - SDL_Window *window = self->viewcontroller.window; - SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); - SDL_DisplayModeData *displaymodedata = (SDL_DisplayModeData *) display->current_mode.driverdata; - if (normalize) { - CGRect bounds = [self bounds]; + CGRect bounds = self.bounds; point.x /= bounds.size.width; point.y /= bounds.size.height; - } else { - point.x *= displaymodedata->scale; - point.y *= displaymodedata->scale; } + return point; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { - NSEnumerator *enumerator = [touches objectEnumerator]; - UITouch *touch = (UITouch*)[enumerator nextObject]; - - while (touch) { + for (UITouch *touch in touches) { if (!leftFingerDown) { CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO]; /* send moved event */ - SDL_SendMouseMotion(self->viewcontroller.window, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y); + SDL_SendMouseMotion(self.viewcontroller.window, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y); /* send mouse down event */ - SDL_SendMouseButton(self->viewcontroller.window, SDL_TOUCH_MOUSEID, SDL_PRESSED, SDL_BUTTON_LEFT); + SDL_SendMouseButton(self.viewcontroller.window, SDL_TOUCH_MOUSEID, SDL_PRESSED, SDL_BUTTON_LEFT); leftFingerDown = touch; } @@ -118,25 +117,21 @@ void _uikit_keyboard_init() ; } } #endif - touch = (UITouch*)[enumerator nextObject]; } } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { - NSEnumerator *enumerator = [touches objectEnumerator]; - UITouch *touch = (UITouch*)[enumerator nextObject]; - - while(touch) { + for (UITouch *touch in touches) { if (touch == leftFingerDown) { /* send mouse up */ - SDL_SendMouseButton(self->viewcontroller.window, SDL_TOUCH_MOUSEID, SDL_RELEASED, SDL_BUTTON_LEFT); + SDL_SendMouseButton(self.viewcontroller.window, SDL_TOUCH_MOUSEID, SDL_RELEASED, SDL_BUTTON_LEFT); leftFingerDown = nil; } CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES]; #ifdef IPHONE_TOUCH_EFFICIENT_DANGEROUS - SDL_SendTouch(touchId, (long)touch, + SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch), SDL_FALSE, locationInView.x, locationInView.y, 1.0f); #else int i; @@ -149,7 +144,6 @@ void _uikit_keyboard_init() ; } } #endif - touch = (UITouch*)[enumerator nextObject]; } } @@ -165,20 +159,17 @@ void _uikit_keyboard_init() ; - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { - NSEnumerator *enumerator = [touches objectEnumerator]; - UITouch *touch = (UITouch*)[enumerator nextObject]; - - while (touch) { + for (UITouch *touch in touches) { if (touch == leftFingerDown) { CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO]; /* send moved event */ - SDL_SendMouseMotion(self->viewcontroller.window, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y); + SDL_SendMouseMotion(self.viewcontroller.window, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y); } CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES]; #ifdef IPHONE_TOUCH_EFFICIENT_DANGEROUS - SDL_SendTouchMotion(touchId, (long)touch, + SDL_SendTouchMotion(touchId, (SDL_FingerID)((size_t)touch), locationInView.x, locationInView.y, 1.0f); #else int i; @@ -190,7 +181,6 @@ void _uikit_keyboard_init() ; } } #endif - touch = (UITouch*)[enumerator nextObject]; } } @@ -199,14 +189,9 @@ void _uikit_keyboard_init() ; */ #if SDL_IPHONE_KEYBOARD -@synthesize textInputRect = textInputRect; -@synthesize keyboardHeight = keyboardHeight; - -/* Is the iPhone virtual keyboard visible onscreen? */ -- (BOOL)keyboardVisible -{ - return keyboardVisible; -} +@synthesize textInputRect; +@synthesize keyboardHeight; +@synthesize keyboardVisible; /* Set ourselves up as a UITextFieldDelegate */ - (void)initializeKeyboard @@ -229,7 +214,6 @@ void _uikit_keyboard_init() ; keyboardVisible = NO; /* add the UITextField (hidden) to our view */ [self addSubview: textField]; - [textField release]; _uikit_keyboard_init(); } @@ -318,8 +302,8 @@ static SDL_uikitview * getWindowView(SDL_Window * window) return nil; } - SDL_WindowData *data = (SDL_WindowData *)window->driverdata; - SDL_uikitview *view = data != NULL ? data->view : nil; + SDL_WindowData *data = (__bridge SDL_WindowData *)window->driverdata; + SDL_uikitview *view = data != nil ? data.view : nil; if (view == nil) { SDL_SetError("Window has no view"); @@ -335,44 +319,49 @@ SDL_bool UIKit_HasScreenKeyboardSupport(_THIS) void UIKit_ShowScreenKeyboard(_THIS, SDL_Window *window) { - SDL_uikitview *view = getWindowView(window); - if (view != nil) { - [view showKeyboard]; + @autoreleasepool { + SDL_uikitview *view = getWindowView(window); + if (view != nil) { + [view showKeyboard]; + } } } void UIKit_HideScreenKeyboard(_THIS, SDL_Window *window) { - SDL_uikitview *view = getWindowView(window); - if (view != nil) { - [view hideKeyboard]; + @autoreleasepool { + SDL_uikitview *view = getWindowView(window); + if (view != nil) { + [view hideKeyboard]; + } } } SDL_bool UIKit_IsScreenKeyboardShown(_THIS, SDL_Window *window) { - SDL_uikitview *view = getWindowView(window); - if (view == nil) { - return 0; - } + @autoreleasepool { + SDL_uikitview *view = getWindowView(window); + if (view == nil) { + return 0; + } - return view.keyboardVisible; + return view.isKeyboardVisible; + } } void _uikit_keyboard_update() { SDL_Window *window = SDL_GetFocusWindow(); if (!window) { return; } - SDL_WindowData *data = (SDL_WindowData *)window->driverdata; + SDL_WindowData *data = (__bridge SDL_WindowData *)window->driverdata; if (!data) { return; } - SDL_uikitview *view = data->view; + SDL_uikitview *view = data.view; if (!view) { return; } - + SDL_Rect r = view.textInputRect; int height = view.keyboardHeight; int offsetx = 0; int offsety = 0; - float scale = [UIScreen mainScreen].scale; if (height) { int sw,sh; SDL_GetWindowSize(window,&sw,&sh); @@ -394,18 +383,16 @@ void _uikit_keyboard_update() { offsety = -offsety; } - offsetx /= scale; - offsety /= scale; - view.frame = CGRectMake(offsetx,offsety,view.frame.size.width,view.frame.size.height); } void _uikit_keyboard_set_height(int height) { SDL_uikitview *view = getWindowView(SDL_GetFocusWindow()); if (view == nil) { - return ; + return; } - + + view.keyboardVisible = height > 0; view.keyboardHeight = height; _uikit_keyboard_update(); } @@ -418,13 +405,12 @@ void _uikit_keyboard_init() { queue:queue usingBlock:^(NSNotification *notification) { int height = 0; - CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; + CGSize keyboardSize = [[notification userInfo][UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; height = keyboardSize.height; UIInterfaceOrientation ui_orient = [[UIApplication sharedApplication] statusBarOrientation]; if (ui_orient == UIInterfaceOrientationLandscapeRight || ui_orient == UIInterfaceOrientationLandscapeLeft) { height = keyboardSize.width; } - height *= [UIScreen mainScreen].scale; _uikit_keyboard_set_height(height); } ]; @@ -444,13 +430,15 @@ UIKit_SetTextInputRect(_THIS, SDL_Rect *rect) SDL_InvalidParamError("rect"); return; } - - SDL_uikitview *view = getWindowView(SDL_GetFocusWindow()); - if (view == nil) { - return ; - } - view.textInputRect = *rect; + @autoreleasepool { + SDL_uikitview *view = getWindowView(SDL_GetFocusWindow()); + if (view == nil) { + return; + } + + view.textInputRect = *rect; + } } diff --git a/src/video/uikit/SDL_uikitviewcontroller.h b/src/video/uikit/SDL_uikitviewcontroller.h index e8d595d9b..0ca98eb40 100644 --- a/src/video/uikit/SDL_uikitviewcontroller.h +++ b/src/video/uikit/SDL_uikitviewcontroller.h @@ -23,12 +23,9 @@ #include "../SDL_sysvideo.h" -@interface SDL_uikitviewcontroller : UIViewController { -@private - SDL_Window *window; -} +@interface SDL_uikitviewcontroller : UIViewController -@property (readwrite) SDL_Window *window; +@property (nonatomic, assign) SDL_Window *window; - (id)initWithSDLWindow:(SDL_Window *)_window; - (void)loadView; @@ -36,5 +33,6 @@ - (NSUInteger)supportedInterfaceOrientations; - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orient; - (BOOL)prefersStatusBarHidden; +- (UIStatusBarStyle)preferredStatusBarStyle; @end diff --git a/src/video/uikit/SDL_uikitviewcontroller.m b/src/video/uikit/SDL_uikitviewcontroller.m index 447b80b80..0a9fbab4a 100644 --- a/src/video/uikit/SDL_uikitviewcontroller.m +++ b/src/video/uikit/SDL_uikitviewcontroller.m @@ -40,12 +40,9 @@ - (id)initWithSDLWindow:(SDL_Window *)_window { - self = [self init]; - if (self == nil) { - return nil; + if (self = [super initWithNibName:nil bundle:nil]) { + self.window = _window; } - self.window = _window; - return self; } @@ -56,31 +53,20 @@ - (void)viewDidLayoutSubviews { - if (self->window->flags & SDL_WINDOW_RESIZABLE) { - SDL_WindowData *data = self->window->driverdata; - SDL_VideoDisplay *display = SDL_GetDisplayForWindow(self->window); - SDL_DisplayModeData *displaymodedata = (SDL_DisplayModeData *) display->current_mode.driverdata; - const CGSize size = data->view.bounds.size; - int w, h; + const CGSize size = self.view.bounds.size; + int w = (int) size.width; + int h = (int) size.height; - w = (int)(size.width * displaymodedata->scale); - h = (int)(size.height * displaymodedata->scale); - - SDL_SendWindowEvent(self->window, SDL_WINDOWEVENT_RESIZED, w, h); - } + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, w, h); } - (NSUInteger)supportedInterfaceOrientations { NSUInteger orientationMask = 0; + const char *hint = SDL_GetHint(SDL_HINT_ORIENTATIONS); - const char *orientationsCString; - if ((orientationsCString = SDL_GetHint(SDL_HINT_ORIENTATIONS)) != NULL) { - BOOL rotate = NO; - NSString *orientationsNSString = [NSString stringWithCString:orientationsCString - encoding:NSUTF8StringEncoding]; - NSArray *orientations = [orientationsNSString componentsSeparatedByCharactersInSet: - [NSCharacterSet characterSetWithCharactersInString:@" "]]; + if (hint != NULL) { + NSArray *orientations = [@(hint) componentsSeparatedByString:@" "]; if ([orientations containsObject:@"LandscapeLeft"]) { orientationMask |= UIInterfaceOrientationMaskLandscapeLeft; @@ -94,14 +80,17 @@ if ([orientations containsObject:@"PortraitUpsideDown"]) { orientationMask |= UIInterfaceOrientationMaskPortraitUpsideDown; } + } - } else if (self->window->flags & SDL_WINDOW_RESIZABLE) { + if (orientationMask == 0 && (window->flags & SDL_WINDOW_RESIZABLE)) { orientationMask = UIInterfaceOrientationMaskAll; /* any orientation is okay. */ - } else { - if (self->window->w >= self->window->h) { + } + + if (orientationMask == 0) { + if (window->w >= window->h) { orientationMask |= UIInterfaceOrientationMaskLandscape; } - if (self->window->h >= self->window->w) { + if (window->h >= window->w) { orientationMask |= (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown); } } @@ -121,13 +110,19 @@ - (BOOL)prefersStatusBarHidden { - if (self->window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_BORDERLESS)) { + if (window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_BORDERLESS)) { return YES; } else { return NO; } } +- (UIStatusBarStyle)preferredStatusBarStyle +{ + /* We assume most games don't have a bright white background. */ + return UIStatusBarStyleLightContent; +} + @end #endif /* SDL_VIDEO_DRIVER_UIKIT */ diff --git a/src/video/uikit/SDL_uikitwindow.h b/src/video/uikit/SDL_uikitwindow.h index 494b028f3..17e9e1bb4 100644 --- a/src/video/uikit/SDL_uikitwindow.h +++ b/src/video/uikit/SDL_uikitwindow.h @@ -26,12 +26,11 @@ #import "SDL_uikitopenglview.h" #import "SDL_uikitviewcontroller.h" -typedef struct SDL_WindowData SDL_WindowData; - extern int UIKit_CreateWindow(_THIS, SDL_Window * window); extern void UIKit_ShowWindow(_THIS, SDL_Window * window); extern void UIKit_HideWindow(_THIS, SDL_Window * window); extern void UIKit_RaiseWindow(_THIS, SDL_Window * window); +extern void UIKit_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered); extern void UIKit_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen); extern void UIKit_DestroyWindow(_THIS, SDL_Window * window); extern SDL_bool UIKit_GetWindowWMInfo(_THIS, SDL_Window * window, @@ -39,12 +38,17 @@ extern SDL_bool UIKit_GetWindowWMInfo(_THIS, SDL_Window * window, @class UIWindow; -struct SDL_WindowData -{ - UIWindow *uiwindow; - SDL_uikitopenglview *view; - SDL_uikitviewcontroller *viewcontroller; -}; +@interface SDL_uikitwindow : UIWindow + +@end + +@interface SDL_WindowData : NSObject + +@property (nonatomic, strong) SDL_uikitwindow *uiwindow; +@property (nonatomic, strong) SDL_uikitopenglview *view; +@property (nonatomic, strong) SDL_uikitviewcontroller *viewcontroller; + +@end #endif /* _SDL_uikitwindow_h */ diff --git a/src/video/uikit/SDL_uikitwindow.m b/src/video/uikit/SDL_uikitwindow.m index 2b1567778..a250ca5c7 100644 --- a/src/video/uikit/SDL_uikitwindow.m +++ b/src/video/uikit/SDL_uikitwindow.m @@ -41,43 +41,56 @@ #include +@implementation SDL_uikitwindow + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + /* This seems to be needed on iOS 8, otherwise the window's frame is put in + * an unexpected position when the screen or device is rotated. + * FIXME: is there a better solution to that problem than this ugly hack? + */ + self.frame = self.screen.bounds; +} + +@end + +@implementation SDL_WindowData + +@synthesize uiwindow; +@synthesize view; +@synthesize viewcontroller; + +@end - -static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bool created) +static int SetupWindowData(_THIS, SDL_Window *window, SDL_uikitwindow *uiwindow, SDL_bool created) { SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); - SDL_DisplayModeData *displaymodedata = (SDL_DisplayModeData *) display->current_mode.driverdata; - SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata; - SDL_WindowData *data; + SDL_DisplayData *displaydata = (__bridge SDL_DisplayData *) display->driverdata; /* Allocate the window data */ - data = (SDL_WindowData *)SDL_malloc(sizeof(*data)); + SDL_WindowData *data = [[SDL_WindowData alloc] init]; if (!data) { return SDL_OutOfMemory(); } - data->uiwindow = uiwindow; - data->viewcontroller = nil; - data->view = nil; + + data.uiwindow = uiwindow; /* Fill in the SDL window with the window data */ { window->x = 0; window->y = 0; - CGRect bounds; - if (window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_BORDERLESS)) { - bounds = [displaydata->uiscreen bounds]; - } else { - bounds = [displaydata->uiscreen applicationFrame]; - } + CGRect frame = UIKit_ComputeViewFrame(window, displaydata.uiscreen); - /* Get frame dimensions in pixels */ - int width = (int)(bounds.size.width * displaymodedata->scale); - int height = (int)(bounds.size.height * displaymodedata->scale); + /* Get frame dimensions */ + int width = (int) frame.size.width; + int height = (int) frame.size.height; /* Make sure the width/height are oriented correctly */ - if (UIKit_IsDisplayLandscape(displaydata->uiscreen) != (width > height)) { + if (UIKit_IsDisplayLandscape(displaydata.uiscreen) != (width > height)) { int temp = width; width = height; height = temp; @@ -87,7 +100,7 @@ static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bo window->h = height; } - window->driverdata = data; + window->driverdata = (void *) CFBridgingRetain(data); /* only one window on iOS, always shown */ window->flags &= ~SDL_WINDOW_HIDDEN; @@ -96,7 +109,7 @@ static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bo * This is only set if the window is on the main screen. Other screens * just force the window to have the borderless flag. */ - if (displaydata->uiscreen == [UIScreen mainScreen]) { + if (displaydata.uiscreen == [UIScreen mainScreen]) { window->flags |= SDL_WINDOW_INPUT_FOCUS; /* always has input focus */ /* This was setup earlier for our window, and in iOS 7 is controlled by the view, not the application @@ -116,10 +129,8 @@ static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bo * device orientation changes. This will trigger resize events, if * appropriate. */ - SDL_uikitviewcontroller *controller; - controller = [SDL_uikitviewcontroller alloc]; - data->viewcontroller = [controller initWithSDLWindow:window]; - [data->viewcontroller setTitle:@"SDL App"]; /* !!! FIXME: hook up SDL_SetWindowTitle() */ + data.viewcontroller = [[SDL_uikitviewcontroller alloc] initWithSDLWindow:window]; + data.viewcontroller.title = @"SDL App"; /* !!! FIXME: hook up SDL_SetWindowTitle() */ return 0; } @@ -127,105 +138,109 @@ static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bo int UIKit_CreateWindow(_THIS, SDL_Window *window) { - SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); - SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata; - const BOOL external = ([UIScreen mainScreen] != data->uiscreen); - const CGSize origsize = [[data->uiscreen currentMode] size]; + @autoreleasepool { + SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); + SDL_DisplayData *data = (__bridge SDL_DisplayData *) display->driverdata; + const BOOL external = ([UIScreen mainScreen] != data.uiscreen); + const CGSize origsize = [[data.uiscreen currentMode] size]; - /* SDL currently puts this window at the start of display's linked list. We rely on this. */ - SDL_assert(_this->windows == window); + /* SDL currently puts this window at the start of display's linked list. We rely on this. */ + SDL_assert(_this->windows == window); - /* We currently only handle a single window per display on iOS */ - if (window->next != NULL) { - return SDL_SetError("Only one window allowed per display."); - } - - /* If monitor has a resolution of 0x0 (hasn't been explicitly set by the - * user, so it's in standby), try to force the display to a resolution - * that most closely matches the desired window size. - */ - if ((origsize.width == 0.0f) && (origsize.height == 0.0f)) { - if (display->num_display_modes == 0) { - _this->GetDisplayModes(_this, display); + /* We currently only handle a single window per display on iOS */ + if (window->next != NULL) { + return SDL_SetError("Only one window allowed per display."); } - int i; - const SDL_DisplayMode *bestmode = NULL; - for (i = display->num_display_modes; i >= 0; i--) { - const SDL_DisplayMode *mode = &display->display_modes[i]; - if ((mode->w >= window->w) && (mode->h >= window->h)) - bestmode = mode; - } - - if (bestmode) { - SDL_DisplayModeData *modedata = (SDL_DisplayModeData *)bestmode->driverdata; - [data->uiscreen setCurrentMode:modedata->uiscreenmode]; - - /* desktop_mode doesn't change here (the higher level will - * use it to set all the screens back to their defaults - * upon window destruction, SDL_Quit(), etc. - */ - display->current_mode = *bestmode; - } - } - - if (data->uiscreen == [UIScreen mainScreen]) { - if (window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_BORDERLESS)) { - [UIApplication sharedApplication].statusBarHidden = YES; - } else { - [UIApplication sharedApplication].statusBarHidden = NO; - } - } - - if (!(window->flags & SDL_WINDOW_RESIZABLE)) { - if (window->w > window->h) { - if (!UIKit_IsDisplayLandscape(data->uiscreen)) { - [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:NO]; + /* If monitor has a resolution of 0x0 (hasn't been explicitly set by the + * user, so it's in standby), try to force the display to a resolution + * that most closely matches the desired window size. + */ + if ((origsize.width == 0.0f) && (origsize.height == 0.0f)) { + if (display->num_display_modes == 0) { + _this->GetDisplayModes(_this, display); } - } else if (window->w < window->h) { - if (UIKit_IsDisplayLandscape(data->uiscreen)) { - [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait animated:NO]; + + int i; + const SDL_DisplayMode *bestmode = NULL; + for (i = display->num_display_modes; i >= 0; i--) { + const SDL_DisplayMode *mode = &display->display_modes[i]; + if ((mode->w >= window->w) && (mode->h >= window->h)) + bestmode = mode; + } + + if (bestmode) { + SDL_DisplayModeData *modedata = (__bridge SDL_DisplayModeData *)bestmode->driverdata; + [data.uiscreen setCurrentMode:modedata.uiscreenmode]; + + /* desktop_mode doesn't change here (the higher level will + * use it to set all the screens back to their defaults + * upon window destruction, SDL_Quit(), etc. + */ + display->current_mode = *bestmode; } } - } - /* ignore the size user requested, and make a fullscreen window */ - /* !!! FIXME: can we have a smaller view? */ - UIWindow *uiwindow = [UIWindow alloc]; - uiwindow = [uiwindow initWithFrame:[data->uiscreen bounds]]; + if (data.uiscreen == [UIScreen mainScreen]) { + if (window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_BORDERLESS)) { + [UIApplication sharedApplication].statusBarHidden = YES; + } else { + [UIApplication sharedApplication].statusBarHidden = NO; + } + } - /* put the window on an external display if appropriate. This implicitly - * does [uiwindow setframe:[uiscreen bounds]], so don't do it on the - * main display, where we land by default, as that would eat the - * status bar real estate. - */ - if (external) { - [uiwindow setScreen:data->uiscreen]; - } + if (!(window->flags & SDL_WINDOW_RESIZABLE)) { + if (window->w > window->h) { + if (!UIKit_IsDisplayLandscape(data.uiscreen)) { + [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:NO]; + } + } else if (window->w < window->h) { + if (UIKit_IsDisplayLandscape(data.uiscreen)) { + [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait animated:NO]; + } + } + } + + /* ignore the size user requested, and make a fullscreen window */ + /* !!! FIXME: can we have a smaller view? */ + SDL_uikitwindow *uiwindow = [[SDL_uikitwindow alloc] initWithFrame:data.uiscreen.bounds]; + + /* put the window on an external display if appropriate. This implicitly + * does [uiwindow setframe:[uiscreen bounds]], so don't do it on the + * main display, where we land by default, as that would eat the + * status bar real estate. + */ + if (external) { + [uiwindow setScreen:data.uiscreen]; + } + + if (SetupWindowData(_this, window, uiwindow, SDL_TRUE) < 0) { + return -1; + } - if (SetupWindowData(_this, window, uiwindow, SDL_TRUE) < 0) { - [uiwindow release]; - return -1; } return 1; - } void UIKit_ShowWindow(_THIS, SDL_Window * window) { - UIWindow *uiwindow = ((SDL_WindowData *) window->driverdata)->uiwindow; + @autoreleasepool { + UIWindow *uiwindow = ((__bridge SDL_WindowData *) window->driverdata).uiwindow; - [uiwindow makeKeyAndVisible]; + [uiwindow makeKeyAndVisible]; + } } void UIKit_HideWindow(_THIS, SDL_Window * window) { - UIWindow *uiwindow = ((SDL_WindowData *) window->driverdata)->uiwindow; + @autoreleasepool { + UIWindow *uiwindow = ((__bridge SDL_WindowData *) window->driverdata).uiwindow; - uiwindow.hidden = YES; + uiwindow.hidden = YES; + } } void @@ -239,91 +254,106 @@ UIKit_RaiseWindow(_THIS, SDL_Window * window) _this->GL_MakeCurrent(_this, _this->current_glwin, _this->current_glctx); } +static void +UIKit_UpdateWindowBorder(_THIS, SDL_Window * window) +{ + SDL_WindowData *windowdata = (__bridge SDL_WindowData *) window->driverdata; + SDL_uikitviewcontroller *viewcontroller = windowdata.viewcontroller; + CGRect frame; + + if (windowdata.uiwindow.screen == [UIScreen mainScreen]) { + if (window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS)) { + [UIApplication sharedApplication].statusBarHidden = YES; + } else { + [UIApplication sharedApplication].statusBarHidden = NO; + } + + /* iOS 7+ won't update the status bar until we tell it to. */ + if ([viewcontroller respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) { + [viewcontroller setNeedsStatusBarAppearanceUpdate]; + } + } + + /* Update the view's frame to account for the status bar change. */ + frame = UIKit_ComputeViewFrame(window, windowdata.uiwindow.screen); + + windowdata.view.frame = frame; + [windowdata.view setNeedsLayout]; + [windowdata.view layoutIfNeeded]; + + /* Get frame dimensions */ + int width = (int) frame.size.width; + int height = (int) frame.size.height; + + /* We can pick either width or height here and we'll rotate the + screen to match, so we pick the closest to what we wanted. + */ + if (window->w >= window->h) { + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, SDL_max(width, height), SDL_min(width, height)); + } else { + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, SDL_min(width, height), SDL_max(width, height)); + } +} + +void +UIKit_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered) +{ + @autoreleasepool { + UIKit_UpdateWindowBorder(_this, window); + } +} + void UIKit_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen) { - SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata; - SDL_DisplayModeData *displaymodedata = (SDL_DisplayModeData *) display->current_mode.driverdata; - UIWindow *uiwindow = ((SDL_WindowData *) window->driverdata)->uiwindow; - - if (fullscreen) { - [UIApplication sharedApplication].statusBarHidden = YES; - } else { - [UIApplication sharedApplication].statusBarHidden = NO; - } - - CGRect bounds; - if (fullscreen) { - bounds = [displaydata->uiscreen bounds]; - } else { - bounds = [displaydata->uiscreen applicationFrame]; - } - - /* Get frame dimensions in pixels */ - int width = (int)(bounds.size.width * displaymodedata->scale); - int height = (int)(bounds.size.height * displaymodedata->scale); - - /* We can pick either width or height here and we'll rotate the - screen to match, so we pick the closest to what we wanted. - */ - if (window->w >= window->h) { - if (width > height) { - window->w = width; - window->h = height; - } else { - window->w = height; - window->h = width; - } - } else { - if (width > height) { - window->w = height; - window->h = width; - } else { - window->w = width; - window->h = height; - } + @autoreleasepool { + UIKit_UpdateWindowBorder(_this, window); } } void UIKit_DestroyWindow(_THIS, SDL_Window * window) { - SDL_WindowData *data = (SDL_WindowData *)window->driverdata; - - if (data) { - [data->viewcontroller release]; - [data->uiwindow release]; - SDL_free(data); + @autoreleasepool { + if (window->driverdata != NULL) { + CFRelease(window->driverdata); + } } + window->driverdata = NULL; } SDL_bool UIKit_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info) { - UIWindow *uiwindow = ((SDL_WindowData *) window->driverdata)->uiwindow; + @autoreleasepool { + UIWindow *uiwindow = ((__bridge SDL_WindowData *) window->driverdata).uiwindow; - if (info->version.major <= SDL_MAJOR_VERSION) { - info->subsystem = SDL_SYSWM_UIKIT; - info->info.uikit.window = uiwindow; - return SDL_TRUE; - } else { - SDL_SetError("Application not compiled with SDL %d.%d\n", - SDL_MAJOR_VERSION, SDL_MINOR_VERSION); - return SDL_FALSE; + if (info->version.major <= SDL_MAJOR_VERSION) { + info->subsystem = SDL_SYSWM_UIKIT; + info->info.uikit.window = uiwindow; + return SDL_TRUE; + } else { + SDL_SetError("Application not compiled with SDL %d.%d\n", + SDL_MAJOR_VERSION, SDL_MINOR_VERSION); + return SDL_FALSE; + } } } int SDL_iPhoneSetAnimationCallback(SDL_Window * window, int interval, void (*callback)(void*), void *callbackParam) { - SDL_WindowData *data = window ? (SDL_WindowData *)window->driverdata : NULL; + @autoreleasepool { + SDL_WindowData *data = window ? (__bridge SDL_WindowData *)window->driverdata : nil; - if (!data || !data->view) { - return SDL_SetError("Invalid window or view not set"); + if (!data || !data.view) { + return SDL_SetError("Invalid window or view not set"); + } + + [data.view setAnimationCallback:interval callback:callback callbackParam:callbackParam]; } - [data->view setAnimationCallback:interval callback:callback callbackParam:callbackParam]; return 0; }