1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "../../SDL_internal.h" 22 23#if SDL_VIDEO_DRIVER_UIKIT 24 25#include "SDL_assert.h" 26#include "SDL_uikitmodes.h" 27 28@implementation SDL_DisplayData 29 30@synthesize uiscreen; 31 32@end 33 34@implementation SDL_DisplayModeData 35 36@synthesize uiscreenmode; 37 38@end 39 40 41static int 42UIKit_AllocateDisplayModeData(SDL_DisplayMode * mode, 43 UIScreenMode * uiscreenmode) 44{ 45 SDL_DisplayModeData *data = nil; 46 47 if (uiscreenmode != nil) { 48 /* Allocate the display mode data */ 49 data = [[SDL_DisplayModeData alloc] init]; 50 if (!data) { 51 return SDL_OutOfMemory(); 52 } 53 54 data.uiscreenmode = uiscreenmode; 55 } 56 57 mode->driverdata = (void *) CFBridgingRetain(data); 58 59 return 0; 60} 61 62static void 63UIKit_FreeDisplayModeData(SDL_DisplayMode * mode) 64{ 65 if (mode->driverdata != NULL) { 66 CFRelease(mode->driverdata); 67 mode->driverdata = NULL; 68 } 69} 70 71static int 72UIKit_AddSingleDisplayMode(SDL_VideoDisplay * display, int w, int h, 73 UIScreenMode * uiscreenmode) 74{ 75 SDL_DisplayMode mode; 76 SDL_zero(mode); 77 78 mode.format = SDL_PIXELFORMAT_ABGR8888; 79 mode.refresh_rate = 0; 80 if (UIKit_AllocateDisplayModeData(&mode, uiscreenmode) < 0) { 81 return -1; 82 } 83 84 mode.w = w; 85 mode.h = h; 86 if (SDL_AddDisplayMode(display, &mode)) { 87 return 0; 88 } else { 89 UIKit_FreeDisplayModeData(&mode); 90 return -1; 91 } 92} 93 94static int 95UIKit_AddDisplayMode(SDL_VideoDisplay * display, int w, int h, 96 UIScreenMode * uiscreenmode, SDL_bool addRotation) 97{ 98 if (UIKit_AddSingleDisplayMode(display, w, h, uiscreenmode) < 0) { 99 return -1; 100 } 101 102 if (addRotation) { 103 /* Add the rotated version */ 104 if (UIKit_AddSingleDisplayMode(display, h, w, uiscreenmode) < 0) { 105 return -1; 106 } 107 } 108 109 return 0; 110} 111 112static int 113UIKit_AddDisplay(UIScreen *uiscreen) 114{ 115 CGSize size = uiscreen.bounds.size; 116 117 /* Make sure the width/height are oriented correctly */ 118 if (UIKit_IsDisplayLandscape(uiscreen) != (size.width > size.height)) { 119 CGFloat height = size.width; 120 size.width = size.height; 121 size.height = height; 122 } 123 124 SDL_VideoDisplay display; 125 SDL_DisplayMode mode; 126 SDL_zero(mode); 127 mode.format = SDL_PIXELFORMAT_ABGR8888; 128 mode.w = (int) size.width; 129 mode.h = (int) size.height; 130 131 UIScreenMode *uiscreenmode = uiscreen.currentMode; 132 133 if (UIKit_AllocateDisplayModeData(&mode, uiscreenmode) < 0) { 134 return -1; 135 } 136 137 SDL_zero(display); 138 display.desktop_mode = mode; 139 display.current_mode = mode; 140 141 /* Allocate the display data */ 142 SDL_DisplayData *data = [[SDL_DisplayData alloc] init]; 143 if (!data) { 144 UIKit_FreeDisplayModeData(&display.desktop_mode); 145 return SDL_OutOfMemory(); 146 } 147 148 data.uiscreen = uiscreen; 149 150 display.driverdata = (void *) CFBridgingRetain(data); 151 SDL_AddVideoDisplay(&display); 152 153 return 0; 154} 155 156SDL_bool 157UIKit_IsDisplayLandscape(UIScreen *uiscreen) 158{ 159#if !TARGET_OS_TV 160 if (uiscreen == [UIScreen mainScreen]) { 161 return UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation); 162 } else 163#endif /* !TARGET_OS_TV */ 164 { 165 CGSize size = uiscreen.bounds.size; 166 return (size.width > size.height); 167 } 168} 169 170int 171UIKit_InitModes(_THIS) 172{ 173 @autoreleasepool { 174 for (UIScreen *uiscreen in [UIScreen screens]) { 175 if (UIKit_AddDisplay(uiscreen) < 0) { 176 return -1; 177 } 178 } 179 } 180 181 return 0; 182} 183 184void 185UIKit_GetDisplayModes(_THIS, SDL_VideoDisplay * display) 186{ 187 @autoreleasepool { 188 SDL_DisplayData *data = (__bridge SDL_DisplayData *) display->driverdata; 189 190 SDL_bool isLandscape = UIKit_IsDisplayLandscape(data.uiscreen); 191 SDL_bool addRotation = (data.uiscreen == [UIScreen mainScreen]); 192 CGFloat scale = data.uiscreen.scale; 193 NSArray *availableModes = nil; 194 195#if TARGET_OS_TV 196 addRotation = SDL_FALSE; 197 availableModes = @[data.uiscreen.currentMode]; 198#else 199 availableModes = data.uiscreen.availableModes; 200#endif 201 202#ifdef __IPHONE_8_0 203 /* The UIScreenMode of an iPhone 6 Plus should be 1080x1920 rather than 204 * 1242x2208 (414x736@3x), so we should use the native scale. */ 205 if ([data.uiscreen respondsToSelector:@selector(nativeScale)]) { 206 scale = data.uiscreen.nativeScale; 207 } 208#endif 209 210 for (UIScreenMode *uimode in availableModes) { 211 /* The size of a UIScreenMode is in pixels, but we deal exclusively 212 * in points (except in SDL_GL_GetDrawableSize.) */ 213 int w = (int)(uimode.size.width / scale); 214 int h = (int)(uimode.size.height / scale); 215 216 /* Make sure the width/height are oriented correctly */ 217 if (isLandscape != (w > h)) { 218 int tmp = w; 219 w = h; 220 h = tmp; 221 } 222 223 UIKit_AddDisplayMode(display, w, h, uimode, addRotation); 224 } 225 } 226} 227 228int 229UIKit_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode) 230{ 231 @autoreleasepool { 232 SDL_DisplayData *data = (__bridge SDL_DisplayData *) display->driverdata; 233 234#if !TARGET_OS_TV 235 SDL_DisplayModeData *modedata = (__bridge SDL_DisplayModeData *)mode->driverdata; 236 [data.uiscreen setCurrentMode:modedata.uiscreenmode]; 237#endif 238 239 if (data.uiscreen == [UIScreen mainScreen]) { 240 /* [UIApplication setStatusBarOrientation:] no longer works reliably 241 * in recent iOS versions, so we can't rotate the screen when setting 242 * the display mode. */ 243 if (mode->w > mode->h) { 244 if (!UIKit_IsDisplayLandscape(data.uiscreen)) { 245 return SDL_SetError("Screen orientation does not match display mode size"); 246 } 247 } else if (mode->w < mode->h) { 248 if (UIKit_IsDisplayLandscape(data.uiscreen)) { 249 return SDL_SetError("Screen orientation does not match display mode size"); 250 } 251 } 252 } 253 } 254 255 return 0; 256} 257 258int 259UIKit_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) 260{ 261 @autoreleasepool { 262 int displayIndex = (int) (display - _this->displays); 263 SDL_DisplayData *data = (__bridge SDL_DisplayData *) display->driverdata; 264 265 /* the default function iterates displays to make a fake offset, 266 as if all the displays were side-by-side, which is fine for iOS. */ 267 if (SDL_GetDisplayBounds(displayIndex, rect) < 0) { 268 return -1; 269 } 270 271 CGRect frame = data.uiscreen.bounds; 272 273#if !TARGET_OS_TV 274 if (!UIKit_IsSystemVersionAtLeast(7.0)) { 275 frame = [data.uiscreen applicationFrame]; 276 } 277#endif 278 279 rect->x += frame.origin.x; 280 rect->y += frame.origin.y; 281 rect->w = frame.size.width; 282 rect->h = frame.size.height; 283 } 284 285 return 0; 286} 287 288void 289UIKit_QuitModes(_THIS) 290{ 291 /* Release Objective-C objects, so higher level doesn't free() them. */ 292 int i, j; 293 @autoreleasepool { 294 for (i = 0; i < _this->num_displays; i++) { 295 SDL_VideoDisplay *display = &_this->displays[i]; 296 297 UIKit_FreeDisplayModeData(&display->desktop_mode); 298 for (j = 0; j < display->num_display_modes; j++) { 299 SDL_DisplayMode *mode = &display->display_modes[j]; 300 UIKit_FreeDisplayModeData(mode); 301 } 302 303 if (display->driverdata != NULL) { 304 CFRelease(display->driverdata); 305 display->driverdata = NULL; 306 } 307 } 308 } 309} 310 311#endif /* SDL_VIDEO_DRIVER_UIKIT */ 312 313/* vi: set ts=4 sw=4 expandtab: */ 314