• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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