Merged default into iOS-improvements

This commit is contained in:
Alex Szpakowski 2015-04-06 15:26:37 -03:00
commit df98b11c47
32 changed files with 1644 additions and 1350 deletions

View File

@ -8,6 +8,9 @@ Makefile
sdl-config sdl-config
SDL2.spec SDL2.spec
build build
Build
*xcuserdata*
*xcworkspacedata*
# for Xcode # for Xcode
*.orig *.orig

View File

@ -387,6 +387,9 @@ main(int argc, char *argv[])
SDL_GL_SetAttribute(SDL_GL_RETAINED_BACKING, 0); SDL_GL_SetAttribute(SDL_GL_RETAINED_BACKING, 0);
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
/* create main window and renderer */ /* create main window and renderer */
window = SDL_CreateWindow(NULL, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, window = SDL_CreateWindow(NULL, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT,
SDL_WINDOW_OPENGL | SDL_WINDOW_OPENGL |

View File

@ -1274,8 +1274,14 @@
FD6526640DE8FCCB002AD96B /* Debug */ = { FD6526640DE8FCCB002AD96B /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
IPHONEOS_DEPLOYMENT_TARGET = 5.1.1; 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; PRODUCT_NAME = SDL2;
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
}; };
@ -1284,8 +1290,14 @@
FD6526650DE8FCCB002AD96B /* Release */ = { FD6526650DE8FCCB002AD96B /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES;
COPY_PHASE_STRIP = YES; COPY_PHASE_STRIP = YES;
IPHONEOS_DEPLOYMENT_TARGET = 5.1.1; 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; PRODUCT_NAME = SDL2;
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
}; };

View File

@ -145,6 +145,9 @@
/* enable iPhone keyboard support */ /* enable iPhone keyboard support */
#define SDL_IPHONE_KEYBOARD 1 #define SDL_IPHONE_KEYBOARD 1
/* enable iOS extended launch screen */
#define SDL_IPHONE_LAUNCHSCREEN 1
/* enable joystick subsystem */ /* enable joystick subsystem */
#define SDL_JOYSTICK_DISABLED 0 #define SDL_JOYSTICK_DISABLED 0

View File

@ -261,8 +261,9 @@ extern "C" {
#define SDL_HINT_ORIENTATIONS "SDL_IOS_ORIENTATIONS" #define SDL_HINT_ORIENTATIONS "SDL_IOS_ORIENTATIONS"
/** /**
* \brief A variable controlling whether an Android built-in accelerometer should be * \brief A variable controlling whether the Android / iOS built-in
* listed as a joystick device, rather than listing actual joysticks only. * accelerometer should be listed as a joystick device, rather than listing
* actual joysticks only.
* *
* This variable can be set to the following values: * This variable can be set to the following values:
* "0" - List only real joysticks and accept input from them * "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" #define SDL_HINT_VIDEO_HIGHDPI_DISABLED "SDL_VIDEO_HIGHDPI_DISABLED"

View File

@ -76,6 +76,20 @@ extern DECLSPEC SDL_bool SDLCALL SDL_DXGIGetOutputInfo( int displayIndex, int *a
extern DECLSPEC int SDLCALL SDL_iPhoneSetAnimationCallback(SDL_Window * window, int interval, void (*callback)(void*), void *callbackParam); extern DECLSPEC int SDLCALL SDL_iPhoneSetAnimationCallback(SDL_Window * window, int interval, void (*callback)(void*), void *callbackParam);
extern DECLSPEC void SDLCALL SDL_iPhoneSetEventPump(SDL_bool enabled); extern DECLSPEC void SDLCALL SDL_iPhoneSetEventPump(SDL_bool enabled);
/**
\brief Returns the OpenGL Renderbuffer Object associated with the window's main view.
The Renderbuffer must be bound when calling SDL_GL_SwapWindow.
*/
extern DECLSPEC Uint32 SDLCALL SDL_iPhoneGetViewRenderbuffer(SDL_Window * window);
/**
\brief Returns the OpenGL Framebuffer Object associated with the window's main view.
The Framebuffer must be bound when rendering to the screen.
*/
extern DECLSPEC Uint32 SDLCALL SDL_iPhoneGetViewFramebuffer(SDL_Window * window);
#endif /* __IPHONEOS__ */ #endif /* __IPHONEOS__ */

View File

@ -126,6 +126,9 @@
#ifndef SDL_IPHONE_KEYBOARD #ifndef SDL_IPHONE_KEYBOARD
#define SDL_IPHONE_KEYBOARD 1 #define SDL_IPHONE_KEYBOARD 1
#endif #endif
#ifndef SDL_IPHONE_LAUNCHSCREEN
#define SDL_IPHONE_LAUNCHSCREEN 1
#endif
#ifndef SDL_POWER_UIKIT #ifndef SDL_POWER_UIKIT
#define SDL_POWER_UIKIT 1 #define SDL_POWER_UIKIT 1
#endif #endif

View File

@ -50,14 +50,13 @@ FILE* SDL_OpenFPFromBundleOrFallback(const char *file, const char *mode)
NSString* full_path_with_file_to_try = [resource_path stringByAppendingPathComponent:ns_string_file_component]; NSString* full_path_with_file_to_try = [resource_path stringByAppendingPathComponent:ns_string_file_component];
if([file_manager fileExistsAtPath:full_path_with_file_to_try]) { if([file_manager fileExistsAtPath:full_path_with_file_to_try]) {
fp = fopen([full_path_with_file_to_try fileSystemRepresentation], mode); fp = fopen([full_path_with_file_to_try fileSystemRepresentation], mode);
} } else {
else {
fp = fopen(file, mode); fp = fopen(file, mode);
} }
return fp; return fp;
}} }}
#endif /* __MACOSX__ */ #endif /* __APPLE__ */
/* vi: set ts=4 sw=4 expandtab: */ /* vi: set ts=4 sw=4 expandtab: */

View File

@ -41,6 +41,7 @@ SDL_GetBasePath(void)
const char* baseType = [[[bundle infoDictionary] objectForKey:@"SDL_FILESYSTEM_BASE_DIR_TYPE"] UTF8String]; const char* baseType = [[[bundle infoDictionary] objectForKey:@"SDL_FILESYSTEM_BASE_DIR_TYPE"] UTF8String];
const char *base = NULL; const char *base = NULL;
char *retval = NULL; char *retval = NULL;
if (baseType == NULL) { if (baseType == NULL) {
baseType = "resource"; baseType = "resource";
} }
@ -52,6 +53,7 @@ SDL_GetBasePath(void)
/* this returns the exedir for non-bundled and the resourceDir for bundled apps */ /* this returns the exedir for non-bundled and the resourceDir for bundled apps */
base = [[bundle resourcePath] fileSystemRepresentation]; base = [[bundle resourcePath] fileSystemRepresentation];
} }
if (base) { if (base) {
const size_t len = SDL_strlen(base) + 2; const size_t len = SDL_strlen(base) + 2;
retval = (char *) SDL_malloc(len); retval = (char *) SDL_malloc(len);
@ -69,9 +71,10 @@ char *
SDL_GetPrefPath(const char *org, const char *app) SDL_GetPrefPath(const char *org, const char *app)
{ @autoreleasepool { @autoreleasepool
{ {
NSArray *array = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
char *retval = NULL; char *retval = NULL;
NSArray *array = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
if ([array count] > 0) { /* we only want the first item in the list. */ if ([array count] > 0) { /* we only want the first item in the list. */
NSString *str = [array objectAtIndex:0]; NSString *str = [array objectAtIndex:0];
const char *base = [str fileSystemRepresentation]; const char *base = [str fileSystemRepresentation];

View File

@ -23,6 +23,7 @@
/* This is the iOS implementation of the SDL joystick API */ /* This is the iOS implementation of the SDL joystick API */
#include "SDL_joystick.h" #include "SDL_joystick.h"
#include "SDL_hints.h"
#include "SDL_stdinc.h" #include "SDL_stdinc.h"
#include "../SDL_sysjoystick.h" #include "../SDL_sysjoystick.h"
#include "../SDL_joystick_c.h" #include "../SDL_joystick_c.h"
@ -32,9 +33,10 @@
/* needed for SDL_IPHONE_MAX_GFORCE macro */ /* needed for SDL_IPHONE_MAX_GFORCE macro */
#import "SDL_config_iphoneos.h" #import "SDL_config_iphoneos.h"
const char *accelerometerName = "iOS accelerometer"; const char *accelerometerName = "iOS Accelerometer";
static CMMotionManager *motionManager = nil; static CMMotionManager *motionManager = nil;
static int numjoysticks = 0;
/* Function to scan the system for joysticks. /* Function to scan the system for joysticks.
* Joystick 0 should be the system default joystick. * Joystick 0 should be the system default joystick.
@ -43,12 +45,18 @@ static CMMotionManager *motionManager = nil;
int int
SDL_SYS_JoystickInit(void) 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() int SDL_SYS_NumJoysticks()
{ {
return 1; return numjoysticks;
} }
void SDL_SYS_JoystickDetect() void SDL_SYS_JoystickDetect()
@ -81,13 +89,15 @@ SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
joystick->nballs = 0; joystick->nballs = 0;
joystick->nbuttons = 0; joystick->nbuttons = 0;
if (motionManager == nil) { @autoreleasepool {
motionManager = [[CMMotionManager alloc] init]; if (motionManager == nil) {
} motionManager = [[CMMotionManager alloc] init];
}
/* Shorter times between updates can significantly increase CPU usage. */ /* Shorter times between updates can significantly increase CPU usage. */
motionManager.accelerometerUpdateInterval = 0.1; motionManager.accelerometerUpdateInterval = 0.1;
[motionManager startAccelerometerUpdates]; [motionManager startAccelerometerUpdates];
}
return 0; return 0;
} }
@ -104,11 +114,13 @@ static void SDL_SYS_AccelerometerUpdate(SDL_Joystick * joystick)
const SInt16 maxsint16 = 0x7FFF; const SInt16 maxsint16 = 0x7FFF;
CMAcceleration accel; CMAcceleration accel;
if (!motionManager.accelerometerActive) { @autoreleasepool {
return; if (!motionManager.accelerometerActive) {
} return;
}
accel = [[motionManager accelerometerData] acceleration]; accel = motionManager.accelerometerData.acceleration;
}
/* /*
Convert accelerometer data from floating point to Sint16, which is what Convert accelerometer data from floating point to Sint16, which is what
@ -152,17 +164,20 @@ SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
void void
SDL_SYS_JoystickClose(SDL_Joystick * joystick) SDL_SYS_JoystickClose(SDL_Joystick * joystick)
{ {
[motionManager stopAccelerometerUpdates]; @autoreleasepool {
[motionManager stopAccelerometerUpdates];
}
} }
/* Function to perform any system-specific joystick related cleanup */ /* Function to perform any system-specific joystick related cleanup */
void void
SDL_SYS_JoystickQuit(void) SDL_SYS_JoystickQuit(void)
{ {
if (motionManager != nil) { @autoreleasepool {
[motionManager release];
motionManager = nil; motionManager = nil;
} }
numjoysticks = 0;
} }
SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index ) SDL_JoystickGUID SDL_SYS_JoystickGetDeviceGUID( int device_index )

View File

@ -50,24 +50,24 @@ SDL_UIKit_UpdateBatteryMonitoring(void)
SDL_bool SDL_bool
SDL_GetPowerInfo_UIKit(SDL_PowerState * state, int *seconds, int *percent) SDL_GetPowerInfo_UIKit(SDL_PowerState * state, int *seconds, int *percent)
{ {
UIDevice *uidev = [UIDevice currentDevice]; @autoreleasepool {
UIDevice *uidev = [UIDevice currentDevice];
if (!SDL_UIKitLastPowerInfoQuery) { if (!SDL_UIKitLastPowerInfoQuery) {
SDL_assert([uidev isBatteryMonitoringEnabled] == NO); SDL_assert(uidev.isBatteryMonitoringEnabled == NO);
[uidev setBatteryMonitoringEnabled:YES]; uidev.batteryMonitoringEnabled = YES;
} }
/* UIKit_GL_SwapWindow() (etc) will check this and disable the battery /* UIKit_GL_SwapWindow() (etc) will check this and disable the battery
* monitoring if the app hasn't queried it in the last X seconds. * monitoring if the app hasn't queried it in the last X seconds.
* Apparently monitoring the battery burns battery life. :) * Apparently monitoring the battery burns battery life. :)
* Apple's docs say not to monitor the battery unless you need it. * Apple's docs say not to monitor the battery unless you need it.
*/ */
SDL_UIKitLastPowerInfoQuery = SDL_GetTicks(); 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: case UIDeviceBatteryStateCharging:
*state = SDL_POWERSTATE_CHARGING; *state = SDL_POWERSTATE_CHARGING;
break; break;
@ -84,11 +84,12 @@ SDL_GetPowerInfo_UIKit(SDL_PowerState * state, int *seconds, int *percent)
default: default:
*state = SDL_POWERSTATE_UNKNOWN; *state = SDL_POWERSTATE_UNKNOWN;
break; break;
} }
const float level = [uidev batteryLevel]; const float level = uidev.batteryLevel;
*percent = ( (level < 0.0f) ? -1 : ((int) ((level * 100) + 0.5f)) ); *percent = ( (level < 0.0f) ? -1 : ((int) ((level * 100) + 0.5f)) );
return SDL_TRUE; /* always the definitive answer on iOS. */ return SDL_TRUE; /* always the definitive answer on iOS. */
}
} }
#endif /* SDL_POWER_UIKIT */ #endif /* SDL_POWER_UIKIT */

View File

@ -55,6 +55,7 @@ static const float inv255f = 1.0f / 255.0f;
static SDL_Renderer *GLES_CreateRenderer(SDL_Window * window, Uint32 flags); static SDL_Renderer *GLES_CreateRenderer(SDL_Window * window, Uint32 flags);
static void GLES_WindowEvent(SDL_Renderer * renderer, static void GLES_WindowEvent(SDL_Renderer * renderer,
const SDL_WindowEvent *event); const SDL_WindowEvent *event);
static int GLES_GetOutputSize(SDL_Renderer * renderer, int *w, int *h);
static int GLES_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture); static int GLES_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture);
static int GLES_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, static int GLES_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * rect, const void *pixels, const SDL_Rect * rect, const void *pixels,
@ -321,6 +322,7 @@ GLES_CreateRenderer(SDL_Window * window, Uint32 flags)
} }
renderer->WindowEvent = GLES_WindowEvent; renderer->WindowEvent = GLES_WindowEvent;
renderer->GetOutputSize = GLES_GetOutputSize;
renderer->CreateTexture = GLES_CreateTexture; renderer->CreateTexture = GLES_CreateTexture;
renderer->UpdateTexture = GLES_UpdateTexture; renderer->UpdateTexture = GLES_UpdateTexture;
renderer->LockTexture = GLES_LockTexture; renderer->LockTexture = GLES_LockTexture;
@ -438,6 +440,13 @@ GLES_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
} }
} }
static int
GLES_GetOutputSize(SDL_Renderer * renderer, int *w, int *h)
{
SDL_GL_GetDrawableSize(renderer->window, w, h);
return 0;
}
static SDL_INLINE int static SDL_INLINE int
power_of_2(int input) power_of_2(int input)
{ {

View File

@ -369,6 +369,13 @@ GLES2_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
} }
} }
static int
GLES2_GetOutputSize(SDL_Renderer * renderer, int *w, int *h)
{
SDL_GL_GetDrawableSize(renderer->window, w, h);
return 0;
}
static int static int
GLES2_UpdateViewport(SDL_Renderer * renderer) GLES2_UpdateViewport(SDL_Renderer * renderer)
{ {
@ -2059,6 +2066,7 @@ GLES2_CreateRenderer(SDL_Window *window, Uint32 flags)
/* Populate the function pointers for the module */ /* Populate the function pointers for the module */
renderer->WindowEvent = &GLES2_WindowEvent; renderer->WindowEvent = &GLES2_WindowEvent;
renderer->GetOutputSize = &GLES2_GetOutputSize;
renderer->CreateTexture = &GLES2_CreateTexture; renderer->CreateTexture = &GLES2_CreateTexture;
renderer->UpdateTexture = &GLES2_UpdateTexture; renderer->UpdateTexture = &GLES2_UpdateTexture;
renderer->UpdateTextureYUV = &GLES2_UpdateTextureYUV; renderer->UpdateTextureYUV = &GLES2_UpdateTextureYUV;

View File

@ -1107,22 +1107,22 @@ SDL_RestoreMousePosition(SDL_Window *window)
} }
} }
static void static int
SDL_UpdateFullscreenMode(SDL_Window * window, SDL_bool fullscreen) SDL_UpdateFullscreenMode(SDL_Window * window, SDL_bool fullscreen)
{ {
SDL_VideoDisplay *display; SDL_VideoDisplay *display;
SDL_Window *other; SDL_Window *other;
CHECK_WINDOW_MAGIC(window,); CHECK_WINDOW_MAGIC(window,-1);
/* if we are in the process of hiding don't go back to fullscreen */ /* if we are in the process of hiding don't go back to fullscreen */
if ( window->is_hiding && fullscreen ) if ( window->is_hiding && fullscreen )
return; return 0;
#ifdef __MACOSX__ #ifdef __MACOSX__
if (Cocoa_SetWindowFullscreenSpace(window, fullscreen)) { if (Cocoa_SetWindowFullscreenSpace(window, fullscreen)) {
window->last_fullscreen_flags = window->flags; window->last_fullscreen_flags = window->flags;
return; return 0;
} }
#endif #endif
@ -1139,7 +1139,7 @@ SDL_UpdateFullscreenMode(SDL_Window * window, SDL_bool fullscreen)
/* See if anything needs to be done now */ /* See if anything needs to be done now */
if ((display->fullscreen_window == window) == fullscreen) { if ((display->fullscreen_window == window) == fullscreen) {
if ((window->last_fullscreen_flags & FULLSCREEN_MASK) == (window->flags & FULLSCREEN_MASK)) { if ((window->last_fullscreen_flags & FULLSCREEN_MASK) == (window->flags & FULLSCREEN_MASK)) {
return; return 0;
} }
} }
@ -1168,9 +1168,13 @@ SDL_UpdateFullscreenMode(SDL_Window * window, SDL_bool fullscreen)
/* only do the mode change if we want exclusive fullscreen */ /* only do the mode change if we want exclusive fullscreen */
if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP) { if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP) {
SDL_SetDisplayModeForDisplay(display, &fullscreen_mode); if (SDL_SetDisplayModeForDisplay(display, &fullscreen_mode) < 0) {
return -1;
}
} else { } else {
SDL_SetDisplayModeForDisplay(display, NULL); if (SDL_SetDisplayModeForDisplay(display, NULL) < 0) {
return -1;
}
} }
if (_this->SetWindowFullscreen) { if (_this->SetWindowFullscreen) {
@ -1189,7 +1193,7 @@ SDL_UpdateFullscreenMode(SDL_Window * window, SDL_bool fullscreen)
SDL_RestoreMousePosition(other); SDL_RestoreMousePosition(other);
window->last_fullscreen_flags = window->flags; window->last_fullscreen_flags = window->flags;
return; return 0;
} }
} }
} }
@ -1209,6 +1213,7 @@ SDL_UpdateFullscreenMode(SDL_Window * window, SDL_bool fullscreen)
SDL_RestoreMousePosition(window); SDL_RestoreMousePosition(window);
window->last_fullscreen_flags = window->flags; window->last_fullscreen_flags = window->flags;
return 0;
} }
#define CREATE_FLAGS \ #define CREATE_FLAGS \
@ -1935,9 +1940,7 @@ SDL_SetWindowFullscreen(SDL_Window * window, Uint32 flags)
window->flags &= ~FULLSCREEN_MASK; window->flags &= ~FULLSCREEN_MASK;
window->flags |= flags; window->flags |= flags;
SDL_UpdateFullscreenMode(window, FULLSCREEN_VISIBLE(window)); return SDL_UpdateFullscreenMode(window, FULLSCREEN_VISIBLE(window));
return 0;
} }
static SDL_Surface * static SDL_Surface *

View File

@ -21,12 +21,21 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
@interface SDLUIKitDelegate : NSObject<UIApplicationDelegate> { @interface SDLLaunchScreenController : UIViewController
}
+ (id) sharedAppDelegate; - (instancetype)init;
- (void)loadView;
- (NSUInteger)supportedInterfaceOrientations;
@end
@interface SDLUIKitDelegate : NSObject<UIApplicationDelegate>
+ (id)sharedAppDelegate;
+ (NSString *)getAppDelegateClassName; + (NSString *)getAppDelegateClassName;
- (void)hideLaunchScreen;
@end @end
/* vi: set ts=4 sw=4 expandtab: */ /* vi: set ts=4 sw=4 expandtab: */

View File

@ -28,8 +28,10 @@
#include "SDL_system.h" #include "SDL_system.h"
#include "SDL_main.h" #include "SDL_main.h"
#include "SDL_uikitappdelegate.h" #import "SDL_uikitappdelegate.h"
#include "SDL_uikitmodes.h" #import "SDL_uikitmodes.h"
#import "SDL_uikitwindow.h"
#include "../../events/SDL_events_c.h" #include "../../events/SDL_events_c.h"
#ifdef main #ifdef main
@ -39,12 +41,10 @@
static int forward_argc; static int forward_argc;
static char **forward_argv; static char **forward_argv;
static int exit_status; static int exit_status;
static UIWindow *launch_window;
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int i; int i;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
/* store arguments */ /* store arguments */
forward_argc = argc; forward_argc = argc;
@ -56,7 +56,9 @@ int main(int argc, char **argv)
forward_argv[i] = NULL; forward_argv[i] = NULL;
/* Give over control to run loop, SDLUIKitDelegate will handle most things from here */ /* 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 */ /* free the memory we used to hold copies of argc and argv */
for (i = 0; i < forward_argc; i++) { for (i = 0; i < forward_argc; i++) {
@ -64,7 +66,6 @@ int main(int argc, char **argv)
} }
free(forward_argv); free(forward_argv);
[pool release];
return exit_status; return exit_status;
} }
@ -75,224 +76,262 @@ SDL_IdleTimerDisabledChanged(void *userdata, const char *name, const char *oldVa
[UIApplication sharedApplication].idleTimerDisabled = disable; [UIApplication sharedApplication].idleTimerDisabled = disable;
} }
/* Load a launch image using the old UILaunchImageFile-era naming rules. */
static UIImage *
SDL_LoadLaunchImageNamed(NSString *name, int screenh)
{
UIInterfaceOrientation curorient = [UIApplication sharedApplication].statusBarOrientation;
UIUserInterfaceIdiom idiom = [UIDevice currentDevice].userInterfaceIdiom;
UIImage *image = nil;
@interface SDL_launchscreenviewcontroller : UIViewController { if (idiom == UIUserInterfaceIdiomPhone && screenh == 568) {
/* The image name for the iPhone 5 uses its height as a suffix. */
image = [UIImage imageNamed:[NSString stringWithFormat:@"%@-568h", name]];
} else if (idiom == UIUserInterfaceIdiomPad) {
/* iPad apps can launch in any orientation. */
if (UIInterfaceOrientationIsLandscape(curorient)) {
if (curorient == UIInterfaceOrientationLandscapeLeft) {
image = [UIImage imageNamed:[NSString stringWithFormat:@"%@-LandscapeLeft", name]];
} else {
image = [UIImage imageNamed:[NSString stringWithFormat:@"%@-LandscapeRight", name]];
}
if (!image) {
image = [UIImage imageNamed:[NSString stringWithFormat:@"%@-Landscape", name]];
}
} else {
if (curorient == UIInterfaceOrientationPortraitUpsideDown) {
image = [UIImage imageNamed:[NSString stringWithFormat:@"%@-PortraitUpsideDown", name]];
}
if (!image) {
image = [UIImage imageNamed:[NSString stringWithFormat:@"%@-Portrait", name]];
}
}
}
if (!image) {
image = [UIImage imageNamed:name];
}
return image;
} }
@end @implementation SDLLaunchScreenController {
UIInterfaceOrientationMask supportedOrientations;
}
@implementation SDL_launchscreenviewcontroller - (instancetype)init
- (id)init
{ {
self = [super init]; if (!(self = [super initWithNibName:nil bundle:nil])) {
if (self == nil) {
return nil; return nil;
} }
NSString* launch_screen_name = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UILaunchStoryboardName"]; NSBundle *bundle = [NSBundle mainBundle];
NSString *screenname = [bundle objectForInfoDictionaryKey:@"UILaunchStoryboardName"];
if(launch_screen_name) { /* Normally we don't want to rotate from the initial orientation. */
// TODO: If the NIB is not in the bundle, this will throw an exception. We might consider a pre-emptive check, but returning a useless viewcontroller isn't helpful and the check should be outside. supportedOrientations = (1 << [UIApplication sharedApplication].statusBarOrientation);
UIView* launch_screen = [[[NSBundle mainBundle] loadNibNamed:launch_screen_name owner:self options:nil] objectAtIndex:0];
CGSize size = [UIScreen mainScreen].bounds.size;
CGRect bounds = CGRectMake(0, 0, size.width, size.height); /* Launch screens were added in iOS 8. Otherwise we use launch images. */
if (screenname && UIKit_IsSystemVersionAtLeast(8.0)) {
[launch_screen setFrame:bounds]; @try {
[self setView:launch_screen]; self.view = [bundle loadNibNamed:screenname owner:self options:nil][0];
[launch_screen release]; }
@catch (NSException *exception) {
/* iOS displays a blank screen rather than falling back to an image,
* if a launch screen name is specified but it fails to load. */
return nil;
}
} }
if (!self.view) {
NSArray *launchimages = [bundle objectForInfoDictionaryKey:@"UILaunchImages"];
UIInterfaceOrientation curorient = [UIApplication sharedApplication].statusBarOrientation;
NSString *imagename = nil;
UIImage *image = nil;
int screenw = (int)([UIScreen mainScreen].bounds.size.width + 0.5);
int screenh = (int)([UIScreen mainScreen].bounds.size.height + 0.5);
/* We always want portrait-oriented size, to match UILaunchImageSize. */
if (screenw > screenh) {
int width = screenw;
screenw = screenh;
screenh = width;
}
/* Xcode 5 introduced a dictionary of launch images in Info.plist. */
if (launchimages) {
for (NSDictionary *dict in launchimages) {
UIInterfaceOrientationMask orientmask = UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
NSString *minversion = dict[@"UILaunchImageMinimumOSVersion"];
NSString *sizestring = dict[@"UILaunchImageSize"];
NSString *orientstring = dict[@"UILaunchImageOrientation"];
/* Ignore this image if the current version is too low. */
if (minversion && !UIKit_IsSystemVersionAtLeast(minversion.doubleValue)) {
continue;
}
/* Ignore this image if the size doesn't match. */
if (sizestring) {
CGSize size = CGSizeFromString(sizestring);
if ((int)(size.width + 0.5) != screenw || (int)(size.height + 0.5) != screenh) {
continue;
}
}
if (orientstring) {
if ([orientstring isEqualToString:@"PortraitUpsideDown"]) {
orientmask = UIInterfaceOrientationMaskPortraitUpsideDown;
} else if ([orientstring isEqualToString:@"Landscape"]) {
orientmask = UIInterfaceOrientationMaskLandscape;
} else if ([orientstring isEqualToString:@"LandscapeLeft"]) {
orientmask = UIInterfaceOrientationMaskLandscapeLeft;
} else if ([orientstring isEqualToString:@"LandscapeRight"]) {
orientmask = UIInterfaceOrientationMaskLandscapeRight;
}
}
/* Ignore this image if the orientation doesn't match. */
if ((orientmask & (1 << curorient)) == 0) {
continue;
}
imagename = dict[@"UILaunchImageName"];
}
if (imagename) {
image = [UIImage imageNamed:imagename];
}
} else {
imagename = [bundle objectForInfoDictionaryKey:@"UILaunchImageFile"];
if (imagename) {
image = SDL_LoadLaunchImageNamed(imagename, screenh);
}
if (!image) {
image = SDL_LoadLaunchImageNamed(@"Default", screenh);
}
}
if (image) {
if (image.size.width > image.size.height) {
supportedOrientations = UIInterfaceOrientationMaskLandscape;
} else {
supportedOrientations = UIInterfaceOrientationMaskPortrait;
}
self.view = [[UIImageView alloc] initWithImage:image];
}
}
return self; return self;
} }
- (NSUInteger)supportedInterfaceOrientations - (void)loadView
{ {
NSUInteger orientationMask = UIInterfaceOrientationMaskAll; /* Do nothing. */
/* Don't allow upside-down orientation on the phone, so answering calls is in the natural orientation */
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
orientationMask &= ~UIInterfaceOrientationMaskPortraitUpsideDown;
}
return orientationMask;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orient
{
NSUInteger orientationMask = [self supportedInterfaceOrientations];
return (orientationMask & (1 << orient));
}
@end
@interface SDL_splashviewcontroller : UIViewController {
UIImageView *splash;
UIImage *splashPortrait;
UIImage *splashLandscape;
}
- (void)updateSplashImage:(UIInterfaceOrientation)interfaceOrientation;
@end
@implementation SDL_splashviewcontroller
- (id)init
{
self = [super init];
if (self == nil) {
return nil;
}
self->splash = [[UIImageView alloc] init];
[self setView:self->splash];
CGSize size = [UIScreen mainScreen].bounds.size;
float height = SDL_max(size.width, size.height);
/* FIXME: Some where around iOS 7, UILaunchImages in the Info.plist was introduced which explicitly maps image names to devices and orientations.
This gets rid of the hardcoded magic file names and allows more control for OS version, orientation, retina, and device.
But this existing code needs to be modified to look in the Info.plist for each key and act appropriately for the correct iOS version.
But iOS 8 superscedes this process and introduces the LaunchScreen NIB which uses autolayout to handle all orientations and devices.
Since we now have a LaunchScreen solution, this may never get fixed,
but this note is here for anybody trying to debug their program on iOS 7 and doesn't understand why their Info.plist isn't working.
*/
self->splashPortrait = [UIImage imageNamed:[NSString stringWithFormat:@"Default-%dh.png", (int)height]];
if (!self->splashPortrait) {
self->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];
}
[self updateSplashImage:[[UIApplication sharedApplication] statusBarOrientation]];
return self;
} }
- (NSUInteger)supportedInterfaceOrientations - (NSUInteger)supportedInterfaceOrientations
{ {
NSUInteger orientationMask = UIInterfaceOrientationMaskAll; return supportedOrientations;
/* Don't allow upside-down orientation on the phone, so answering calls is in the natural orientation */
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
orientationMask &= ~UIInterfaceOrientationMaskPortraitUpsideDown;
}
return orientationMask;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orient
{
NSUInteger orientationMask = [self supportedInterfaceOrientations];
return (orientationMask & (1 << orient));
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration
{
[self updateSplashImage:interfaceOrientation];
}
- (void)updateSplashImage:(UIInterfaceOrientation)interfaceOrientation
{
UIImage *image;
if (UIInterfaceOrientationIsLandscape(interfaceOrientation)) {
image = self->splashLandscape;
} else {
image = self->splashPortrait;
}
if (image)
{
splash.image = image;
}
} }
@end @end
@implementation SDLUIKitDelegate {
@implementation SDLUIKitDelegate UIWindow *launchWindow;
}
/* convenience method */ /* convenience method */
+ (id) sharedAppDelegate + (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
return [[UIApplication sharedApplication] delegate]; * called before this method */
return [UIApplication sharedApplication].delegate;
} }
+ (NSString *)getAppDelegateClassName + (NSString *)getAppDelegateClassName
{ {
/* subclassing notice: when you subclass this appdelegate, make sure to add a category to override /* subclassing notice: when you subclass this appdelegate, make sure to add
this method and return the actual name of the delegate */ * a category to override this method and return the actual name of the
* delegate */
return @"SDLUIKitDelegate"; return @"SDLUIKitDelegate";
} }
- (id)init - (void)hideLaunchScreen
{ {
self = [super init]; UIWindow *window = launchWindow;
return self;
if (!window || window.hidden) {
return;
}
launchWindow = nil;
/* Do a nice animated fade-out (roughly matches the real launch behavior.) */
[UIView animateWithDuration:0.2 animations:^{
window.alpha = 0.0;
} completion:^(BOOL finished) {
window.hidden = YES;
}];
} }
- (void)postFinishLaunch - (void)postFinishLaunch
{ {
/* Hide the launch screen the next time the run loop is run. SDL apps will
* have a chance to load resources while the launch screen is still up. */
[self performSelector:@selector(hideLaunchScreen) withObject:nil afterDelay:0.0];
/* run the user's application, passing argc and argv */ /* run the user's application, passing argc and argv */
SDL_iPhoneSetEventPump(SDL_TRUE); SDL_iPhoneSetEventPump(SDL_TRUE);
exit_status = SDL_main(forward_argc, forward_argv); exit_status = SDL_main(forward_argc, forward_argv);
SDL_iPhoneSetEventPump(SDL_FALSE); SDL_iPhoneSetEventPump(SDL_FALSE);
/* If we showed a splash image, clean it up */ if (launchWindow) {
if (launch_window) { launchWindow.hidden = YES;
[launch_window release]; launchWindow = nil;
launch_window = NULL;
} }
/* exit, passing the return status from the user's application */ /* exit, passing the return status from the user's application */
/* We don't actually exit to support applications that do setup in /* We don't actually exit to support applications that do setup in their
* their main function and then allow the Cocoa event loop to run. * main function and then allow the Cocoa event loop to run. */
*/
/* exit(exit_status); */ /* exit(exit_status); */
} }
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{ {
/* Keep the launch image up until we set a video mode */ NSBundle *bundle = [NSBundle mainBundle];
launch_window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
/* iOS 8 introduces LaunchScreen NIBs which use autolayout to handle all devices and orientations with a single NIB instead of multiple launch images. #if SDL_IPHONE_LAUNCHSCREEN
This is also the only way to get the App Store badge "Optimized for iPhone 6 and iPhone 6 Plus". /* The normal launch screen is displayed until didFinishLaunching returns,
So if the application is running on iOS 8 or greater AND has specified a LaunchScreen in their Info.plist, we should use the LaunchScreen NIB. * but SDL_main is called after that happens and there may be a noticeable
Otherwise, we should fallback to the legacy behavior of launch screen images. * delay between the start of SDL_main and when the first real frame is
*/ * displayed (e.g. if resources are loaded before SDL_GL_SwapWindow is
NSString* launch_screen_name = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UILaunchStoryboardName"]; * called), so we show the launch screen programmatically until the first
if( ([[UIDevice currentDevice].systemVersion intValue] >= 8) && (nil != launch_screen_name) ) { * time events are pumped. */
// iOS 8.0 and above uses LaunchScreen.xib UIViewController *viewcontroller = [[SDLLaunchScreenController alloc] init];
SDL_launchscreenviewcontroller* launch_screen_view_controller = [[SDL_launchscreenviewcontroller alloc] init];
launch_window.rootViewController = launch_screen_view_controller;
[launch_window addSubview:launch_screen_view_controller.view];
[launch_window makeKeyAndVisible];
} else {
// Anything less than iOS 8.0
UIViewController *splashViewController = [[SDL_splashviewcontroller alloc] init]; if (viewcontroller.view) {
launch_window.rootViewController = splashViewController; launchWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
[launch_window addSubview:splashViewController.view];
[launch_window makeKeyAndVisible]; /* We don't want the launch window immediately hidden when a real SDL
* window is shown - we fade it out ourselves when we're ready. */
launchWindow.windowLevel = UIWindowLevelNormal + 1.0;
/* Show the window but don't make it key. Events should always go to
* other windows when possible. */
launchWindow.hidden = NO;
launchWindow.rootViewController = viewcontroller;
} }
#endif
/* Set working directory to resource path */ /* Set working directory to resource path */
[[NSFileManager defaultManager] changeCurrentDirectoryPath: [[NSBundle mainBundle] resourcePath]]; [[NSFileManager defaultManager] changeCurrentDirectoryPath:[bundle resourcePath]];
/* register a callback for the idletimer hint */ /* register a callback for the idletimer hint */
SDL_AddHintCallback(SDL_HINT_IDLE_TIMER_DISABLED, SDL_AddHintCallback(SDL_HINT_IDLE_TIMER_DISABLED,
@ -314,7 +353,35 @@ SDL_IdleTimerDisabledChanged(void *userdata, const char *name, const char *oldVa
SDL_SendAppEvent(SDL_APP_LOWMEMORY); SDL_SendAppEvent(SDL_APP_LOWMEMORY);
} }
- (void) applicationWillResignActive:(UIApplication*)application - (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(); SDL_VideoDevice *_this = SDL_GetVideoDevice();
if (_this) { if (_this) {
@ -327,17 +394,17 @@ SDL_IdleTimerDisabledChanged(void *userdata, const char *name, const char *oldVa
SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND); SDL_SendAppEvent(SDL_APP_WILLENTERBACKGROUND);
} }
- (void) applicationDidEnterBackground:(UIApplication*)application - (void)applicationDidEnterBackground:(UIApplication*)application
{ {
SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND); SDL_SendAppEvent(SDL_APP_DIDENTERBACKGROUND);
} }
- (void) applicationWillEnterForeground:(UIApplication*)application - (void)applicationWillEnterForeground:(UIApplication*)application
{ {
SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND); SDL_SendAppEvent(SDL_APP_WILLENTERFOREGROUND);
} }
- (void) applicationDidBecomeActive:(UIApplication*)application - (void)applicationDidBecomeActive:(UIApplication*)application
{ {
SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND); SDL_SendAppEvent(SDL_APP_DIDENTERFOREGROUND);
@ -353,11 +420,11 @@ SDL_IdleTimerDisabledChanged(void *userdata, const char *name, const char *oldVa
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{ {
NSURL *fileURL = [url filePathURL]; NSURL *fileURL = url.filePathURL;
if (fileURL != nil) { if (fileURL != nil) {
SDL_SendDropFile([[fileURL path] UTF8String]); SDL_SendDropFile([fileURL.path UTF8String]);
} else { } else {
SDL_SendDropFile([[url absoluteString] UTF8String]); SDL_SendDropFile([url.absoluteString UTF8String]);
} }
return YES; return YES;
} }

View File

@ -40,8 +40,9 @@ SDL_iPhoneSetEventPump(SDL_bool enabled)
void void
UIKit_PumpEvents(_THIS) UIKit_PumpEvents(_THIS)
{ {
if (!UIKit_EventPumpEnabled) if (!UIKit_EventPumpEnabled) {
return; return;
}
/* Let the run loop run for a short amount of time: long enough for /* 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 touch events to get processed (which is important to get certain

View File

@ -30,35 +30,20 @@
static SDL_bool s_showingMessageBox = SDL_FALSE; static SDL_bool s_showingMessageBox = SDL_FALSE;
@interface UIKit_UIAlertViewDelegate : NSObject <UIAlertViewDelegate> { @interface SDLAlertViewDelegate : NSObject <UIAlertViewDelegate>
@private
int *clickedButtonIndex;
}
- (id)initWithButtonIndex:(int *)_buttonIndex; @property (nonatomic, assign) int clickedIndex;
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex;
@end @end
@implementation UIKit_UIAlertViewDelegate @implementation SDLAlertViewDelegate
- (id)initWithButtonIndex:(int *)buttonIndex - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{ {
self = [self init]; _clickedIndex = (int)buttonIndex;
if (self == nil) {
return nil;
}
self->clickedButtonIndex = buttonIndex;
return self;
} }
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex; @end
{
*clickedButtonIndex = (int)buttonIndex;
}
@end /* UIKit_UIAlertViewDelegate */
SDL_bool SDL_bool
@ -70,42 +55,39 @@ UIKit_ShowingMessageBox()
int int
UIKit_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid) 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; int i;
for (i = 0; i < messageboxdata->numbuttons; ++i) { const SDL_MessageBoxButtonData *buttons = messageboxdata->buttons;
[alert addButtonWithTitle:[[NSString alloc] initWithUTF8String:buttons[i].text]];
@autoreleasepool {
UIAlertView *alert = [[UIAlertView alloc] init];
SDLAlertViewDelegate *delegate = [[SDLAlertViewDelegate alloc] init];
alert.delegate = delegate;
alert.title = @(messageboxdata->title);
alert.message = @(messageboxdata->message);
for (i = 0; i < messageboxdata->numbuttons; ++i) {
[alert addButtonWithTitle:@(buttons[i].text)];
}
/* Set up for showing the alert */
delegate.clickedIndex = 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 (delegate.clickedIndex == messageboxdata->numbuttons) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
s_showingMessageBox = SDL_FALSE;
*buttonid = messageboxdata->buttons[delegate.clickedIndex].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; return 0;
} }

View File

@ -25,17 +25,17 @@
#include "SDL_uikitvideo.h" #include "SDL_uikitvideo.h"
typedef struct @interface SDL_DisplayData : NSObject
{
UIScreen *uiscreen;
CGFloat scale;
} SDL_DisplayData;
typedef struct @property (nonatomic, strong) UIScreen *uiscreen;
{
UIScreenMode *uiscreenmode; @end
CGFloat scale;
} SDL_DisplayModeData; @interface SDL_DisplayModeData : NSObject
@property (nonatomic, strong) UIScreenMode *uiscreenmode;
@end
extern SDL_bool UIKit_IsDisplayLandscape(UIScreen *uiscreen); extern SDL_bool UIKit_IsDisplayLandscape(UIScreen *uiscreen);

View File

@ -25,27 +25,36 @@
#include "SDL_assert.h" #include "SDL_assert.h"
#include "SDL_uikitmodes.h" #include "SDL_uikitmodes.h"
@implementation SDL_DisplayData
@synthesize uiscreen;
@end
@implementation SDL_DisplayModeData
@synthesize uiscreenmode;
@end
static int static int
UIKit_AllocateDisplayModeData(SDL_DisplayMode * mode, UIKit_AllocateDisplayModeData(SDL_DisplayMode * mode,
UIScreenMode * uiscreenmode, CGFloat scale) UIScreenMode * uiscreenmode)
{ {
SDL_DisplayModeData *data = NULL; SDL_DisplayModeData *data = nil;
if (uiscreenmode != nil) { if (uiscreenmode != nil) {
/* Allocate the display mode data */ /* Allocate the display mode data */
data = (SDL_DisplayModeData *) SDL_malloc(sizeof(*data)); data = [[SDL_DisplayModeData alloc] init];
if (!data) { if (!data) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
data->uiscreenmode = uiscreenmode; data.uiscreenmode = uiscreenmode;
[data->uiscreenmode retain];
data->scale = scale;
} }
mode->driverdata = data; mode->driverdata = (void *) CFBridgingRetain(data);
return 0; return 0;
} }
@ -54,23 +63,21 @@ static void
UIKit_FreeDisplayModeData(SDL_DisplayMode * mode) UIKit_FreeDisplayModeData(SDL_DisplayMode * mode)
{ {
if (mode->driverdata != NULL) { if (mode->driverdata != NULL) {
SDL_DisplayModeData *data = (SDL_DisplayModeData *)mode->driverdata; CFRelease(mode->driverdata);
[data->uiscreenmode release];
SDL_free(data);
mode->driverdata = NULL; mode->driverdata = NULL;
} }
} }
static int static int
UIKit_AddSingleDisplayMode(SDL_VideoDisplay * display, int w, int h, UIKit_AddSingleDisplayMode(SDL_VideoDisplay * display, int w, int h,
UIScreenMode * uiscreenmode, CGFloat scale) UIScreenMode * uiscreenmode)
{ {
SDL_DisplayMode mode; SDL_DisplayMode mode;
SDL_zero(mode); SDL_zero(mode);
mode.format = SDL_PIXELFORMAT_ABGR8888; mode.format = SDL_PIXELFORMAT_ABGR8888;
mode.refresh_rate = 0; mode.refresh_rate = 0;
if (UIKit_AllocateDisplayModeData(&mode, uiscreenmode, scale) < 0) { if (UIKit_AllocateDisplayModeData(&mode, uiscreenmode) < 0) {
return -1; return -1;
} }
@ -85,16 +92,16 @@ UIKit_AddSingleDisplayMode(SDL_VideoDisplay * display, int w, int h,
} }
static int 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) UIScreenMode * uiscreenmode, SDL_bool addRotation)
{ {
if (UIKit_AddSingleDisplayMode(display, w, h, uiscreenmode, scale) < 0) { if (UIKit_AddSingleDisplayMode(display, w, h, uiscreenmode) < 0) {
return -1; return -1;
} }
if (addRotation) { if (addRotation) {
/* Add the rotated version */ /* Add the rotated version */
if (UIKit_AddSingleDisplayMode(display, h, w, uiscreenmode, scale) < 0) { if (UIKit_AddSingleDisplayMode(display, h, w, uiscreenmode) < 0) {
return -1; return -1;
} }
} }
@ -105,7 +112,7 @@ UIKit_AddDisplayMode(SDL_VideoDisplay * display, int w, int h, CGFloat scale,
static int static int
UIKit_AddDisplay(UIScreen *uiscreen) UIKit_AddDisplay(UIScreen *uiscreen)
{ {
CGSize size = [uiscreen bounds].size; CGSize size = uiscreen.bounds.size;
/* Make sure the width/height are oriented correctly */ /* Make sure the width/height are oriented correctly */
if (UIKit_IsDisplayLandscape(uiscreen) != (size.width > size.height)) { if (UIKit_IsDisplayLandscape(uiscreen) != (size.width > size.height)) {
@ -114,24 +121,16 @@ UIKit_AddDisplay(UIScreen *uiscreen)
size.height = height; 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_VideoDisplay display;
SDL_DisplayMode mode; SDL_DisplayMode mode;
SDL_zero(mode); SDL_zero(mode);
mode.format = SDL_PIXELFORMAT_ABGR8888; mode.format = SDL_PIXELFORMAT_ABGR8888;
mode.w = (int)(size.width * scale); mode.w = (int) size.width;
mode.h = (int)(size.height * scale); 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; return -1;
} }
@ -140,17 +139,15 @@ UIKit_AddDisplay(UIScreen *uiscreen)
display.current_mode = mode; display.current_mode = mode;
/* Allocate the display data */ /* Allocate the display data */
SDL_DisplayData *data = (SDL_DisplayData *) SDL_malloc(sizeof(*data)); SDL_DisplayData *data = [[SDL_DisplayData alloc] init];
if (!data) { if (!data) {
UIKit_FreeDisplayModeData(&display.desktop_mode); UIKit_FreeDisplayModeData(&display.desktop_mode);
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
[uiscreen retain]; data.uiscreen = uiscreen;
data->uiscreen = uiscreen;
data->scale = scale;
display.driverdata = data; display.driverdata = (void *) CFBridgingRetain(data);
SDL_AddVideoDisplay(&display); SDL_AddVideoDisplay(&display);
return 0; return 0;
@ -160,9 +157,9 @@ SDL_bool
UIKit_IsDisplayLandscape(UIScreen *uiscreen) UIKit_IsDisplayLandscape(UIScreen *uiscreen)
{ {
if (uiscreen == [UIScreen mainScreen]) { if (uiscreen == [UIScreen mainScreen]) {
return UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]); return UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation);
} else { } else {
CGSize size = [uiscreen bounds].size; CGSize size = uiscreen.bounds.size;
return (size.width > size.height); return (size.width > size.height);
} }
} }
@ -170,9 +167,11 @@ UIKit_IsDisplayLandscape(UIScreen *uiscreen)
int int
UIKit_InitModes(_THIS) UIKit_InitModes(_THIS)
{ {
for (UIScreen *uiscreen in [UIScreen screens]) { @autoreleasepool {
if (UIKit_AddDisplay(uiscreen) < 0) { for (UIScreen *uiscreen in [UIScreen screens]) {
return -1; if (UIKit_AddDisplay(uiscreen) < 0) {
return -1;
}
} }
} }
@ -182,34 +181,35 @@ UIKit_InitModes(_THIS)
void void
UIKit_GetDisplayModes(_THIS, SDL_VideoDisplay * display) 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 isLandscape = UIKit_IsDisplayLandscape(data.uiscreen);
SDL_bool addRotation = (data->uiscreen == [UIScreen mainScreen]); SDL_bool addRotation = (data.uiscreen == [UIScreen mainScreen]);
CGFloat scale = data.uiscreen.scale;
for (UIScreenMode *uimode in [data->uiscreen availableModes]) { #ifdef __IPHONE_8_0
CGSize size = [uimode size]; /* The UIScreenMode of an iPhone 6 Plus should be 1080x1920 rather than
int w = (int)size.width; * 1242x2208 (414x736@3x), so we should use the native scale. */
int h = (int)size.height; if ([data.uiscreen respondsToSelector:@selector(nativeScale)]) {
scale = data.uiscreen.nativeScale;
/* Make sure the width/height are oriented correctly */
if (isLandscape != (w > h)) {
int tmp = w;
w = h;
h = tmp;
} }
#endif
/* Add the native screen resolution. */ for (UIScreenMode *uimode in data.uiscreen.availableModes) {
UIKit_AddDisplayMode(display, w, h, data->scale, uimode, addRotation); /* The size of a UIScreenMode is in pixels, but we deal exclusively
* in points (except in SDL_GL_GetDrawableSize.) */
int w = (int)(uimode.size.width / scale);
int h = (int)(uimode.size.height / scale);
if (data->scale != 1.0f) { /* Make sure the width/height are oriented correctly */
/* Add the native screen resolution divided by its scale. if (isLandscape != (w > h)) {
* This is so devices capable of e.g. 640x960 also advertise 320x480. int tmp = w;
*/ w = h;
UIKit_AddDisplayMode(display, h = tmp;
(int)(size.width / data->scale), }
(int)(size.height / data->scale),
1.0f, uimode, addRotation); UIKit_AddDisplayMode(display, w, h, uimode, addRotation);
} }
} }
} }
@ -217,19 +217,24 @@ UIKit_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
int int
UIKit_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode) UIKit_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
{ {
SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata; @autoreleasepool {
SDL_DisplayModeData *modedata = (SDL_DisplayModeData *)mode->driverdata; 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 (data.uiscreen == [UIScreen mainScreen]) {
if (mode->w > mode->h) { /* [UIApplication setStatusBarOrientation:] no longer works reliably
if (!UIKit_IsDisplayLandscape(data->uiscreen)) { * in recent iOS versions, so we can't rotate the screen when setting
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:NO]; * the display mode. */
} if (mode->w > mode->h) {
} else if (mode->w < mode->h) { if (!UIKit_IsDisplayLandscape(data.uiscreen)) {
if (UIKit_IsDisplayLandscape(data->uiscreen)) { return SDL_SetError("Screen orientation does not match display mode size");
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait animated:NO]; }
} else if (mode->w < mode->h) {
if (UIKit_IsDisplayLandscape(data.uiscreen)) {
return SDL_SetError("Screen orientation does not match display mode size");
}
} }
} }
} }
@ -242,19 +247,21 @@ UIKit_QuitModes(_THIS)
{ {
/* Release Objective-C objects, so higher level doesn't free() them. */ /* Release Objective-C objects, so higher level doesn't free() them. */
int i, j; int i, j;
for (i = 0; i < _this->num_displays; i++) { @autoreleasepool {
SDL_VideoDisplay *display = &_this->displays[i]; for (i = 0; i < _this->num_displays; i++) {
SDL_VideoDisplay *display = &_this->displays[i];
UIKit_FreeDisplayModeData(&display->desktop_mode); UIKit_FreeDisplayModeData(&display->desktop_mode);
for (j = 0; j < display->num_display_modes; j++) { for (j = 0; j < display->num_display_modes; j++) {
SDL_DisplayMode *mode = &display->display_modes[j]; SDL_DisplayMode *mode = &display->display_modes[j];
UIKit_FreeDisplayModeData(mode); 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;
} }
} }

View File

@ -25,6 +25,8 @@
extern int UIKit_GL_MakeCurrent(_THIS, SDL_Window * window, extern int UIKit_GL_MakeCurrent(_THIS, SDL_Window * window,
SDL_GLContext context); 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 void UIKit_GL_SwapWindow(_THIS, SDL_Window * window);
extern SDL_GLContext UIKit_GL_CreateContext(_THIS, SDL_Window * window); extern SDL_GLContext UIKit_GL_CreateContext(_THIS, SDL_Window * window);
extern void UIKit_GL_DeleteContext(_THIS, SDL_GLContext context); extern void UIKit_GL_DeleteContext(_THIS, SDL_GLContext context);

View File

@ -23,10 +23,10 @@
#if SDL_VIDEO_DRIVER_UIKIT #if SDL_VIDEO_DRIVER_UIKIT
#include "SDL_uikitopengles.h" #include "SDL_uikitopengles.h"
#include "SDL_uikitopenglview.h" #import "SDL_uikitopenglview.h"
#include "SDL_uikitappdelegate.h"
#include "SDL_uikitmodes.h" #include "SDL_uikitmodes.h"
#include "SDL_uikitwindow.h" #include "SDL_uikitwindow.h"
#include "SDL_uikitevents.h"
#include "../SDL_sysvideo.h" #include "../SDL_sysvideo.h"
#include "../../events/SDL_keyboard_c.h" #include "../../events/SDL_keyboard_c.h"
#include "../../events/SDL_mouse_c.h" #include "../../events/SDL_mouse_c.h"
@ -40,148 +40,189 @@ void *
UIKit_GL_GetProcAddress(_THIS, const char *proc) UIKit_GL_GetProcAddress(_THIS, const char *proc)
{ {
/* Look through all SO's for the proc symbol. Here's why: /* Look through all SO's for the proc symbol. Here's why:
-Looking for the path to the OpenGL Library seems not to work in the iPhone Simulator. * -Looking for the path to the OpenGL Library seems not to work in the iOS Simulator.
-We don't know that the path won't change in the future. * -We don't know that the path won't change in the future. */
*/
return dlsym(RTLD_DEFAULT, proc); return dlsym(RTLD_DEFAULT, proc);
} }
/* /*
note that SDL_GL_Delete context makes it current without passing the window note that SDL_GL_DeleteContext 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)
{ {
[EAGLContext setCurrentContext: context]; @autoreleasepool {
SDLEAGLContext *eaglcontext = (__bridge SDLEAGLContext *) context;
if (![EAGLContext setCurrentContext:eaglcontext]) {
return SDL_SetError("Could not make EAGL context current");
}
if (eaglcontext) {
[eaglcontext.sdlView setSDLWindow:window];
}
}
return 0; return 0;
} }
void
UIKit_GL_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h)
{
@autoreleasepool {
SDL_WindowData *data = (__bridge SDL_WindowData *)window->driverdata;
UIView *view = data.viewcontroller.view;
if ([view isKindOfClass:[SDL_uikitopenglview class]]) {
SDL_uikitopenglview *glview = (SDL_uikitopenglview *) view;
if (w) {
*w = glview.backingWidth;
}
if (h) {
*h = glview.backingHeight;
}
}
}
}
int int
UIKit_GL_LoadLibrary(_THIS, const char *path) UIKit_GL_LoadLibrary(_THIS, const char *path)
{ {
/* /* We shouldn't pass a path to this function, since we've already loaded the
shouldn't be passing a path into this function * library. */
why? Because we've already loaded the library
and because the SDK forbids loading an external SO
*/
if (path != NULL) { if (path != NULL) {
return SDL_SetError("iPhone GL Load Library just here for compatibility"); return SDL_SetError("iOS GL Load Library just here for compatibility");
} }
return 0; return 0;
} }
void UIKit_GL_SwapWindow(_THIS, SDL_Window * window) void UIKit_GL_SwapWindow(_THIS, SDL_Window * window)
{ {
@autoreleasepool {
SDLEAGLContext *context = (__bridge SDLEAGLContext *) SDL_GL_GetCurrentContext();
#if SDL_POWER_UIKIT #if SDL_POWER_UIKIT
/* Check once a frame to see if we should turn off the battery monitor. */ /* Check once a frame to see if we should turn off the battery monitor. */
SDL_UIKit_UpdateBatteryMonitoring(); SDL_UIKit_UpdateBatteryMonitoring();
#endif #endif
SDL_WindowData *data = (SDL_WindowData *)window->driverdata; [context.sdlView swapBuffers];
if (nil == data->view) { /* You need to pump events in order for the OS to make changes visible.
return; * We don't pump events here because we don't want iOS application events
* (low memory, terminate, etc.) to happen inside low level rendering. */
} }
[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_GLContext
UIKit_GL_CreateContext(_THIS, SDL_Window * window)
{ {
SDL_uikitopenglview *view; @autoreleasepool {
SDL_WindowData *data = (SDL_WindowData *) window->driverdata; SDL_uikitopenglview *view;
SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
SDL_DisplayData *displaydata = display->driverdata; CGRect frame = UIKit_ComputeViewFrame(window, data.uiwindow.screen);
SDL_DisplayModeData *displaymodedata = display->current_mode.driverdata; EAGLSharegroup *sharegroup = nil;
UIWindow *uiwindow = data->uiwindow; CGFloat scale = 1.0;
EAGLSharegroup *share_group = nil;
if (_this->gl_config.share_with_current_context) { if (_this->gl_config.share_with_current_context) {
SDL_uikitopenglview *view = (SDL_uikitopenglview *) SDL_GL_GetCurrentContext(); EAGLContext *context = (__bridge EAGLContext *) SDL_GL_GetCurrentContext();
share_group = [view.context sharegroup]; sharegroup = context.sharegroup;
} }
/* construct our view, passing in SDL's OpenGL configuration data */ if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
CGRect frame; /* Set the scale to the natural scale factor of the screen - the
if (window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_BORDERLESS)) { * backing dimensions of the OpenGL view will match the pixel
frame = [displaydata->uiscreen bounds]; * dimensions of the screen rather than the dimensions in points. */
} else { #ifdef __IPHONE_8_0
frame = [displaydata->uiscreen applicationFrame]; if ([data.uiwindow.screen respondsToSelector:@selector(nativeScale)]) {
} scale = data.uiwindow.screen.nativeScale;
view = [[SDL_uikitopenglview alloc] initWithFrame: frame } else
scale: displaymodedata->scale #endif
retainBacking: _this->gl_config.retained_backing {
rBits: _this->gl_config.red_size scale = data.uiwindow.screen.scale;
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;
}
EAGLContext *context = view.context;
if (UIKit_GL_MakeCurrent(_this, window, context) < 0) {
UIKit_GL_DeleteContext(_this, context);
return NULL;
}
/* Make this window the current mouse focus for touch input */
if (displaydata->uiscreen == [UIScreen mainScreen]) {
SDL_SetMouseFocus(window);
SDL_SetKeyboardFocus(window);
}
return context;
}
void UIKit_GL_DeleteContext(_THIS, SDL_GLContext context)
{
SDL_Window *window;
/* Find the view associated with this context */
for (window = _this->windows; window; window = window->next) {
SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
SDL_uikitopenglview *view = data->view;
if (view.context == context) {
/* the delegate has retained the view, this will release him */
if (view->viewcontroller) {
UIWindow *uiwindow = (UIWindow *)view.superview;
if (uiwindow.rootViewController == view->viewcontroller) {
uiwindow.rootViewController = nil;
}
[view->viewcontroller setView:nil];
[view->viewcontroller release];
} }
[view removeFromSuperview]; }
/* FIXME: This doesn't actually call view dealloc - what is holding a reference to it? */ /* construct our view, passing in SDL's OpenGL configuration data */
[view release]; view = [[SDL_uikitopenglview alloc] initWithFrame:frame
return; 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:sharegroup];
if (!view) {
return NULL;
}
SDLEAGLContext *context = view.context;
if (UIKit_GL_MakeCurrent(_this, window, (__bridge SDL_GLContext) context) < 0) {
UIKit_GL_DeleteContext(_this, (SDL_GLContext) CFBridgingRetain(context));
return NULL;
}
/* We return a +1'd context. The window's driverdata owns the view (via
* MakeCurrent.) */
return (SDL_GLContext) CFBridgingRetain(context);
}
}
void
UIKit_GL_DeleteContext(_THIS, SDL_GLContext context)
{
@autoreleasepool {
/* Transfer ownership the +1'd context to ARC. */
SDLEAGLContext *eaglcontext = (SDLEAGLContext *) CFBridgingRelease(context);
/* Detach the context's view from its window. */
[eaglcontext.sdlView setSDLWindow:NULL];
}
}
Uint32
SDL_iPhoneGetViewRenderbuffer(SDL_Window * window)
{
if (!window) {
SDL_SetError("Invalid window");
return 0;
}
@autoreleasepool {
SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
UIView *view = data.viewcontroller.view;
if ([view isKindOfClass:[SDL_uikitopenglview class]]) {
SDL_uikitopenglview *glview = (SDL_uikitopenglview *) view;
return glview.drawableRenderbuffer;
} }
} }
/* View not found... delete the context anyway? */ SDL_SetError("Window does not have an attached OpenGL view");
[(EAGLContext *)context release]; return 0;
}
Uint32
SDL_iPhoneGetViewFramebuffer(SDL_Window * window)
{
if (!window) {
SDL_SetError("Invalid window");
return 0;
}
@autoreleasepool {
SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
UIView *view = data.viewcontroller.view;
if ([view isKindOfClass:[SDL_uikitopenglview class]]) {
SDL_uikitopenglview *glview = (SDL_uikitopenglview *) view;
return glview.drawableFramebuffer;
}
}
SDL_SetError("Window does not have an attached OpenGL view");
return 0;
} }
#endif /* SDL_VIDEO_DRIVER_UIKIT */ #endif /* SDL_VIDEO_DRIVER_UIKIT */

View File

@ -21,66 +21,48 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import <OpenGLES/EAGL.h> #import <OpenGLES/EAGL.h>
#import <OpenGLES/ES1/gl.h> #import <OpenGLES/ES2/gl.h>
#import <OpenGLES/ES1/glext.h>
#import "SDL_uikitview.h" #import "SDL_uikitview.h"
/* #include "SDL_uikitvideo.h"
This class wraps the CAEAGLLayer from CoreAnimation into a convenient UIView subclass.
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 {
@private @class SDL_uikitopenglview;
/* The pixel dimensions of the backbuffer */
GLint backingWidth;
GLint backingHeight;
EAGLContext *context; @interface SDLEAGLContext : EAGLContext
/* OpenGL names for the renderbuffer and framebuffers used to render to this view */ @property (nonatomic, weak) SDL_uikitopenglview *sdlView;
GLuint viewRenderbuffer, viewFramebuffer;
/* OpenGL name for the depth buffer that is attached to viewFramebuffer, if it exists (0 if it does not exist) */ @end
GLuint depthRenderbuffer;
/* format of depthRenderbuffer */ @interface SDL_uikitopenglview : SDL_uikitview
GLenum depthBufferFormat;
id displayLink; - (instancetype)initWithFrame:(CGRect)frame
int animationInterval; scale:(CGFloat)scale
void (*animationCallback)(void*); retainBacking:(BOOL)retained
void *animationCallbackParam; 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;
@property (nonatomic, retain, readonly) EAGLContext *context; @property (nonatomic, readonly, strong) SDLEAGLContext *context;
/* The width and height of the drawable in pixels (as opposed to points.) */
@property (nonatomic, readonly) int backingWidth;
@property (nonatomic, readonly) int backingHeight;
@property (nonatomic, readonly) GLuint drawableRenderbuffer;
@property (nonatomic, readonly) GLuint drawableFramebuffer;
- (void)swapBuffers; - (void)swapBuffers;
- (void)setCurrentContext; - (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)updateFrame;
- (void)setAnimationCallback:(int)interval
callback:(void (*)(void*))callback
callbackParam:(void*)callbackParam;
- (void)startAnimation;
- (void)stopAnimation;
- (void)doLoop:(CADisplayLink*)sender;
@end @end
/* vi: set ts=4 sw=4 expandtab: */ /* vi: set ts=4 sw=4 expandtab: */

View File

@ -22,170 +22,190 @@
#if SDL_VIDEO_DRIVER_UIKIT #if SDL_VIDEO_DRIVER_UIKIT
#include <QuartzCore/QuartzCore.h>
#include <OpenGLES/EAGLDrawable.h> #include <OpenGLES/EAGLDrawable.h>
#include "SDL_uikitopenglview.h" #include <OpenGLES/ES2/glext.h>
#include "SDL_uikitmessagebox.h" #import "SDL_uikitopenglview.h"
#include "SDL_uikitwindow.h"
@implementation SDLEAGLContext
@implementation SDL_uikitopenglview @end
@implementation SDL_uikitopenglview {
/* The renderbuffer and framebuffer used to render to this layer. */
GLuint viewRenderbuffer, viewFramebuffer;
/* The depth buffer that is attached to viewFramebuffer, if it exists. */
GLuint depthRenderbuffer;
/* format of depthRenderbuffer */
GLenum depthBufferFormat;
}
@synthesize context; @synthesize context;
@synthesize backingWidth;
@synthesize backingHeight;
+ (Class)layerClass + (Class)layerClass
{ {
return [CAEAGLLayer class]; return [CAEAGLLayer class];
} }
- (id)initWithFrame:(CGRect)frame - (instancetype)initWithFrame:(CGRect)frame
scale:(CGFloat)scale scale:(CGFloat)scale
retainBacking:(BOOL)retained retainBacking:(BOOL)retained
rBits:(int)rBits rBits:(int)rBits
gBits:(int)gBits gBits:(int)gBits
bBits:(int)bBits bBits:(int)bBits
aBits:(int)aBits aBits:(int)aBits
depthBits:(int)depthBits depthBits:(int)depthBits
stencilBits:(int)stencilBits stencilBits:(int)stencilBits
majorVersion:(int)majorVersion sRGB:(BOOL)sRGB
shareGroup:(EAGLSharegroup*)shareGroup majorVersion:(int)majorVersion
shareGroup:(EAGLSharegroup*)shareGroup
{ {
depthBufferFormat = 0;
if ((self = [super initWithFrame:frame])) { if ((self = [super initWithFrame:frame])) {
const BOOL useStencilBuffer = (stencilBits != 0); const BOOL useStencilBuffer = (stencilBits != 0);
const BOOL useDepthBuffer = (depthBits != 0); const BOOL useDepthBuffer = (depthBits != 0);
NSString *colorFormat = nil; NSString *colorFormat = nil;
/* The EAGLRenderingAPI enum values currently map 1:1 to major GLES /* The EAGLRenderingAPI enum values currently map 1:1 to major GLES
versions, and this allows us to handle future OpenGL ES versions. * versions, and this allows us to handle future OpenGL ES versions. */
*/
EAGLRenderingAPI api = majorVersion; EAGLRenderingAPI api = majorVersion;
if (rBits == 8 && gBits == 8 && bBits == 8) { context = [[SDLEAGLContext alloc] initWithAPI:api sharegroup:shareGroup];
/* if user specifically requests rbg888 or some color format higher than 16bpp */
colorFormat = kEAGLColorFormatRGBA8;
} else {
/* default case (faster) */
colorFormat = kEAGLColorFormatRGB565;
}
/* Get the layer */
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]) { if (!context || ![EAGLContext setCurrentContext:context]) {
[self release];
SDL_SetError("OpenGL ES %d not supported", majorVersion); SDL_SetError("OpenGL ES %d not supported", majorVersion);
return nil; return nil;
} }
context.sdlView = self;
if (sRGB) {
/* sRGB EAGL drawable support was added in iOS 7. */
if (UIKit_IsSystemVersionAtLeast(7.0)) {
colorFormat = kEAGLColorFormatSRGBA8;
} else {
SDL_SetError("sRGB drawables are not supported.");
return nil;
}
} else if (rBits >= 8 || gBits >= 8 || bBits >= 8) {
/* if user specifically requests rbg888 or some color format higher than 16bpp */
colorFormat = kEAGLColorFormatRGBA8;
} else {
/* default case (potentially faster) */
colorFormat = kEAGLColorFormatRGB565;
}
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
eaglLayer.opaque = YES;
eaglLayer.drawableProperties = @{
kEAGLDrawablePropertyRetainedBacking:@(retained),
kEAGLDrawablePropertyColorFormat:colorFormat
};
/* Set the appropriate scale (for retina display support) */ /* Set the appropriate scale (for retina display support) */
self.contentScaleFactor = scale; self.contentScaleFactor = scale;
/* create the buffers */ /* Create the color Renderbuffer Object */
glGenFramebuffersOES(1, &viewFramebuffer); glGenRenderbuffers(1, &viewRenderbuffer);
glGenRenderbuffersOES(1, &viewRenderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); if (![context renderbufferStorage:GL_RENDERBUFFER fromDrawable:eaglLayer]) {
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); SDL_SetError("Failed to create OpenGL ES drawable");
[context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer]; return nil;
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer); }
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth); /* Create the Framebuffer Object */
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight); glGenFramebuffers(1, &viewFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, viewFramebuffer);
/* attach the color renderbuffer to the FBO */
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, viewRenderbuffer);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
if ((useDepthBuffer) || (useStencilBuffer)) { if ((useDepthBuffer) || (useStencilBuffer)) {
if (useStencilBuffer) { if (useStencilBuffer) {
/* Apparently you need to pack stencil and depth into one buffer. */ /* Apparently you need to pack stencil and depth into one buffer. */
depthBufferFormat = GL_DEPTH24_STENCIL8_OES; depthBufferFormat = GL_DEPTH24_STENCIL8_OES;
} else if (useDepthBuffer) { } else if (useDepthBuffer) {
/* iOS only has 24-bit depth buffers, even with GL_DEPTH_COMPONENT16_OES */ /* iOS only uses 32-bit float (exposed as fixed point 24-bit)
* depth buffers. */
depthBufferFormat = GL_DEPTH_COMPONENT24_OES; depthBufferFormat = GL_DEPTH_COMPONENT24_OES;
} }
glGenRenderbuffersOES(1, &depthRenderbuffer); glGenRenderbuffers(1, &depthRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
glRenderbufferStorageOES(GL_RENDERBUFFER_OES, depthBufferFormat, backingWidth, backingHeight); glRenderbufferStorage(GL_RENDERBUFFER, depthBufferFormat, backingWidth, backingHeight);
if (useDepthBuffer) { if (useDepthBuffer) {
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
} }
if (useStencilBuffer) { if (useStencilBuffer) {
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_STENCIL_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
} }
} }
if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) { if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
return NO; SDL_SetError("Failed creating OpenGL ES framebuffer");
return nil;
} }
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
/* end create buffers */
self.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); [self setDebugLabels];
self.autoresizesSubviews = YES;
} }
return self; return self;
} }
- (GLuint)drawableRenderbuffer
{
return viewRenderbuffer;
}
- (GLuint)drawableFramebuffer
{
return viewFramebuffer;
}
- (void)updateFrame - (void)updateFrame
{ {
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); GLint prevRenderbuffer = 0;
glBindRenderbufferOES(GL_RENDERBUFFER_OES, 0); glGetIntegerv(GL_RENDERBUFFER_BINDING, &prevRenderbuffer);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, 0);
glDeleteRenderbuffersOES(1, &viewRenderbuffer);
glGenRenderbuffersOES(1, &viewRenderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
[context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer];
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth); glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight); glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
if (depthRenderbuffer != 0) { if (depthRenderbuffer != 0) {
glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
glRenderbufferStorageOES(GL_RENDERBUFFER_OES, depthBufferFormat, backingWidth, backingHeight); glRenderbufferStorage(GL_RENDERBUFFER, depthBufferFormat, backingWidth, backingHeight);
} }
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); glBindRenderbuffer(GL_RENDERBUFFER, prevRenderbuffer);
} }
- (void)setAnimationCallback:(int)interval - (void)setDebugLabels
callback:(void (*)(void*))callback
callbackParam:(void*)callbackParam
{ {
[self stopAnimation]; if (viewFramebuffer != 0) {
glLabelObjectEXT(GL_FRAMEBUFFER, viewFramebuffer, 0, "context FBO");
}
animationInterval = interval; if (viewRenderbuffer != 0) {
animationCallback = callback; glLabelObjectEXT(GL_RENDERBUFFER, viewRenderbuffer, 0, "context color buffer");
animationCallbackParam = callbackParam; }
if (animationCallback) if (depthRenderbuffer != 0) {
[self startAnimation]; if (depthBufferFormat == GL_DEPTH24_STENCIL8_OES) {
} glLabelObjectEXT(GL_RENDERBUFFER, depthRenderbuffer, 0, "context depth-stencil buffer");
} else {
- (void)startAnimation glLabelObjectEXT(GL_RENDERBUFFER, depthRenderbuffer, 0, "context depth buffer");
{ }
displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(doLoop:)];
[displayLink setFrameInterval:animationInterval];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)stopAnimation
{
[displayLink invalidate];
displayLink = nil;
}
- (void)doLoop:(CADisplayLink*)sender
{
/* Don't run the game loop while a messagebox is up */
if (!UIKit_ShowingMessageBox()) {
animationCallback(animationCallbackParam);
} }
} }
@ -194,44 +214,60 @@
[EAGLContext setCurrentContext:context]; [EAGLContext setCurrentContext:context];
} }
- (void)swapBuffers - (void)swapBuffers
{ {
/* viewRenderbuffer should always be bound here. Code that binds something /* viewRenderbuffer should always be bound here. Code that binds something
else is responsible for rebinding viewRenderbuffer, to reduce * else is responsible for rebinding viewRenderbuffer, to reduce duplicate
duplicate state changes. */ * state changes. */
[context presentRenderbuffer:GL_RENDERBUFFER_OES]; [context presentRenderbuffer:GL_RENDERBUFFER];
} }
- (void)layoutSubviews - (void)layoutSubviews
{ {
[EAGLContext setCurrentContext:context]; [super layoutSubviews];
[self updateFrame];
int width = (int) (self.bounds.size.width * self.contentScaleFactor);
int height = (int) (self.bounds.size.height * self.contentScaleFactor);
/* Update the color and depth buffer storage if the layer size has changed. */
if (width != backingWidth || height != backingHeight) {
EAGLContext *prevContext = [EAGLContext currentContext];
if (prevContext != context) {
[EAGLContext setCurrentContext:context];
}
[self updateFrame];
if (prevContext != context) {
[EAGLContext setCurrentContext:prevContext];
}
}
} }
- (void)destroyFramebuffer - (void)destroyFramebuffer
{ {
glDeleteFramebuffersOES(1, &viewFramebuffer); if (viewFramebuffer != 0) {
viewFramebuffer = 0; glDeleteFramebuffers(1, &viewFramebuffer);
glDeleteRenderbuffersOES(1, &viewRenderbuffer); viewFramebuffer = 0;
viewRenderbuffer = 0; }
if (depthRenderbuffer) { if (viewRenderbuffer != 0) {
glDeleteRenderbuffersOES(1, &depthRenderbuffer); glDeleteRenderbuffers(1, &viewRenderbuffer);
viewRenderbuffer = 0;
}
if (depthRenderbuffer != 0) {
glDeleteRenderbuffers(1, &depthRenderbuffer);
depthRenderbuffer = 0; depthRenderbuffer = 0;
} }
} }
- (void)dealloc - (void)dealloc
{ {
[self destroyFramebuffer];
if ([EAGLContext currentContext] == context) { if ([EAGLContext currentContext] == context) {
[self destroyFramebuffer];
[EAGLContext setCurrentContext:nil]; [EAGLContext setCurrentContext:nil];
} }
[context release];
[super dealloc];
} }
@end @end

View File

@ -25,20 +25,8 @@
#include "../SDL_sysvideo.h" #include "../SDL_sysvideo.h"
#ifndef __IPHONE_6_0 BOOL UIKit_IsSystemVersionAtLeast(double version);
/* 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 */ CGRect UIKit_ComputeViewFrame(SDL_Window *window, UIScreen *screen);
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 */
#endif /* _SDL_uikitvideo_h */ #endif /* _SDL_uikitvideo_h */

View File

@ -75,15 +75,15 @@ UIKit_CreateDevice(int devindex)
device->SetDisplayMode = UIKit_SetDisplayMode; device->SetDisplayMode = UIKit_SetDisplayMode;
device->PumpEvents = UIKit_PumpEvents; device->PumpEvents = UIKit_PumpEvents;
device->CreateWindow = UIKit_CreateWindow; device->CreateWindow = UIKit_CreateWindow;
device->SetWindowTitle = UIKit_SetWindowTitle;
device->ShowWindow = UIKit_ShowWindow; device->ShowWindow = UIKit_ShowWindow;
device->HideWindow = UIKit_HideWindow; device->HideWindow = UIKit_HideWindow;
device->RaiseWindow = UIKit_RaiseWindow; device->RaiseWindow = UIKit_RaiseWindow;
device->SetWindowBordered = UIKit_SetWindowBordered;
device->SetWindowFullscreen = UIKit_SetWindowFullscreen; device->SetWindowFullscreen = UIKit_SetWindowFullscreen;
device->DestroyWindow = UIKit_DestroyWindow; device->DestroyWindow = UIKit_DestroyWindow;
device->GetWindowWMInfo = UIKit_GetWindowWMInfo; device->GetWindowWMInfo = UIKit_GetWindowWMInfo;
/* !!! FIXME: implement SetWindowBordered */
#if SDL_IPHONE_KEYBOARD #if SDL_IPHONE_KEYBOARD
device->HasScreenKeyboardSupport = UIKit_HasScreenKeyboardSupport; device->HasScreenKeyboardSupport = UIKit_HasScreenKeyboardSupport;
device->ShowScreenKeyboard = UIKit_ShowScreenKeyboard; device->ShowScreenKeyboard = UIKit_ShowScreenKeyboard;
@ -93,12 +93,13 @@ UIKit_CreateDevice(int devindex)
#endif #endif
/* OpenGL (ES) functions */ /* OpenGL (ES) functions */
device->GL_MakeCurrent = UIKit_GL_MakeCurrent; device->GL_MakeCurrent = UIKit_GL_MakeCurrent;
device->GL_SwapWindow = UIKit_GL_SwapWindow; device->GL_GetDrawableSize = UIKit_GL_GetDrawableSize;
device->GL_SwapWindow = UIKit_GL_SwapWindow;
device->GL_CreateContext = UIKit_GL_CreateContext; device->GL_CreateContext = UIKit_GL_CreateContext;
device->GL_DeleteContext = UIKit_GL_DeleteContext; device->GL_DeleteContext = UIKit_GL_DeleteContext;
device->GL_GetProcAddress = UIKit_GL_GetProcAddress; device->GL_GetProcAddress = UIKit_GL_GetProcAddress;
device->GL_LoadLibrary = UIKit_GL_LoadLibrary; device->GL_LoadLibrary = UIKit_GL_LoadLibrary;
device->free = UIKit_DeleteDevice; device->free = UIKit_DeleteDevice;
device->gl_config.accelerated = 1; device->gl_config.accelerated = 1;
@ -129,6 +130,25 @@ UIKit_VideoQuit(_THIS)
UIKit_QuitModes(_this); UIKit_QuitModes(_this);
} }
BOOL
UIKit_IsSystemVersionAtLeast(double version)
{
return [[UIDevice currentDevice].systemVersion doubleValue] >= version;
}
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. * iOS log support.
* *

View File

@ -20,59 +20,22 @@
*/ */
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import "SDL_uikitviewcontroller.h"
#include "../SDL_sysvideo.h"
#include "SDL_touch.h" #include "SDL_touch.h"
#define IPHONE_TOUCH_EFFICIENT_DANGEROUS @interface SDL_uikitview : UIView
#ifndef IPHONE_TOUCH_EFFICIENT_DANGEROUS - (instancetype)initWithFrame:(CGRect)frame;
#define MAX_SIMULTANEOUS_TOUCHES 5
#endif
#if SDL_IPHONE_KEYBOARD - (void)setSDLWindow:(SDL_Window *)window;
@interface SDL_uikitview : UIView<UITextFieldDelegate> {
#else
@interface SDL_uikitview : UIView {
#endif
SDL_TouchID touchId;
UITouch *leftFingerDown;
#ifndef IPHONE_TOUCH_EFFICIENT_DANGEROUS
UITouch *finger[MAX_SIMULTANEOUS_TOUCHES];
#endif
#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; - (CGPoint)touchLocation:(UITouch *)touch shouldNormalize:(BOOL)normalize;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
#if SDL_IPHONE_KEYBOARD
- (void)showKeyboard;
- (void)hideKeyboard;
- (void)initializeKeyboard;
@property (readonly) 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);
void UIKit_HideScreenKeyboard(_THIS, SDL_Window *window);
SDL_bool UIKit_IsScreenKeyboardShown(_THIS, SDL_Window *window);
void UIKit_SetTextInputRect(_THIS, SDL_Rect *rect);
#endif
@end @end
/* vi: set ts=4 sw=4 expandtab: */ /* vi: set ts=4 sw=4 expandtab: */

View File

@ -24,438 +24,167 @@
#include "SDL_uikitview.h" #include "SDL_uikitview.h"
#include "../../events/SDL_keyboard_c.h"
#include "../../events/SDL_mouse_c.h" #include "../../events/SDL_mouse_c.h"
#include "../../events/SDL_touch_c.h" #include "../../events/SDL_touch_c.h"
#include "../../events/SDL_events_c.h"
#if SDL_IPHONE_KEYBOARD #import "SDL_uikitappdelegate.h"
#include "keyinfotable.h" #import "SDL_uikitmodes.h"
#endif #import "SDL_uikitwindow.h"
#include "SDL_uikitappdelegate.h"
#include "SDL_uikitmodes.h"
#include "SDL_uikitwindow.h"
void _uikit_keyboard_init() ; @implementation SDL_uikitview {
SDL_Window *sdlwindow;
@implementation SDL_uikitview SDL_TouchID touchId;
UITouch * __weak firstFingerDown;
- (void)dealloc
{
[super dealloc];
} }
- (id)initWithFrame:(CGRect)frame - (instancetype)initWithFrame:(CGRect)frame
{ {
self = [super initWithFrame: frame]; if ((self = [super initWithFrame:frame])) {
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.autoresizesSubviews = YES;
#if SDL_IPHONE_KEYBOARD self.multipleTouchEnabled = YES;
[self initializeKeyboard];
#endif
self.multipleTouchEnabled = YES; touchId = 1;
SDL_AddTouch(touchId, "");
touchId = 1; }
SDL_AddTouch(touchId, "");
return self; return self;
}
- (void)setSDLWindow:(SDL_Window *)window
{
SDL_WindowData *data = nil;
if (window == sdlwindow) {
return;
}
if (sdlwindow) {
SDL_uikitview *view = nil;
data = (__bridge SDL_WindowData *) sdlwindow->driverdata;
[data.views removeObject:self];
[self removeFromSuperview];
/* Restore the next-oldest view in the old window. */
if (data.views.count > 0) {
view = data.views[data.views.count - 1];
}
data.viewcontroller.view = view;
if (data.uiwindow.rootViewController != data.viewcontroller) {
data.uiwindow.rootViewController = data.viewcontroller;
} else if (view) {
[data.uiwindow addSubview:view];
}
[data.uiwindow layoutIfNeeded];
}
if (window) {
data = (__bridge SDL_WindowData *) window->driverdata;
/* Make sure the SDL window has a strong reference to this view. */
[data.views addObject:self];
/* Replace the view controller's old view with this one. */
[data.viewcontroller.view removeFromSuperview];
data.viewcontroller.view = self;
if (data.uiwindow.rootViewController != data.viewcontroller) {
/* The root view controller handles rotation and the status bar.
* Assigning it also adds the controller's view to the window. */
data.uiwindow.rootViewController = data.viewcontroller;
} else {
[data.uiwindow addSubview:self];
}
/* The view's bounds may not be correct until the next event cycle. That
* might happen after the current dimensions are queried, so we force a
* layout now to immediately update the bounds. */
[data.uiwindow layoutIfNeeded];
}
sdlwindow = window;
} }
- (CGPoint)touchLocation:(UITouch *)touch shouldNormalize:(BOOL)normalize - (CGPoint)touchLocation:(UITouch *)touch shouldNormalize:(BOOL)normalize
{ {
CGPoint point = [touch locationInView: self]; 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) { if (normalize) {
CGRect bounds = [self bounds]; CGRect bounds = self.bounds;
point.x /= bounds.size.width; point.x /= bounds.size.width;
point.y /= bounds.size.height; point.y /= bounds.size.height;
} else {
point.x *= displaymodedata->scale;
point.y *= displaymodedata->scale;
} }
return point; return point;
} }
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{ {
NSEnumerator *enumerator = [touches objectEnumerator]; for (UITouch *touch in touches) {
UITouch *touch = (UITouch*)[enumerator nextObject]; if (!firstFingerDown) {
while (touch) {
if (!leftFingerDown) {
CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO]; CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO];
/* send moved event */ /* send mouse moved event */
SDL_SendMouseMotion(self->viewcontroller.window, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y); SDL_SendMouseMotion(sdlwindow, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y);
/* send mouse down event */ /* send mouse down event */
SDL_SendMouseButton(self->viewcontroller.window, SDL_TOUCH_MOUSEID, SDL_PRESSED, SDL_BUTTON_LEFT); SDL_SendMouseButton(sdlwindow, SDL_TOUCH_MOUSEID, SDL_PRESSED, SDL_BUTTON_LEFT);
leftFingerDown = touch; firstFingerDown = touch;
} }
CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES]; CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
#ifdef IPHONE_TOUCH_EFFICIENT_DANGEROUS
/* FIXME: TODO: Using touch as the fingerId is potentially dangerous
* It is also much more efficient than storing the UITouch pointer
* and comparing it to the incoming event.
*/
SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch), SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
SDL_TRUE, locationInView.x, locationInView.y, 1.0f); SDL_TRUE, locationInView.x, locationInView.y, 1.0f);
#else
int i;
for(i = 0; i < MAX_SIMULTANEOUS_TOUCHES; i++) {
if (finger[i] == NULL) {
finger[i] = touch;
SDL_SendTouch(touchId, i,
SDL_TRUE, locationInView.x, locationInView.y, 1.0f);
break;
}
}
#endif
touch = (UITouch*)[enumerator nextObject];
} }
} }
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{ {
NSEnumerator *enumerator = [touches objectEnumerator]; for (UITouch *touch in touches) {
UITouch *touch = (UITouch*)[enumerator nextObject]; if (touch == firstFingerDown) {
while(touch) {
if (touch == leftFingerDown) {
/* send mouse up */ /* send mouse up */
SDL_SendMouseButton(self->viewcontroller.window, SDL_TOUCH_MOUSEID, SDL_RELEASED, SDL_BUTTON_LEFT); SDL_SendMouseButton(sdlwindow, SDL_TOUCH_MOUSEID, SDL_RELEASED, SDL_BUTTON_LEFT);
leftFingerDown = nil; firstFingerDown = nil;
} }
CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES]; CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
#ifdef IPHONE_TOUCH_EFFICIENT_DANGEROUS SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
SDL_SendTouch(touchId, (long)touch,
SDL_FALSE, locationInView.x, locationInView.y, 1.0f); SDL_FALSE, locationInView.x, locationInView.y, 1.0f);
#else
int i;
for (i = 0; i < MAX_SIMULTANEOUS_TOUCHES; i++) {
if (finger[i] == touch) {
SDL_SendTouch(touchId, i,
SDL_FALSE, locationInView.x, locationInView.y, 1.0f);
finger[i] = NULL;
break;
}
}
#endif
touch = (UITouch*)[enumerator nextObject];
} }
} }
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{ {
/* [self touchesEnded:touches withEvent:event];
this can happen if the user puts more than 5 touches on the screen
at once, or perhaps in other circumstances. Usually (it seems)
all active touches are canceled.
*/
[self touchesEnded: touches withEvent: event];
} }
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{ {
NSEnumerator *enumerator = [touches objectEnumerator]; for (UITouch *touch in touches) {
UITouch *touch = (UITouch*)[enumerator nextObject]; if (touch == firstFingerDown) {
while (touch) {
if (touch == leftFingerDown) {
CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO]; CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO];
/* send moved event */ /* send moved event */
SDL_SendMouseMotion(self->viewcontroller.window, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y); SDL_SendMouseMotion(sdlwindow, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y);
} }
CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES]; CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
#ifdef IPHONE_TOUCH_EFFICIENT_DANGEROUS SDL_SendTouchMotion(touchId, (SDL_FingerID)((size_t)touch),
SDL_SendTouchMotion(touchId, (long)touch,
locationInView.x, locationInView.y, 1.0f); locationInView.x, locationInView.y, 1.0f);
#else
int i;
for (i = 0; i < MAX_SIMULTANEOUS_TOUCHES; i++) {
if (finger[i] == touch) {
SDL_SendTouchMotion(touchId, i,
locationInView.x, locationInView.y, 1.0f);
break;
}
}
#endif
touch = (UITouch*)[enumerator nextObject];
} }
} }
/*
---- Keyboard related functionality below this line ----
*/
#if SDL_IPHONE_KEYBOARD
@synthesize textInputRect = textInputRect;
@synthesize keyboardHeight = keyboardHeight;
/* Is the iPhone virtual keyboard visible onscreen? */
- (BOOL)keyboardVisible
{
return keyboardVisible;
}
/* Set ourselves up as a UITextFieldDelegate */
- (void)initializeKeyboard
{
textField = [[UITextField alloc] initWithFrame: CGRectZero];
textField.delegate = self;
/* placeholder so there is something to delete! */
textField.text = @" ";
/* set UITextInputTrait properties, mostly to defaults */
textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
textField.autocorrectionType = UITextAutocorrectionTypeNo;
textField.enablesReturnKeyAutomatically = NO;
textField.keyboardAppearance = UIKeyboardAppearanceDefault;
textField.keyboardType = UIKeyboardTypeDefault;
textField.returnKeyType = UIReturnKeyDefault;
textField.secureTextEntry = NO;
textField.hidden = YES;
keyboardVisible = NO;
/* add the UITextField (hidden) to our view */
[self addSubview: textField];
[textField release];
_uikit_keyboard_init();
}
/* reveal onscreen virtual keyboard */
- (void)showKeyboard
{
keyboardVisible = YES;
[textField becomeFirstResponder];
}
/* hide onscreen virtual keyboard */
- (void)hideKeyboard
{
keyboardVisible = NO;
[textField resignFirstResponder];
}
/* UITextFieldDelegate method. Invoked when user types something. */
- (BOOL)textField:(UITextField *)_textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if ([string length] == 0) {
/* it wants to replace text with nothing, ie a delete */
SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_BACKSPACE);
SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_BACKSPACE);
}
else {
/* go through all the characters in the string we've been sent
and convert them to key presses */
int i;
for (i = 0; i < [string length]; i++) {
unichar c = [string characterAtIndex: i];
Uint16 mod = 0;
SDL_Scancode code;
if (c < 127) {
/* figure out the SDL_Scancode and SDL_keymod for this unichar */
code = unicharToUIKeyInfoTable[c].code;
mod = unicharToUIKeyInfoTable[c].mod;
}
else {
/* we only deal with ASCII right now */
code = SDL_SCANCODE_UNKNOWN;
mod = 0;
}
if (mod & KMOD_SHIFT) {
/* If character uses shift, press shift down */
SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_LSHIFT);
}
/* send a keydown and keyup even for the character */
SDL_SendKeyboardKey(SDL_PRESSED, code);
SDL_SendKeyboardKey(SDL_RELEASED, code);
if (mod & KMOD_SHIFT) {
/* If character uses shift, press shift back up */
SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LSHIFT);
}
}
SDL_SendKeyboardText([string UTF8String]);
}
return NO; /* don't allow the edit! (keep placeholder text there) */
}
/* Terminates the editing session */
- (BOOL)textFieldShouldReturn:(UITextField*)_textField
{
SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_RETURN);
SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RETURN);
SDL_StopTextInput();
return YES;
}
#endif
@end @end
/* iPhone keyboard addition functions */
#if SDL_IPHONE_KEYBOARD
static SDL_uikitview * getWindowView(SDL_Window * window)
{
if (window == NULL) {
SDL_SetError("Window does not exist");
return nil;
}
SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
SDL_uikitview *view = data != NULL ? data->view : nil;
if (view == nil) {
SDL_SetError("Window has no view");
}
return view;
}
SDL_bool UIKit_HasScreenKeyboardSupport(_THIS)
{
return SDL_TRUE;
}
void UIKit_ShowScreenKeyboard(_THIS, SDL_Window *window)
{
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];
}
}
SDL_bool UIKit_IsScreenKeyboardShown(_THIS, SDL_Window *window)
{
SDL_uikitview *view = getWindowView(window);
if (view == nil) {
return 0;
}
return view.keyboardVisible;
}
void _uikit_keyboard_update() {
SDL_Window *window = SDL_GetFocusWindow();
if (!window) { return; }
SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
if (!data) { return; }
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);
int bottom = (r.y + r.h);
int kbottom = sh - height;
if (kbottom < bottom) {
offsety = kbottom-bottom;
}
}
UIInterfaceOrientation ui_orient = [[UIApplication sharedApplication] statusBarOrientation];
if (ui_orient == UIInterfaceOrientationLandscapeLeft) {
int tmp = offsetx; offsetx = offsety; offsety = tmp;
}
if (ui_orient == UIInterfaceOrientationLandscapeRight) {
offsety = -offsety;
int tmp = offsetx; offsetx = offsety; offsety = tmp;
}
if (ui_orient == UIInterfaceOrientationPortraitUpsideDown) {
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 ;
}
view.keyboardHeight = height;
_uikit_keyboard_update();
}
void _uikit_keyboard_init() {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
NSOperationQueue *queue = [NSOperationQueue mainQueue];
[center addObserverForName:UIKeyboardWillShowNotification
object:nil
queue:queue
usingBlock:^(NSNotification *notification) {
int height = 0;
CGSize keyboardSize = [[[notification userInfo] objectForKey: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);
}
];
[center addObserverForName:UIKeyboardDidHideNotification
object:nil
queue:queue
usingBlock:^(NSNotification *notification) {
_uikit_keyboard_set_height(0);
}
];
}
void
UIKit_SetTextInputRect(_THIS, SDL_Rect *rect)
{
if (!rect) {
SDL_InvalidParamError("rect");
return;
}
SDL_uikitview *view = getWindowView(SDL_GetFocusWindow());
if (view == nil) {
return ;
}
view.textInputRect = *rect;
}
#endif /* SDL_IPHONE_KEYBOARD */
#endif /* SDL_VIDEO_DRIVER_UIKIT */ #endif /* SDL_VIDEO_DRIVER_UIKIT */
/* vi: set ts=4 sw=4 expandtab: */ /* vi: set ts=4 sw=4 expandtab: */

View File

@ -23,18 +23,56 @@
#include "../SDL_sysvideo.h" #include "../SDL_sysvideo.h"
@interface SDL_uikitviewcontroller : UIViewController { #include "SDL_touch.h"
@private
SDL_Window *window;
}
@property (readwrite) SDL_Window *window; #if SDL_IPHONE_KEYBOARD
@interface SDL_uikitviewcontroller : UIViewController <UITextFieldDelegate>
#else
@interface SDL_uikitviewcontroller : UIViewController
#endif
@property (nonatomic, assign) SDL_Window *window;
- (instancetype)initWithSDLWindow:(SDL_Window *)_window;
- (void)setAnimationCallback:(int)interval
callback:(void (*)(void*))callback
callbackParam:(void*)callbackParam;
- (void)startAnimation;
- (void)stopAnimation;
- (void)doLoop:(CADisplayLink*)sender;
- (id)initWithSDLWindow:(SDL_Window *)_window;
- (void)loadView; - (void)loadView;
- (void)viewDidLayoutSubviews; - (void)viewDidLayoutSubviews;
- (NSUInteger)supportedInterfaceOrientations; - (NSUInteger)supportedInterfaceOrientations;
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orient; - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orient;
- (BOOL)prefersStatusBarHidden; - (BOOL)prefersStatusBarHidden;
- (UIStatusBarStyle)preferredStatusBarStyle;
#if SDL_IPHONE_KEYBOARD
- (void)showKeyboard;
- (void)hideKeyboard;
- (void)initKeyboard;
- (void)deinitKeyboard;
- (void)keyboardWillShow:(NSNotification *)notification;
- (void)keyboardWillHide:(NSNotification *)notification;
- (void)updateKeyboard;
@property (nonatomic, assign, getter=isKeyboardVisible) BOOL keyboardVisible;
@property (nonatomic, assign) SDL_Rect textInputRect;
@property (nonatomic, assign) int keyboardHeight;
#endif
@end @end
#if SDL_IPHONE_KEYBOARD
SDL_bool UIKit_HasScreenKeyboardSupport(_THIS);
void UIKit_ShowScreenKeyboard(_THIS, SDL_Window *window);
void UIKit_HideScreenKeyboard(_THIS, SDL_Window *window);
SDL_bool UIKit_IsScreenKeyboardShown(_THIS, SDL_Window *window);
void UIKit_SetTextInputRect(_THIS, SDL_Rect *rect);
#endif

View File

@ -28,108 +28,389 @@
#include "../SDL_sysvideo.h" #include "../SDL_sysvideo.h"
#include "../../events/SDL_events_c.h" #include "../../events/SDL_events_c.h"
#include "SDL_uikitviewcontroller.h" #import "SDL_uikitviewcontroller.h"
#import "SDL_uikitmessagebox.h"
#include "SDL_uikitvideo.h" #include "SDL_uikitvideo.h"
#include "SDL_uikitmodes.h" #include "SDL_uikitmodes.h"
#include "SDL_uikitwindow.h" #include "SDL_uikitwindow.h"
#if SDL_IPHONE_KEYBOARD
#include "keyinfotable.h"
#endif
@implementation SDL_uikitviewcontroller @implementation SDL_uikitviewcontroller {
CADisplayLink *displayLink;
int animationInterval;
void (*animationCallback)(void*);
void *animationCallbackParam;
#if SDL_IPHONE_KEYBOARD
UITextField *textField;
#endif
}
@synthesize window; @synthesize window;
- (id)initWithSDLWindow:(SDL_Window *)_window - (instancetype)initWithSDLWindow:(SDL_Window *)_window
{ {
self = [self init]; if (self = [super initWithNibName:nil bundle:nil]) {
if (self == nil) { self.window = _window;
return nil;
}
self.window = _window;
#if SDL_IPHONE_KEYBOARD
[self initKeyboard];
#endif
}
return self; return self;
} }
- (void)dealloc
{
#if SDL_IPHONE_KEYBOARD
[self deinitKeyboard];
#endif
}
- (void)setAnimationCallback:(int)interval
callback:(void (*)(void*))callback
callbackParam:(void*)callbackParam
{
[self stopAnimation];
animationInterval = interval;
animationCallback = callback;
animationCallbackParam = callbackParam;
if (animationCallback) {
[self startAnimation];
}
}
- (void)startAnimation
{
displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(doLoop:)];
[displayLink setFrameInterval:animationInterval];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)stopAnimation
{
[displayLink invalidate];
displayLink = nil;
}
- (void)doLoop:(CADisplayLink*)sender
{
/* Don't run the game loop while a messagebox is up */
if (!UIKit_ShowingMessageBox()) {
animationCallback(animationCallbackParam);
}
}
- (void)loadView - (void)loadView
{ {
/* do nothing. */ /* Do nothing. */
} }
- (void)viewDidLayoutSubviews - (void)viewDidLayoutSubviews
{ {
if (self->window->flags & SDL_WINDOW_RESIZABLE) { const CGSize size = self.view.bounds.size;
SDL_WindowData *data = self->window->driverdata; int w = (int) size.width;
SDL_VideoDisplay *display = SDL_GetDisplayForWindow(self->window); int h = (int) size.height;
SDL_DisplayModeData *displaymodedata = (SDL_DisplayModeData *) display->current_mode.driverdata;
const CGSize size = data->view.bounds.size;
int w, h;
w = (int)(size.width * displaymodedata->scale); SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, w, h);
h = (int)(size.height * displaymodedata->scale);
SDL_SendWindowEvent(self->window, SDL_WINDOWEVENT_RESIZED, w, h);
}
} }
- (NSUInteger)supportedInterfaceOrientations - (NSUInteger)supportedInterfaceOrientations
{ {
NSUInteger orientationMask = 0; return UIKit_GetSupportedOrientations(window);
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 ([orientations containsObject:@"LandscapeLeft"]) {
orientationMask |= UIInterfaceOrientationMaskLandscapeLeft;
}
if ([orientations containsObject:@"LandscapeRight"]) {
orientationMask |= UIInterfaceOrientationMaskLandscapeRight;
}
if ([orientations containsObject:@"Portrait"]) {
orientationMask |= UIInterfaceOrientationMaskPortrait;
}
if ([orientations containsObject:@"PortraitUpsideDown"]) {
orientationMask |= UIInterfaceOrientationMaskPortraitUpsideDown;
}
} else if (self->window->flags & SDL_WINDOW_RESIZABLE) {
orientationMask = UIInterfaceOrientationMaskAll; /* any orientation is okay. */
} else {
if (self->window->w >= self->window->h) {
orientationMask |= UIInterfaceOrientationMaskLandscape;
}
if (self->window->h >= self->window->w) {
orientationMask |= (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown);
}
}
/* Don't allow upside-down orientation on the phone, so answering calls is in the natural orientation */
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
orientationMask &= ~UIInterfaceOrientationMaskPortraitUpsideDown;
}
return orientationMask;
} }
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orient - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orient
{ {
NSUInteger orientationMask = [self supportedInterfaceOrientations]; return ([self supportedInterfaceOrientations] & (1 << orient)) != 0;
return (orientationMask & (1 << orient));
} }
- (BOOL)prefersStatusBarHidden - (BOOL)prefersStatusBarHidden
{ {
if (self->window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_BORDERLESS)) { return (window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_BORDERLESS)) != 0;
return YES; }
} else {
return NO; - (UIStatusBarStyle)preferredStatusBarStyle
{
/* We assume most SDL apps don't have a bright white background. */
return UIStatusBarStyleLightContent;
}
/*
---- Keyboard related functionality below this line ----
*/
#if SDL_IPHONE_KEYBOARD
@synthesize textInputRect;
@synthesize keyboardHeight;
@synthesize keyboardVisible;
/* Set ourselves up as a UITextFieldDelegate */
- (void)initKeyboard
{
textField = [[UITextField alloc] initWithFrame:CGRectZero];
textField.delegate = self;
/* placeholder so there is something to delete! */
textField.text = @" ";
/* set UITextInputTrait properties, mostly to defaults */
textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
textField.autocorrectionType = UITextAutocorrectionTypeNo;
textField.enablesReturnKeyAutomatically = NO;
textField.keyboardAppearance = UIKeyboardAppearanceDefault;
textField.keyboardType = UIKeyboardTypeDefault;
textField.returnKeyType = UIReturnKeyDefault;
textField.secureTextEntry = NO;
textField.hidden = YES;
keyboardVisible = NO;
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[center addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
- (void)setView:(UIView *)view
{
[super setView:view];
[view addSubview:textField];
if (keyboardVisible) {
[self showKeyboard];
} }
} }
- (void)deinitKeyboard
{
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[center removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
/* reveal onscreen virtual keyboard */
- (void)showKeyboard
{
keyboardVisible = YES;
if (textField.window) {
[textField becomeFirstResponder];
}
}
/* hide onscreen virtual keyboard */
- (void)hideKeyboard
{
keyboardVisible = NO;
[textField resignFirstResponder];
}
- (void)keyboardWillShow:(NSNotification *)notification
{
CGRect kbrect = [[notification userInfo][UIKeyboardFrameBeginUserInfoKey] CGRectValue];
UIView *view = self.view;
int height = 0;
/* The keyboard rect is in the coordinate space of the screen, but we want
* its height in the view's coordinate space. */
#ifdef __IPHONE_8_0
if ([view respondsToSelector:@selector(convertRect:fromCoordinateSpace:)]) {
UIScreen *screen = view.window.screen;
kbrect = [view convertRect:kbrect fromCoordinateSpace:screen.coordinateSpace];
height = kbrect.size.height;
} else
#endif
{
/* In iOS 7 and below, the screen's coordinate space is never rotated. */
if (UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) {
height = kbrect.size.width;
} else {
height = kbrect.size.height;
}
}
[self setKeyboardHeight:height];
}
- (void)keyboardWillHide:(NSNotification *)notification
{
[self setKeyboardHeight:0];
}
- (void)updateKeyboard
{
SDL_Rect textrect = self.textInputRect;
CGAffineTransform t = self.view.transform;
CGPoint offset = CGPointMake(0.0, 0.0);
if (self.keyboardHeight) {
int rectbottom = textrect.y + textrect.h;
int kbottom = self.view.bounds.size.height - self.keyboardHeight;
if (kbottom < rectbottom) {
offset.y = kbottom - rectbottom;
}
}
/* Put the offset into the this view transform's coordinate space. */
t.tx = 0.0;
t.ty = 0.0;
offset = CGPointApplyAffineTransform(offset, t);
t.tx = offset.x;
t.ty = offset.y;
/* Move the view by applying the updated transform. */
self.view.transform = t;
}
- (void)setKeyboardHeight:(int)height
{
keyboardVisible = height > 0;
keyboardHeight = height;
[self updateKeyboard];
}
/* UITextFieldDelegate method. Invoked when user types something. */
- (BOOL)textField:(UITextField *)_textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSUInteger len = string.length;
if (len == 0) {
/* it wants to replace text with nothing, ie a delete */
SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_BACKSPACE);
SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_BACKSPACE);
} else {
/* go through all the characters in the string we've been sent and
* convert them to key presses */
int i;
for (i = 0; i < len; i++) {
unichar c = [string characterAtIndex:i];
Uint16 mod = 0;
SDL_Scancode code;
if (c < 127) {
/* figure out the SDL_Scancode and SDL_keymod for this unichar */
code = unicharToUIKeyInfoTable[c].code;
mod = unicharToUIKeyInfoTable[c].mod;
} else {
/* we only deal with ASCII right now */
code = SDL_SCANCODE_UNKNOWN;
mod = 0;
}
if (mod & KMOD_SHIFT) {
/* If character uses shift, press shift down */
SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_LSHIFT);
}
/* send a keydown and keyup even for the character */
SDL_SendKeyboardKey(SDL_PRESSED, code);
SDL_SendKeyboardKey(SDL_RELEASED, code);
if (mod & KMOD_SHIFT) {
/* If character uses shift, press shift back up */
SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LSHIFT);
}
}
SDL_SendKeyboardText([string UTF8String]);
}
return NO; /* don't allow the edit! (keep placeholder text there) */
}
/* Terminates the editing session */
- (BOOL)textFieldShouldReturn:(UITextField*)_textField
{
SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_RETURN);
SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RETURN);
SDL_StopTextInput();
return YES;
}
#endif
@end @end
/* iPhone keyboard addition functions */
#if SDL_IPHONE_KEYBOARD
static SDL_uikitviewcontroller *
GetWindowViewController(SDL_Window * window)
{
if (!window || !window->driverdata) {
SDL_SetError("Invalid window");
return nil;
}
SDL_WindowData *data = (__bridge SDL_WindowData *)window->driverdata;
return data.viewcontroller;
}
SDL_bool
UIKit_HasScreenKeyboardSupport(_THIS)
{
return SDL_TRUE;
}
void
UIKit_ShowScreenKeyboard(_THIS, SDL_Window *window)
{
@autoreleasepool {
SDL_uikitviewcontroller *vc = GetWindowViewController(window);
[vc showKeyboard];
}
}
void
UIKit_HideScreenKeyboard(_THIS, SDL_Window *window)
{
@autoreleasepool {
SDL_uikitviewcontroller *vc = GetWindowViewController(window);
[vc hideKeyboard];
}
}
SDL_bool
UIKit_IsScreenKeyboardShown(_THIS, SDL_Window *window)
{
@autoreleasepool {
SDL_uikitviewcontroller *vc = GetWindowViewController(window);
if (vc != nil) {
return vc.isKeyboardVisible;
}
return SDL_FALSE;
}
}
void
UIKit_SetTextInputRect(_THIS, SDL_Rect *rect)
{
if (!rect) {
SDL_InvalidParamError("rect");
return;
}
@autoreleasepool {
SDL_uikitviewcontroller *vc = GetWindowViewController(SDL_GetFocusWindow());
if (vc != nil) {
vc.textInputRect = *rect;
if (vc.keyboardVisible) {
[vc updateKeyboard];
}
}
}
}
#endif /* SDL_IPHONE_KEYBOARD */
#endif /* SDL_VIDEO_DRIVER_UIKIT */ #endif /* SDL_VIDEO_DRIVER_UIKIT */
/* vi: set ts=4 sw=4 expandtab: */ /* vi: set ts=4 sw=4 expandtab: */

View File

@ -23,28 +23,33 @@
#include "../SDL_sysvideo.h" #include "../SDL_sysvideo.h"
#import "SDL_uikitvideo.h" #import "SDL_uikitvideo.h"
#import "SDL_uikitopenglview.h" #import "SDL_uikitview.h"
#import "SDL_uikitviewcontroller.h" #import "SDL_uikitviewcontroller.h"
typedef struct SDL_WindowData SDL_WindowData;
extern int UIKit_CreateWindow(_THIS, SDL_Window * window); extern int UIKit_CreateWindow(_THIS, SDL_Window * window);
extern void UIKit_SetWindowTitle(_THIS, SDL_Window * window);
extern void UIKit_ShowWindow(_THIS, SDL_Window * window); extern void UIKit_ShowWindow(_THIS, SDL_Window * window);
extern void UIKit_HideWindow(_THIS, SDL_Window * window); extern void UIKit_HideWindow(_THIS, SDL_Window * window);
extern void UIKit_RaiseWindow(_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_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen);
extern void UIKit_DestroyWindow(_THIS, SDL_Window * window); extern void UIKit_DestroyWindow(_THIS, SDL_Window * window);
extern SDL_bool UIKit_GetWindowWMInfo(_THIS, SDL_Window * window, extern SDL_bool UIKit_GetWindowWMInfo(_THIS, SDL_Window * window,
struct SDL_SysWMinfo * info); struct SDL_SysWMinfo * info);
extern NSUInteger UIKit_GetSupportedOrientations(SDL_Window * window);
@class UIWindow; @class UIWindow;
struct SDL_WindowData @interface SDL_WindowData : NSObject
{
UIWindow *uiwindow; @property (nonatomic, strong) UIWindow *uiwindow;
SDL_uikitopenglview *view; @property (nonatomic, strong) SDL_uikitviewcontroller *viewcontroller;
SDL_uikitviewcontroller *viewcontroller;
}; /* Array of SDL_uikitviews owned by this window. */
@property (nonatomic, copy) NSMutableArray *views;
@end
#endif /* _SDL_uikitwindow_h */ #endif /* _SDL_uikitwindow_h */

View File

@ -37,89 +37,110 @@
#include "SDL_uikitwindow.h" #include "SDL_uikitwindow.h"
#import "SDL_uikitappdelegate.h" #import "SDL_uikitappdelegate.h"
#import "SDL_uikitopenglview.h" #import "SDL_uikitview.h"
#include <Foundation/Foundation.h> #include <Foundation/Foundation.h>
@implementation SDL_WindowData
@synthesize uiwindow;
@synthesize viewcontroller;
@synthesize views;
- (instancetype)init
{
if ((self = [super init])) {
views = [NSMutableArray new];
}
return self;
}
@end
@interface SDL_uikitwindow : UIWindow
- (void)layoutSubviews;
@end
@implementation SDL_uikitwindow
- (void)layoutSubviews
{
/* Workaround to fix window orientation issues in iOS 8+. */
self.frame = self.screen.bounds;
[super layoutSubviews];
}
@end
static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bool created) static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bool created)
{ {
SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
SDL_DisplayModeData *displaymodedata = (SDL_DisplayModeData *) display->current_mode.driverdata; SDL_DisplayData *displaydata = (__bridge SDL_DisplayData *) display->driverdata;
SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata; SDL_uikitview *view;
SDL_WindowData *data;
/* Allocate the window data */ CGRect frame = UIKit_ComputeViewFrame(window, displaydata.uiscreen);
data = (SDL_WindowData *)SDL_malloc(sizeof(*data)); int width = (int) frame.size.width;
int height = (int) frame.size.height;
SDL_WindowData *data = [[SDL_WindowData alloc] init];
if (!data) { if (!data) {
return SDL_OutOfMemory(); return SDL_OutOfMemory();
} }
data->uiwindow = uiwindow;
data->viewcontroller = nil;
data->view = nil;
/* Fill in the SDL window with the window data */ window->driverdata = (void *) CFBridgingRetain(data);
{
window->x = 0;
window->y = 0;
CGRect bounds; data.uiwindow = uiwindow;
if (window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_BORDERLESS)) {
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);
/* Make sure the width/height are oriented correctly */
if (UIKit_IsDisplayLandscape(displaydata->uiscreen) != (width > height)) {
int temp = width;
width = height;
height = temp;
}
window->w = width;
window->h = height;
}
window->driverdata = data;
/* only one window on iOS, always shown */ /* only one window on iOS, always shown */
window->flags &= ~SDL_WINDOW_HIDDEN; window->flags &= ~SDL_WINDOW_HIDDEN;
/* SDL_WINDOW_BORDERLESS controls whether status bar is hidden. if (displaydata.uiscreen == [UIScreen mainScreen]) {
* 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]) {
window->flags |= SDL_WINDOW_INPUT_FOCUS; /* always has input focus */ 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
if ([UIApplication sharedApplication].statusBarHidden) {
window->flags |= SDL_WINDOW_BORDERLESS;
} else {
window->flags &= ~SDL_WINDOW_BORDERLESS;
}
*/
} else { } else {
window->flags &= ~SDL_WINDOW_RESIZABLE; /* window is NEVER resizeable */ window->flags &= ~SDL_WINDOW_RESIZABLE; /* window is NEVER resizable */
window->flags &= ~SDL_WINDOW_INPUT_FOCUS; /* never has input focus */ window->flags &= ~SDL_WINDOW_INPUT_FOCUS; /* never has input focus */
window->flags |= SDL_WINDOW_BORDERLESS; /* never has a status bar. */ window->flags |= SDL_WINDOW_BORDERLESS; /* never has a status bar. */
} }
/* The View Controller will handle rotating the view when the if (displaydata.uiscreen == [UIScreen mainScreen]) {
* device orientation changes. This will trigger resize events, if NSUInteger orients = UIKit_GetSupportedOrientations(window);
* appropriate. BOOL supportsLandscape = (orients & UIInterfaceOrientationMaskLandscape) != 0;
*/ BOOL supportsPortrait = (orients & (UIInterfaceOrientationMaskPortrait|UIInterfaceOrientationMaskPortraitUpsideDown)) != 0;
SDL_uikitviewcontroller *controller;
controller = [SDL_uikitviewcontroller alloc]; /* Make sure the width/height are oriented correctly */
data->viewcontroller = [controller initWithSDLWindow:window]; if ((width > height && !supportsLandscape) || (height > width && !supportsPortrait)) {
[data->viewcontroller setTitle:@"SDL App"]; /* !!! FIXME: hook up SDL_SetWindowTitle() */ int temp = width;
width = height;
height = temp;
}
}
window->x = 0;
window->y = 0;
window->w = width;
window->h = height;
/* The View Controller will handle rotating the view when the device
* orientation changes. This will trigger resize events, if appropriate. */
data.viewcontroller = [[SDL_uikitviewcontroller alloc] initWithSDLWindow:window];
/* The window will initially contain a generic view so resizes, touch events,
* etc. can be handled without an active OpenGL view/context. */
view = [[SDL_uikitview alloc] initWithFrame:frame];
/* Sets this view as the controller's view, and adds the view to the window
* heirarchy. */
[view setSDLWindow:window];
/* Make this window the current mouse focus for touch input */
if (displaydata.uiscreen == [UIScreen mainScreen]) {
SDL_SetMouseFocus(window);
SDL_SetKeyboardFocus(window);
}
return 0; return 0;
} }
@ -127,173 +148,165 @@ static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bo
int int
UIKit_CreateWindow(_THIS, SDL_Window *window) UIKit_CreateWindow(_THIS, SDL_Window *window)
{ {
SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); @autoreleasepool {
SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata; SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
const BOOL external = ([UIScreen mainScreen] != data->uiscreen); SDL_DisplayData *data = (__bridge SDL_DisplayData *) display->driverdata;
const CGSize origsize = [[data->uiscreen currentMode] size]; 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 currently puts this window at the start of display's linked list. We rely on this. */
SDL_assert(_this->windows == window); SDL_assert(_this->windows == window);
/* We currently only handle a single window per display on iOS */ /* We currently only handle a single window per display on iOS */
if (window->next != NULL) { if (window->next != NULL) {
return SDL_SetError("Only one window allowed per display."); 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);
} }
int i; /* If monitor has a resolution of 0x0 (hasn't been explicitly set by the
const SDL_DisplayMode *bestmode = NULL; * user, so it's in standby), try to force the display to a resolution
for (i = display->num_display_modes; i >= 0; i--) { * that most closely matches the desired window size. */
const SDL_DisplayMode *mode = &display->display_modes[i]; if ((origsize.width == 0.0f) && (origsize.height == 0.0f)) {
if ((mode->w >= window->w) && (mode->h >= window->h)) if (display->num_display_modes == 0) {
bestmode = mode; _this->GetDisplayModes(_this, display);
}
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];
} }
} else if (window->w < window->h) {
if (UIKit_IsDisplayLandscape(data->uiscreen)) { int i;
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait animated:NO]; 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 */ if (data.uiscreen == [UIScreen mainScreen]) {
/* !!! FIXME: can we have a smaller view? */ NSUInteger orientations = UIKit_GetSupportedOrientations(window);
UIWindow *uiwindow = [UIWindow alloc]; UIApplication *app = [UIApplication sharedApplication];
uiwindow = [uiwindow initWithFrame:[data->uiscreen bounds]];
/* put the window on an external display if appropriate. This implicitly if (window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_BORDERLESS)) {
* does [uiwindow setframe:[uiscreen bounds]], so don't do it on the app.statusBarHidden = YES;
* main display, where we land by default, as that would eat the } else {
* status bar real estate. app.statusBarHidden = NO;
*/ }
if (external) { }
[uiwindow setScreen:data->uiscreen];
}
if (SetupWindowData(_this, window, uiwindow, SDL_TRUE) < 0) { /* ignore the size user requested, and make a fullscreen window */
[uiwindow release]; /* !!! FIXME: can we have a smaller view? */
return -1; UIWindow *uiwindow = [[SDL_uikitwindow alloc] initWithFrame:data.uiscreen.bounds];
/* put the window on an external display if appropriate. */
if (data.uiscreen != [UIScreen mainScreen]) {
[uiwindow setScreen:data.uiscreen];
}
if (SetupWindowData(_this, window, uiwindow, SDL_TRUE) < 0) {
return -1;
}
} }
return 1; return 1;
}
void
UIKit_SetWindowTitle(_THIS, SDL_Window * window)
{
@autoreleasepool {
SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
if (window->title) {
data.viewcontroller.title = @(window->title);
} else {
data.viewcontroller.title = nil;
}
}
} }
void void
UIKit_ShowWindow(_THIS, SDL_Window * window) UIKit_ShowWindow(_THIS, SDL_Window * window)
{ {
UIWindow *uiwindow = ((SDL_WindowData *) window->driverdata)->uiwindow; @autoreleasepool {
SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
[uiwindow makeKeyAndVisible]; [data.uiwindow makeKeyAndVisible];
}
} }
void void
UIKit_HideWindow(_THIS, SDL_Window * window) UIKit_HideWindow(_THIS, SDL_Window * window)
{ {
UIWindow *uiwindow = ((SDL_WindowData *) window->driverdata)->uiwindow; @autoreleasepool {
SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
uiwindow.hidden = YES; data.uiwindow.hidden = YES;
}
} }
void void
UIKit_RaiseWindow(_THIS, SDL_Window * window) UIKit_RaiseWindow(_THIS, SDL_Window * window)
{ {
/* We don't currently offer a concept of "raising" the SDL window, since /* We don't currently offer a concept of "raising" the SDL window, since
* we only allow one per display, in the iOS fashion. * we only allow one per display, in the iOS fashion.
* However, we use this entry point to rebind the context to the view * However, we use this entry point to rebind the context to the view
* during OnWindowRestored processing. * during OnWindowRestored processing. */
*/
_this->GL_MakeCurrent(_this, _this->current_glwin, _this->current_glctx); _this->GL_MakeCurrent(_this, _this->current_glwin, _this->current_glctx);
} }
static void
UIKit_UpdateWindowBorder(_THIS, SDL_Window * window)
{
SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
SDL_uikitviewcontroller *viewcontroller = data.viewcontroller;
if (data.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. */
viewcontroller.view.frame = UIKit_ComputeViewFrame(window, data.uiwindow.screen);
[viewcontroller.view setNeedsLayout];
[viewcontroller.view layoutIfNeeded];
}
void
UIKit_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered)
{
@autoreleasepool {
UIKit_UpdateWindowBorder(_this, window);
}
}
void void
UIKit_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen) UIKit_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
{ {
SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata; @autoreleasepool {
SDL_DisplayModeData *displaymodedata = (SDL_DisplayModeData *) display->current_mode.driverdata; UIKit_UpdateWindowBorder(_this, window);
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;
}
} }
} }
void void
UIKit_DestroyWindow(_THIS, SDL_Window * window) UIKit_DestroyWindow(_THIS, SDL_Window * window)
{ {
SDL_WindowData *data = (SDL_WindowData *)window->driverdata; @autoreleasepool {
if (window->driverdata != NULL) {
if (data) { SDL_WindowData *data = (SDL_WindowData *) CFBridgingRelease(window->driverdata);
[data->viewcontroller release]; [data.viewcontroller stopAnimation];
[data->uiwindow release]; }
SDL_free(data);
} }
window->driverdata = NULL; window->driverdata = NULL;
} }
@ -301,29 +314,82 @@ UIKit_DestroyWindow(_THIS, SDL_Window * window)
SDL_bool SDL_bool
UIKit_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info) UIKit_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
{ {
UIWindow *uiwindow = ((SDL_WindowData *) window->driverdata)->uiwindow; @autoreleasepool {
SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
if (info->version.major <= SDL_MAJOR_VERSION) { if (info->version.major <= SDL_MAJOR_VERSION) {
info->subsystem = SDL_SYSWM_UIKIT; info->subsystem = SDL_SYSWM_UIKIT;
info->info.uikit.window = uiwindow; info->info.uikit.window = data.uiwindow;
return SDL_TRUE; return SDL_TRUE;
} else { } else {
SDL_SetError("Application not compiled with SDL %d.%d\n", SDL_SetError("Application not compiled with SDL %d.%d\n",
SDL_MAJOR_VERSION, SDL_MINOR_VERSION); SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
return SDL_FALSE; return SDL_FALSE;
}
} }
} }
NSUInteger
UIKit_GetSupportedOrientations(SDL_Window * window)
{
const char *hint = SDL_GetHint(SDL_HINT_ORIENTATIONS);
NSUInteger orientationMask = 0;
@autoreleasepool {
if (hint != NULL) {
NSArray *orientations = [@(hint) componentsSeparatedByString:@" "];
if ([orientations containsObject:@"LandscapeLeft"]) {
orientationMask |= UIInterfaceOrientationMaskLandscapeLeft;
}
if ([orientations containsObject:@"LandscapeRight"]) {
orientationMask |= UIInterfaceOrientationMaskLandscapeRight;
}
if ([orientations containsObject:@"Portrait"]) {
orientationMask |= UIInterfaceOrientationMaskPortrait;
}
if ([orientations containsObject:@"PortraitUpsideDown"]) {
orientationMask |= UIInterfaceOrientationMaskPortraitUpsideDown;
}
}
if (orientationMask == 0 && (window->flags & SDL_WINDOW_RESIZABLE)) {
/* any orientation is okay. */
orientationMask = UIInterfaceOrientationMaskAll;
}
if (orientationMask == 0) {
if (window->w >= window->h) {
orientationMask |= UIInterfaceOrientationMaskLandscape;
}
if (window->h >= window->w) {
orientationMask |= (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown);
}
}
/* Don't allow upside-down orientation on the phone, so answering calls is in the natural orientation */
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) {
orientationMask &= ~UIInterfaceOrientationMaskPortraitUpsideDown;
}
}
return orientationMask;
}
int int
SDL_iPhoneSetAnimationCallback(SDL_Window * window, int interval, void (*callback)(void*), void *callbackParam) SDL_iPhoneSetAnimationCallback(SDL_Window * window, int interval, void (*callback)(void*), void *callbackParam)
{ {
SDL_WindowData *data = window ? (SDL_WindowData *)window->driverdata : NULL; if (!window || !window->driverdata) {
return SDL_SetError("Invalid window");
if (!data || !data->view) { }
return SDL_SetError("Invalid window or view not set");
@autoreleasepool {
SDL_WindowData *data = (__bridge SDL_WindowData *)window->driverdata;
[data.viewcontroller setAnimationCallback:interval
callback:callback
callbackParam:callbackParam];
} }
[data->view setAnimationCallback:interval callback:callback callbackParam:callbackParam];
return 0; return 0;
} }