Initial Apple TV / tvOS support.

The Apple TV remote is currently exposed as a joystick with its touch surface treated as two axes. Key presses are also generated when its buttons and touch surface are used.

A new hint has been added to help deal with deciding whether to background the app when the remote's menu button is pressed: SDL_HINT_APPLE_TV_CONTROLLER_UI_EVENTS.
This commit is contained in:
Alex Szpakowski
2016-09-13 22:18:06 -03:00
parent 86708c3cd8
commit f050576665
21 changed files with 1191 additions and 62 deletions

View File

@@ -443,6 +443,8 @@ SDL_GetPlatform()
return "Windows";
#elif __WINRT__
return "WinRT";
#elif __TVOS__
return "tvOS";
#elif __IPHONEOS__
return "iOS";
#elif __PSP__

View File

@@ -274,7 +274,7 @@ static int open_capture_devices = 0;
static void update_audio_session()
{
#if !MACOSX_COREAUDIO
#if !MACOSX_COREAUDIO && !TARGET_OS_TV
/* !!! FIXME: move this to AVAudioSession. This is deprecated, and the new version is available as of (ancient!) iOS 3.0 */
UInt32 category;
if (open_playback_devices && open_capture_devices) {
@@ -569,8 +569,8 @@ prepare_audioqueue(_THIS)
/* We're running! */
return 1;
}
static int
audioqueue_thread(void *arg)
{
SDL_AudioDevice *this = (SDL_AudioDevice *) arg;
@@ -725,9 +725,11 @@ COREAUDIO_Init(SDL_AudioDriverImpl * impl)
!!! FIXME: do this when a device is opened, and deinitialize when all devices close.
*/
/* !!! FIXME: move this to AVAudioSession. This is deprecated, and the new version is available as of (ancient!) iOS 3.0 */
#if !TARGET_OS_TV
AudioSessionInitialize(NULL, NULL, NULL, nil);
UInt32 category = kAudioSessionCategory_AmbientSound;
AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(UInt32), &category);
#endif /* !TARGET_OS_TV */
#endif
impl->ProvidesOwnCallbackThread = 1;

View File

@@ -33,7 +33,13 @@
#include "../SDL_sysjoystick.h"
#include "../SDL_joystick_c.h"
#if !SDL_EVENTS_DISABLED
#include "../../events/SDL_events_c.h"
#endif
#if !TARGET_OS_TV
#import <CoreMotion/CoreMotion.h>
#endif
#ifdef SDL_JOYSTICK_MFI
#import <GameController/GameController.h>
@@ -42,8 +48,10 @@ static id connectObserver = nil;
static id disconnectObserver = nil;
#endif /* SDL_JOYSTICK_MFI */
#if !TARGET_OS_TV
static const char *accelerometerName = "iOS Accelerometer";
static CMMotionManager *motionManager = nil;
#endif /* !TARGET_OS_TV */
static SDL_JoystickDeviceItem *deviceList = NULL;
@@ -102,6 +110,11 @@ SDL_SYS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCController *contr
} else if (controller.gamepad) {
device->guid.data[10] = 2;
}
#if TARGET_OS_TV
else if (controller.microGamepad) {
device->guid.data[10] = 3;
}
#endif /* TARGET_OS_TV */
if (controller.extendedGamepad) {
device->naxes = 6; /* 2 thumbsticks and 2 triggers */
@@ -112,12 +125,19 @@ SDL_SYS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCController *contr
device->nhats = 1; /* d-pad */
device->nbuttons = 7; /* ABXY, shoulder buttons, pause button */
}
/* TODO: Handle micro profiles on tvOS. */
#if TARGET_OS_TV
else if (controller.microGamepad) {
device->naxes = 2; /* treat the touch surface as two axes */
device->nhats = 0; /* apparently the touch surface-as-dpad is buggy */
device->nbuttons = 3; /* AX, pause button */
}
#endif /* TARGET_OS_TV */
/* This will be set when the first button press of the controller is
* detected. */
controller.playerIndex = -1;
#endif
#endif /* SDL_JOYSTICK_MFI */
}
static void
@@ -143,6 +163,10 @@ SDL_SYS_AddJoystickDevice(GCController *controller, SDL_bool accelerometer)
device->instance_id = instancecounter++;
if (accelerometer) {
#if TARGET_OS_TV
SDL_free(device);
return;
#else
device->name = SDL_strdup(accelerometerName);
device->naxes = 3; /* Device acceleration in the x, y, and z axes. */
device->nhats = 0;
@@ -150,6 +174,7 @@ SDL_SYS_AddJoystickDevice(GCController *controller, SDL_bool accelerometer)
/* Use the accelerometer name as a GUID. */
SDL_memcpy(&device->guid.data, device->name, SDL_min(sizeof(SDL_JoystickGUID), SDL_strlen(device->name)));
#endif /* TARGET_OS_TV */
} else if (controller) {
SDL_SYS_AddMFIJoystickDevice(device, controller);
}
@@ -232,12 +257,14 @@ SDL_SYS_JoystickInit(void)
{
@autoreleasepool {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
const char *hint = SDL_GetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK);
#if !TARGET_OS_TV
const char *hint = SDL_GetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK);
if (!hint || SDL_atoi(hint)) {
/* Default behavior, accelerometer as joystick */
SDL_SYS_AddJoystickDevice(nil, SDL_TRUE);
}
#endif /* !TARGET_OS_TV */
#ifdef SDL_JOYSTICK_MFI
/* GameController.framework was added in iOS 7. */
@@ -326,6 +353,7 @@ SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
@autoreleasepool {
if (device->accelerometer) {
#if !TARGET_OS_TV
if (motionManager == nil) {
motionManager = [[CMMotionManager alloc] init];
}
@@ -333,6 +361,7 @@ SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
/* Shorter times between updates can significantly increase CPU usage. */
motionManager.accelerometerUpdateInterval = 0.1;
[motionManager startAccelerometerUpdates];
#endif /* !TARGET_OS_TV */
} else {
#ifdef SDL_JOYSTICK_MFI
GCController *controller = device->controller;
@@ -358,6 +387,7 @@ SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
static void
SDL_SYS_AccelerometerUpdate(SDL_Joystick * joystick)
{
#if !TARGET_OS_TV
const float maxgforce = SDL_IPHONE_MAX_GFORCE;
const SInt16 maxsint16 = 0x7FFF;
CMAcceleration accel;
@@ -395,6 +425,7 @@ SDL_SYS_AccelerometerUpdate(SDL_Joystick * joystick)
SDL_PrivateJoystickAxis(joystick, 0, (accel.x / maxgforce) * maxsint16);
SDL_PrivateJoystickAxis(joystick, 1, -(accel.y / maxgforce) * maxsint16);
SDL_PrivateJoystickAxis(joystick, 2, (accel.z / maxgforce) * maxsint16);
#endif /* !TARGET_OS_TV */
}
#ifdef SDL_JOYSTICK_MFI
@@ -426,7 +457,7 @@ SDL_SYS_MFIJoystickHatStateForDPad(GCControllerDirectionPad *dpad)
static void
SDL_SYS_MFIJoystickUpdate(SDL_Joystick * joystick)
{
#ifdef SDL_JOYSTICK_MFI
#if SDL_JOYSTICK_MFI
@autoreleasepool {
GCController *controller = joystick->hwdata->controller;
Uint8 hatstate = SDL_HAT_CENTERED;
@@ -482,13 +513,43 @@ SDL_SYS_MFIJoystickUpdate(SDL_Joystick * joystick)
};
hatstate = SDL_SYS_MFIJoystickHatStateForDPad(gamepad.dpad);
SDL_PrivateJoystickHat(joystick, 0, hatstate);
for (i = 0; i < SDL_arraysize(buttons); i++) {
updateplayerindex |= (joystick->buttons[i] != buttons[i]);
SDL_PrivateJoystickButton(joystick, i, buttons[i]);
}
}
/* TODO: Handle micro profiles on tvOS. */
#if TARGET_OS_TV
else if (controller.microGamepad) {
GCMicroGamepad *gamepad = controller.microGamepad;
Sint16 axes[] = {
(Sint16) (gamepad.dpad.xAxis.value * 32767),
(Sint16) (gamepad.dpad.yAxis.value * -32767),
};
for (i = 0; i < SDL_arraysize(axes); i++) {
updateplayerindex |= (joystick->axes[i] != axes[i]);
SDL_PrivateJoystickAxis(joystick, i, axes[i]);
}
/* Apparently the dpad values are not accurate enough to be useful. */
/* hatstate = SDL_SYS_MFIJoystickHatStateForDPad(gamepad.dpad); */
Uint8 buttons[] = {
gamepad.buttonA.isPressed,
gamepad.buttonX.isPressed,
};
for (i = 0; i < SDL_arraysize(buttons); i++) {
updateplayerindex |= (joystick->buttons[i] != buttons[i]);
SDL_PrivateJoystickButton(joystick, i, buttons[i]);
}
/* TODO: Figure out what to do with reportsAbsoluteDpadValues */
}
#endif /* TARGET_OS_TV */
if (joystick->nhats > 0) {
updateplayerindex |= (joystick->hats[0] != hatstate);
@@ -528,7 +589,7 @@ SDL_SYS_MFIJoystickUpdate(SDL_Joystick * joystick)
}
}
}
#endif
#endif /* SDL_JOYSTICK_MFI */
}
/* Function to update the state of a joystick - called as a device poll.
@@ -566,7 +627,9 @@ SDL_SYS_JoystickClose(SDL_Joystick * joystick)
@autoreleasepool {
if (device->accelerometer) {
#if !TARGET_OS_TV
[motionManager stopAccelerometerUpdates];
#endif /* !TARGET_OS_TV */
} else if (device->controller) {
#ifdef SDL_JOYSTICK_MFI
GCController *controller = device->controller;
@@ -600,7 +663,9 @@ SDL_SYS_JoystickQuit(void)
SDL_SYS_RemoveJoystickDevice(deviceList);
}
#if !TARGET_OS_TV
motionManager = nil;
#endif /* !TARGET_OS_TV */
}
numjoysticks = 0;

View File

@@ -30,6 +30,7 @@
#include "SDL_assert.h"
#include "SDL_syspower.h"
#if !TARGET_OS_TV
/* turn off the battery monitor if it's been more than X ms since last check. */
static const int BATTERY_MONITORING_TIMEOUT = 3000;
static Uint32 SDL_UIKitLastPowerInfoQuery = 0;
@@ -46,10 +47,22 @@ SDL_UIKit_UpdateBatteryMonitoring(void)
}
}
}
#else
void
SDL_UIKit_UpdateBatteryMonitoring(void)
{
/* Do nothing. */
}
#endif /* !TARGET_OS_TV */
SDL_bool
SDL_GetPowerInfo_UIKit(SDL_PowerState * state, int *seconds, int *percent)
{
#if TARGET_OS_TV
*state = SDL_POWERSTATE_NO_BATTERY;
*seconds = -1;
*percent = -1;
#else /* TARGET_OS_TV */
@autoreleasepool {
UIDevice *uidev = [UIDevice currentDevice];
@@ -88,8 +101,10 @@ SDL_GetPowerInfo_UIKit(SDL_PowerState * state, int *seconds, int *percent)
const float level = uidev.batteryLevel;
*percent = ( (level < 0.0f) ? -1 : ((int) ((level * 100) + 0.5f)) );
return SDL_TRUE; /* always the definitive answer on iOS. */
}
#endif /* TARGET_OS_TV */
return SDL_TRUE; /* always the definitive answer on iOS. */
}
#endif /* SDL_POWER_UIKIT */

View File

@@ -343,7 +343,7 @@ SDLgfx_rotateSurface(SDL_Surface * src, double angle, int centerx, int centery,
SDL_Surface *rz_dst;
int is32bit;
int i;
Uint8 r,g,b;
Uint8 r = 0,g = 0,b = 0;
Uint32 colorkey = 0;
int colorKeyAvailable = 0;
double sangleinv, cangleinv;

View File

@@ -25,7 +25,6 @@
- (instancetype)init;
- (void)loadView;
- (NSUInteger)supportedInterfaceOrientations;
@end

View File

@@ -76,6 +76,7 @@ SDL_IdleTimerDisabledChanged(void *userdata, const char *name, const char *oldVa
[UIApplication sharedApplication].idleTimerDisabled = disable;
}
#if !TARGET_OS_TV
/* Load a launch image using the old UILaunchImageFile-era naming rules. */
static UIImage *
SDL_LoadLaunchImageNamed(NSString *name, int screenh)
@@ -114,6 +115,15 @@ SDL_LoadLaunchImageNamed(NSString *name, int screenh)
return image;
}
#endif /* !TARGET_OS_TV */
@interface SDLLaunchScreenController ()
#if !TARGET_OS_TV
- (NSUInteger)supportedInterfaceOrientations;
#endif
@end
@implementation SDLLaunchScreenController
@@ -140,6 +150,7 @@ SDL_LoadLaunchImageNamed(NSString *name, int screenh)
}
if (!self.view) {
#if !TARGET_OS_TV
NSArray *launchimages = [bundle objectForInfoDictionaryKey:@"UILaunchImages"];
UIInterfaceOrientation curorient = [UIApplication sharedApplication].statusBarOrientation;
NSString *imagename = nil;
@@ -244,6 +255,9 @@ SDL_LoadLaunchImageNamed(NSString *name, int screenh)
self.view = view;
}
#else /* !TARGET_OS_TV */
return nil;
#endif
}
return self;
@@ -254,6 +268,7 @@ SDL_LoadLaunchImageNamed(NSString *name, int screenh)
/* Do nothing. */
}
#if !TARGET_OS_TV
- (BOOL)shouldAutorotate
{
/* If YES, the launch image will be incorrectly rotated in some cases. */
@@ -267,6 +282,7 @@ SDL_LoadLaunchImageNamed(NSString *name, int screenh)
* the ones set here (it will cause an exception in that case.) */
return UIInterfaceOrientationMaskAll;
}
#endif /* !TARGET_OS_TV */
@end
@@ -381,6 +397,7 @@ SDL_LoadLaunchImageNamed(NSString *name, int screenh)
SDL_SendAppEvent(SDL_APP_LOWMEMORY);
}
#if !TARGET_OS_TV
- (void)application:(UIApplication *)application didChangeStatusBarOrientation:(UIInterfaceOrientation)oldStatusBarOrientation
{
BOOL isLandscape = UIInterfaceOrientationIsLandscape(application.statusBarOrientation);
@@ -408,6 +425,7 @@ SDL_LoadLaunchImageNamed(NSString *name, int screenh)
}
}
}
#endif
- (void)applicationWillResignActive:(UIApplication*)application
{

View File

@@ -30,15 +30,22 @@
int
UIKit_SetClipboardText(_THIS, const char *text)
{
#if TARGET_OS_TV
return SDL_SetError("The clipboard is not available on tvOS");
#else
@autoreleasepool {
[UIPasteboard generalPasteboard].string = @(text);
return 0;
}
#endif
}
char *
UIKit_GetClipboardText(_THIS)
{
#if TARGET_OS_TV
return SDL_strdup(""); // Unsupported.
#else
@autoreleasepool {
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
NSString *string = pasteboard.string;
@@ -49,15 +56,18 @@ UIKit_GetClipboardText(_THIS)
return SDL_strdup("");
}
}
#endif
}
SDL_bool
UIKit_HasClipboardText(_THIS)
{
@autoreleasepool {
#if !TARGET_OS_TV
if ([UIPasteboard generalPasteboard].string != nil) {
return SDL_TRUE;
}
#endif
return SDL_FALSE;
}
}
@@ -65,6 +75,7 @@ UIKit_HasClipboardText(_THIS)
void
UIKit_InitClipboard(_THIS)
{
#if !TARGET_OS_TV
@autoreleasepool {
SDL_VideoData *data = (__bridge SDL_VideoData *) _this->driverdata;
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
@@ -78,6 +89,7 @@ UIKit_InitClipboard(_THIS)
data.pasteboardObserver = observer;
}
#endif
}
void

View File

@@ -156,9 +156,12 @@ UIKit_AddDisplay(UIScreen *uiscreen)
SDL_bool
UIKit_IsDisplayLandscape(UIScreen *uiscreen)
{
#if !TARGET_OS_TV
if (uiscreen == [UIScreen mainScreen]) {
return UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation);
} else {
} else
#endif /* !TARGET_OS_TV */
{
CGSize size = uiscreen.bounds.size;
return (size.width > size.height);
}
@@ -187,6 +190,14 @@ UIKit_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
SDL_bool isLandscape = UIKit_IsDisplayLandscape(data.uiscreen);
SDL_bool addRotation = (data.uiscreen == [UIScreen mainScreen]);
CGFloat scale = data.uiscreen.scale;
NSArray *availableModes = nil;
#if TARGET_OS_TV
addRotation = SDL_FALSE;
availableModes = @[data.uiscreen.currentMode];
#else
availableModes = data.uiscreen.availableModes;
#endif
#ifdef __IPHONE_8_0
/* The UIScreenMode of an iPhone 6 Plus should be 1080x1920 rather than
@@ -196,7 +207,7 @@ UIKit_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
}
#endif
for (UIScreenMode *uimode in data.uiscreen.availableModes) {
for (UIScreenMode *uimode in availableModes) {
/* 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);
@@ -219,9 +230,11 @@ UIKit_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
{
@autoreleasepool {
SDL_DisplayData *data = (__bridge SDL_DisplayData *) display->driverdata;
SDL_DisplayModeData *modedata = (__bridge SDL_DisplayModeData *)mode->driverdata;
#if !TARGET_OS_TV
SDL_DisplayModeData *modedata = (__bridge SDL_DisplayModeData *)mode->driverdata;
[data.uiscreen setCurrentMode:modedata.uiscreenmode];
#endif
if (data.uiscreen == [UIScreen mainScreen]) {
/* [UIApplication setStatusBarOrientation:] no longer works reliably
@@ -245,20 +258,30 @@ UIKit_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
int
UIKit_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect)
{
/* the default function iterates displays to make a fake offset,
as if all the displays were side-by-side, which is fine for iOS. */
const int displayIndex = (int) (display - _this->displays);
if (SDL_GetDisplayBounds(displayIndex, rect) < 0) {
return -1;
@autoreleasepool {
int displayIndex = (int) (display - _this->displays);
SDL_DisplayData *data = (__bridge SDL_DisplayData *) display->driverdata;
/* the default function iterates displays to make a fake offset,
as if all the displays were side-by-side, which is fine for iOS. */
if (SDL_GetDisplayBounds(displayIndex, rect) < 0) {
return -1;
}
CGRect frame = data.uiscreen.bounds;
#if !TARGET_OS_TV
if (!UIKit_IsSystemVersionAtLeast(7.0)) {
frame = [data.uiscreen applicationFrame];
}
#endif
rect->x += frame.origin.x;
rect->y += frame.origin.y;
rect->w = frame.size.width;
rect->h = frame.size.height;
}
SDL_DisplayData *data = (__bridge SDL_DisplayData *) display->driverdata;
const CGRect frame = [data.uiscreen applicationFrame];
const float scale = (float) data.uiscreen.scale;
rect->x += (int) (frame.origin.x * scale);
rect->y += (int) (frame.origin.y * scale);
rect->w = (int) (frame.size.width * scale);
rect->h = (int) (frame.size.height * scale);
return 0;
}

View File

@@ -176,6 +176,7 @@ UIKit_IsSystemVersionAtLeast(double version)
CGRect
UIKit_ComputeViewFrame(SDL_Window *window, UIScreen *screen)
{
#if !TARGET_OS_TV && (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0)
BOOL hasiOS7 = UIKit_IsSystemVersionAtLeast(7.0);
if (hasiOS7 || (window->flags & (SDL_WINDOW_BORDERLESS|SDL_WINDOW_FULLSCREEN))) {
@@ -184,6 +185,9 @@ UIKit_ComputeViewFrame(SDL_Window *window, UIScreen *screen)
} else {
return screen.applicationFrame;
}
#else
return screen.bounds;
#endif
}
/*

View File

@@ -45,7 +45,9 @@
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.autoresizesSubviews = YES;
#if !TARGET_OS_TV
self.multipleTouchEnabled = YES;
#endif
touchId = 1;
SDL_AddTouch(touchId, "");
@@ -197,6 +199,69 @@
}
}
#if TARGET_OS_TV || defined(__IPHONE_9_1)
- (SDL_Scancode)scancodeFromPressType:(UIPressType)presstype
{
switch (presstype) {
case UIPressTypeUpArrow:
return SDL_SCANCODE_UP;
case UIPressTypeDownArrow:
return SDL_SCANCODE_DOWN;
case UIPressTypeLeftArrow:
return SDL_SCANCODE_LEFT;
case UIPressTypeRightArrow:
return SDL_SCANCODE_RIGHT;
case UIPressTypeSelect:
/* HIG says: "primary button behavior" */
return SDL_SCANCODE_SELECT;
case UIPressTypeMenu:
/* HIG says: "returns to previous screen" */
return SDL_SCANCODE_MENU;
case UIPressTypePlayPause:
/* HIG says: "secondary button behavior" */
return SDL_SCANCODE_PAUSE;
default:
return SDL_SCANCODE_UNKNOWN;
}
}
- (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
{
for (UIPress *press in presses) {
SDL_Scancode scancode = [self scancodeFromPressType:press.type];
SDL_SendKeyboardKey(SDL_PRESSED, scancode);
}
[super pressesBegan:presses withEvent:event];
}
- (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
{
for (UIPress *press in presses) {
SDL_Scancode scancode = [self scancodeFromPressType:press.type];
SDL_SendKeyboardKey(SDL_RELEASED, scancode);
}
[super pressesEnded:presses withEvent:event];
}
- (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
{
for (UIPress *press in presses) {
SDL_Scancode scancode = [self scancodeFromPressType:press.type];
SDL_SendKeyboardKey(SDL_RELEASED, scancode);
}
[super pressesCancelled:presses withEvent:event];
}
- (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
{
/* This is only called when the force of a press changes. */
[super pressesChanged:presses withEvent:event];
}
#endif /* TARGET_OS_TV || defined(__IPHONE_9_1) */
@end
#endif /* SDL_VIDEO_DRIVER_UIKIT */

View File

@@ -18,6 +18,7 @@
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#import <UIKit/UIKit.h>
@@ -25,10 +26,17 @@
#include "SDL_touch.h"
#if SDL_IPHONE_KEYBOARD
@interface SDL_uikitviewcontroller : UIViewController <UITextFieldDelegate>
#if TARGET_OS_TV
#import <GameController/GameController.h>
#define SDLRootViewController GCEventViewController
#else
@interface SDL_uikitviewcontroller : UIViewController
#define SDLRootViewController UIViewController
#endif
#if SDL_IPHONE_KEYBOARD
@interface SDL_uikitviewcontroller : SDLRootViewController <UITextFieldDelegate>
#else
@interface SDL_uikitviewcontroller : SDLRootViewController
#endif
@property (nonatomic, assign) SDL_Window *window;
@@ -46,8 +54,11 @@
- (void)loadView;
- (void)viewDidLayoutSubviews;
#if !TARGET_OS_TV
- (NSUInteger)supportedInterfaceOrientations;
- (BOOL)prefersStatusBarHidden;
#endif
#if SDL_IPHONE_KEYBOARD
- (void)showKeyboard;

View File

@@ -39,6 +39,17 @@
#include "keyinfotable.h"
#endif
#if TARGET_OS_TV
static void
SDL_AppleTVControllerUIHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
{
@autoreleasepool {
SDL_uikitviewcontroller *viewcontroller = (__bridge SDL_uikitviewcontroller *) userdata;
viewcontroller.controllerUserInteractionEnabled = hint && (*hint != '0');
}
}
#endif
@implementation SDL_uikitviewcontroller {
CADisplayLink *displayLink;
int animationInterval;
@@ -60,6 +71,12 @@
#if SDL_IPHONE_KEYBOARD
[self initKeyboard];
#endif
#if TARGET_OS_TV
SDL_AddHintCallback(SDL_HINT_APPLE_TV_CONTROLLER_UI_EVENTS,
SDL_AppleTVControllerUIHintChanged,
(__bridge void *) self);
#endif
}
return self;
}
@@ -69,6 +86,12 @@
#if SDL_IPHONE_KEYBOARD
[self deinitKeyboard];
#endif
#if TARGET_OS_TV
SDL_DelHintCallback(SDL_HINT_APPLE_TV_CONTROLLER_UI_EVENTS,
SDL_AppleTVControllerUIHintChanged,
(__bridge void *) self);
#endif
}
- (void)setAnimationCallback:(int)interval
@@ -124,6 +147,7 @@
SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, w, h);
}
#if !TARGET_OS_TV
- (NSUInteger)supportedInterfaceOrientations
{
return UIKit_GetSupportedOrientations(window);
@@ -138,6 +162,7 @@
{
return (window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_BORDERLESS)) != 0;
}
#endif
/*
---- Keyboard related functionality below this line ----
@@ -168,9 +193,11 @@
textField.hidden = YES;
keyboardVisible = NO;
#if !TARGET_OS_TV
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[center addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
#endif
}
- (void)setView:(UIView *)view
@@ -186,9 +213,11 @@
- (void)deinitKeyboard
{
#if !TARGET_OS_TV
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[center removeObserver:self name:UIKeyboardWillHideNotification object:nil];
#endif
}
/* reveal onscreen virtual keyboard */
@@ -209,6 +238,7 @@
- (void)keyboardWillShow:(NSNotification *)notification
{
#if !TARGET_OS_TV
CGRect kbrect = [[notification userInfo][UIKeyboardFrameBeginUserInfoKey] CGRectValue];
/* The keyboard rect is in the coordinate space of the screen/window, but we
@@ -216,6 +246,7 @@
kbrect = [self.view convertRect:kbrect fromView:nil];
[self setKeyboardHeight:(int)kbrect.size.height];
#endif
}
- (void)keyboardWillHide:(NSNotification *)notification

View File

@@ -107,6 +107,7 @@ static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bo
window->flags |= SDL_WINDOW_BORDERLESS; /* never has a status bar. */
}
#if !TARGET_OS_TV
if (displaydata.uiscreen == [UIScreen mainScreen]) {
NSUInteger orients = UIKit_GetSupportedOrientations(window);
BOOL supportsLandscape = (orients & UIInterfaceOrientationMaskLandscape) != 0;
@@ -119,6 +120,7 @@ static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bo
height = temp;
}
}
#endif /* !TARGET_OS_TV */
window->x = 0;
window->y = 0;
@@ -152,7 +154,6 @@ UIKit_CreateWindow(_THIS, SDL_Window *window)
@autoreleasepool {
SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
SDL_DisplayData *data = (__bridge SDL_DisplayData *) display->driverdata;
const CGSize origsize = data.uiscreen.currentMode.size;
/* SDL currently puts this window at the start of display's linked list. We rely on this. */
SDL_assert(_this->windows == window);
@@ -165,6 +166,8 @@ UIKit_CreateWindow(_THIS, SDL_Window *window)
/* 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 !TARGET_OS_TV
const CGSize origsize = data.uiscreen.currentMode.size;
if ((origsize.width == 0.0f) && (origsize.height == 0.0f)) {
if (display->num_display_modes == 0) {
_this->GetDisplayModes(_this, display);
@@ -197,6 +200,7 @@ UIKit_CreateWindow(_THIS, SDL_Window *window)
[UIApplication sharedApplication].statusBarHidden = NO;
}
}
#endif /* !TARGET_OS_TV */
/* ignore the size user requested, and make a fullscreen window */
/* !!! FIXME: can we have a smaller view? */
@@ -258,6 +262,7 @@ UIKit_UpdateWindowBorder(_THIS, SDL_Window * window)
SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
SDL_uikitviewcontroller *viewcontroller = data.viewcontroller;
#if !TARGET_OS_TV
if (data.uiwindow.screen == [UIScreen mainScreen]) {
if (window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS)) {
[UIApplication sharedApplication].statusBarHidden = YES;
@@ -273,6 +278,7 @@ UIKit_UpdateWindowBorder(_THIS, SDL_Window * window)
/* Update the view's frame to account for the status bar change. */
viewcontroller.view.frame = UIKit_ComputeViewFrame(window, data.uiwindow.screen);
#endif /* !TARGET_OS_TV */
#ifdef SDL_IPHONE_KEYBOARD
/* Make sure the view is offset correctly when the keyboard is visible. */
@@ -363,6 +369,7 @@ UIKit_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
}
}
#if !TARGET_OS_TV
NSUInteger
UIKit_GetSupportedOrientations(SDL_Window * window)
{
@@ -428,6 +435,7 @@ UIKit_GetSupportedOrientations(SDL_Window * window)
return orientationMask;
}
#endif /* !TARGET_OS_TV */
int
SDL_iPhoneSetAnimationCallback(SDL_Window * window, int interval, void (*callback)(void*), void *callbackParam)