src/video/uikit/SDL_uikitmodes.m
author Sam Lantinga <slouken@libsdl.org>
Mon, 10 Sep 2018 23:00:09 -0700
changeset 12183 364f514f94d8
parent 12144 c48e1ae67968
child 12201 8bdc4d340419
permissions -rw-r--r--
Fixed building on tvOS
slouken@6518
     1
/*
slouken@6518
     2
  Simple DirectMedia Layer
slouken@11811
     3
  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
slouken@6518
     4
slouken@6518
     5
  This software is provided 'as-is', without any express or implied
slouken@6518
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@6518
     7
  arising from the use of this software.
slouken@6518
     8
slouken@6518
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@6518
    10
  including commercial applications, and to alter it and redistribute it
slouken@6518
    11
  freely, subject to the following restrictions:
slouken@6518
    12
slouken@6518
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@6518
    14
     claim that you wrote the original software. If you use this software
slouken@6518
    15
     in a product, an acknowledgment in the product documentation would be
slouken@6518
    16
     appreciated but is not required.
slouken@6518
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@6518
    18
     misrepresented as being the original software.
slouken@6518
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@6518
    20
*/
icculus@8093
    21
#include "../../SDL_internal.h"
slouken@6518
    22
slouken@6518
    23
#if SDL_VIDEO_DRIVER_UIKIT
slouken@6518
    24
slouken@6518
    25
#include "SDL_assert.h"
slouken@6518
    26
#include "SDL_uikitmodes.h"
slouken@6518
    27
slouken@12144
    28
#include "../../events/SDL_events_c.h"
slouken@12144
    29
slime73@9510
    30
@implementation SDL_DisplayData
slime73@9510
    31
slime73@9510
    32
@synthesize uiscreen;
slime73@9510
    33
slime73@9510
    34
@end
slime73@9510
    35
slime73@9510
    36
@implementation SDL_DisplayModeData
slime73@9510
    37
slime73@9510
    38
@synthesize uiscreenmode;
slime73@9510
    39
slime73@9510
    40
@end
slime73@9510
    41
slouken@6518
    42
slouken@6518
    43
static int
slouken@6518
    44
UIKit_AllocateDisplayModeData(SDL_DisplayMode * mode,
slime73@9488
    45
    UIScreenMode * uiscreenmode)
slouken@6518
    46
{
slime73@9510
    47
    SDL_DisplayModeData *data = nil;
slouken@7191
    48
slouken@6518
    49
    if (uiscreenmode != nil) {
slouken@6518
    50
        /* Allocate the display mode data */
slime73@9510
    51
        data = [[SDL_DisplayModeData alloc] init];
slouken@6518
    52
        if (!data) {
icculus@7037
    53
            return SDL_OutOfMemory();
slouken@6518
    54
        }
slouken@7191
    55
slime73@9510
    56
        data.uiscreenmode = uiscreenmode;
slouken@6518
    57
    }
slouken@7191
    58
slime73@9510
    59
    mode->driverdata = (void *) CFBridgingRetain(data);
slouken@7191
    60
slouken@6518
    61
    return 0;
slouken@6518
    62
}
slouken@6518
    63
slouken@6518
    64
static void
slouken@6518
    65
UIKit_FreeDisplayModeData(SDL_DisplayMode * mode)
slouken@6518
    66
{
slouken@8891
    67
    if (mode->driverdata != NULL) {
slime73@9510
    68
        CFRelease(mode->driverdata);
slouken@6518
    69
        mode->driverdata = NULL;
slouken@6518
    70
    }
slouken@6518
    71
}
slouken@6518
    72
slime73@11086
    73
static NSUInteger
slime73@11086
    74
UIKit_GetDisplayModeRefreshRate(UIScreen *uiscreen)
slime73@11086
    75
{
slime73@11086
    76
#ifdef __IPHONE_10_3
slime73@11086
    77
    if ([uiscreen respondsToSelector:@selector(maximumFramesPerSecond)]) {
slime73@11086
    78
        return uiscreen.maximumFramesPerSecond;
slime73@11086
    79
    }
slime73@11086
    80
#endif
slime73@11086
    81
    return 0;
slime73@11086
    82
}
slime73@11086
    83
slouken@6518
    84
static int
slouken@6518
    85
UIKit_AddSingleDisplayMode(SDL_VideoDisplay * display, int w, int h,
slime73@11086
    86
    UIScreen * uiscreen, UIScreenMode * uiscreenmode)
slouken@6518
    87
{
slouken@6518
    88
    SDL_DisplayMode mode;
slouken@6518
    89
    SDL_zero(mode);
slouken@7191
    90
slime73@9488
    91
    if (UIKit_AllocateDisplayModeData(&mode, uiscreenmode) < 0) {
slouken@6518
    92
        return -1;
slouken@6518
    93
    }
slouken@7191
    94
slime73@11086
    95
    mode.format = SDL_PIXELFORMAT_ABGR8888;
slime73@11086
    96
    mode.refresh_rate = (int) UIKit_GetDisplayModeRefreshRate(uiscreen);
slouken@6518
    97
    mode.w = w;
slouken@6518
    98
    mode.h = h;
slime73@11086
    99
slouken@6518
   100
    if (SDL_AddDisplayMode(display, &mode)) {
slouken@6518
   101
        return 0;
slouken@6518
   102
    } else {
slouken@6518
   103
        UIKit_FreeDisplayModeData(&mode);
slouken@6518
   104
        return -1;
slouken@6518
   105
    }
slouken@6518
   106
}
slouken@6518
   107
slouken@6518
   108
static int
slime73@11086
   109
UIKit_AddDisplayMode(SDL_VideoDisplay * display, int w, int h, UIScreen * uiscreen,
slouken@6520
   110
                     UIScreenMode * uiscreenmode, SDL_bool addRotation)
slouken@6518
   111
{
slime73@11086
   112
    if (UIKit_AddSingleDisplayMode(display, w, h, uiscreen, uiscreenmode) < 0) {
slouken@6518
   113
        return -1;
slouken@6518
   114
    }
slouken@7191
   115
slouken@6520
   116
    if (addRotation) {
slouken@7191
   117
        /* Add the rotated version */
slime73@11086
   118
        if (UIKit_AddSingleDisplayMode(display, h, w, uiscreen, uiscreenmode) < 0) {
slouken@6518
   119
            return -1;
slouken@6518
   120
        }
slouken@6518
   121
    }
slouken@7191
   122
slouken@6518
   123
    return 0;
slouken@6518
   124
}
slouken@6518
   125
slouken@6518
   126
static int
slouken@6518
   127
UIKit_AddDisplay(UIScreen *uiscreen)
slouken@6518
   128
{
slime73@11086
   129
    UIScreenMode *uiscreenmode = uiscreen.currentMode;
slime73@9532
   130
    CGSize size = uiscreen.bounds.size;
slime73@11086
   131
    SDL_VideoDisplay display;
slime73@11086
   132
    SDL_DisplayMode mode;
slime73@11086
   133
    SDL_zero(mode);
slouken@6518
   134
slouken@7191
   135
    /* Make sure the width/height are oriented correctly */
slouken@6520
   136
    if (UIKit_IsDisplayLandscape(uiscreen) != (size.width > size.height)) {
slouken@6520
   137
        CGFloat height = size.width;
slouken@6520
   138
        size.width = size.height;
slouken@6520
   139
        size.height = height;
slouken@6520
   140
    }
slouken@6520
   141
slouken@6518
   142
    mode.format = SDL_PIXELFORMAT_ABGR8888;
slime73@11086
   143
    mode.refresh_rate = (int) UIKit_GetDisplayModeRefreshRate(uiscreen);
slime73@9488
   144
    mode.w = (int) size.width;
slime73@9488
   145
    mode.h = (int) size.height;
slouken@7191
   146
slime73@9488
   147
    if (UIKit_AllocateDisplayModeData(&mode, uiscreenmode) < 0) {
slouken@6518
   148
        return -1;
slouken@6518
   149
    }
slouken@6518
   150
slouken@6518
   151
    SDL_zero(display);
slouken@6518
   152
    display.desktop_mode = mode;
slouken@6518
   153
    display.current_mode = mode;
slouken@6518
   154
slouken@6518
   155
    /* Allocate the display data */
slime73@9510
   156
    SDL_DisplayData *data = [[SDL_DisplayData alloc] init];
slouken@6518
   157
    if (!data) {
slouken@6518
   158
        UIKit_FreeDisplayModeData(&display.desktop_mode);
icculus@7037
   159
        return SDL_OutOfMemory();
slouken@6518
   160
    }
slouken@7191
   161
slime73@9510
   162
    data.uiscreen = uiscreen;
slouken@7191
   163
slime73@9510
   164
    display.driverdata = (void *) CFBridgingRetain(data);
slouken@6518
   165
    SDL_AddVideoDisplay(&display);
slouken@7191
   166
slouken@6518
   167
    return 0;
slouken@6518
   168
}
slouken@6518
   169
slouken@6520
   170
SDL_bool
slouken@6520
   171
UIKit_IsDisplayLandscape(UIScreen *uiscreen)
slouken@6520
   172
{
slime73@10340
   173
#if !TARGET_OS_TV
slouken@6520
   174
    if (uiscreen == [UIScreen mainScreen]) {
slime73@9532
   175
        return UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation);
slime73@10340
   176
    } else
slime73@10340
   177
#endif /* !TARGET_OS_TV */
slime73@10340
   178
    {
slime73@9532
   179
        CGSize size = uiscreen.bounds.size;
slouken@6520
   180
        return (size.width > size.height);
slouken@6520
   181
    }
slouken@6520
   182
}
slouken@6518
   183
slouken@6518
   184
int
slouken@6518
   185
UIKit_InitModes(_THIS)
slouken@6518
   186
{
slime73@9506
   187
    @autoreleasepool {
slime73@9506
   188
        for (UIScreen *uiscreen in [UIScreen screens]) {
slime73@9506
   189
            if (UIKit_AddDisplay(uiscreen) < 0) {
slime73@9506
   190
                return -1;
slime73@9506
   191
            }
slouken@6518
   192
        }
slouken@12144
   193
#if !TARGET_OS_TV
slouken@12144
   194
		SDL_OnApplicationDidChangeStatusBarOrientation();
slouken@12144
   195
#endif
slouken@6518
   196
    }
slouken@6518
   197
slouken@6518
   198
    return 0;
slouken@6518
   199
}
slouken@6518
   200
slouken@6518
   201
void
slouken@6518
   202
UIKit_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
slouken@6518
   203
{
slime73@9510
   204
    @autoreleasepool {
slime73@9510
   205
        SDL_DisplayData *data = (__bridge SDL_DisplayData *) display->driverdata;
slouken@6518
   206
slime73@9510
   207
        SDL_bool isLandscape = UIKit_IsDisplayLandscape(data.uiscreen);
slime73@9510
   208
        SDL_bool addRotation = (data.uiscreen == [UIScreen mainScreen]);
slime73@9510
   209
        CGFloat scale = data.uiscreen.scale;
slime73@10340
   210
        NSArray *availableModes = nil;
slime73@10340
   211
slime73@10340
   212
#if TARGET_OS_TV
slime73@10340
   213
        addRotation = SDL_FALSE;
slime73@10340
   214
        availableModes = @[data.uiscreen.currentMode];
slime73@10340
   215
#else
slime73@10340
   216
        availableModes = data.uiscreen.availableModes;
slime73@10340
   217
#endif
slouken@6520
   218
slouken@11506
   219
#ifdef __IPHONE_8_0
slime73@9517
   220
        /* The UIScreenMode of an iPhone 6 Plus should be 1080x1920 rather than
slime73@9517
   221
         * 1242x2208 (414x736@3x), so we should use the native scale. */
slime73@9517
   222
        if ([data.uiscreen respondsToSelector:@selector(nativeScale)]) {
slime73@9517
   223
            scale = data.uiscreen.nativeScale;
slime73@9517
   224
        }
slouken@11506
   225
#endif
slime73@9517
   226
slime73@10340
   227
        for (UIScreenMode *uimode in availableModes) {
slime73@9517
   228
            /* The size of a UIScreenMode is in pixels, but we deal exclusively
slime73@9517
   229
             * in points (except in SDL_GL_GetDrawableSize.) */
slime73@9517
   230
            int w = (int)(uimode.size.width / scale);
slime73@9517
   231
            int h = (int)(uimode.size.height / scale);
slouken@6520
   232
slime73@9506
   233
            /* Make sure the width/height are oriented correctly */
slime73@9506
   234
            if (isLandscape != (w > h)) {
slime73@9506
   235
                int tmp = w;
slime73@9506
   236
                w = h;
slime73@9506
   237
                h = tmp;
slime73@9506
   238
            }
slime73@9506
   239
slime73@11086
   240
            UIKit_AddDisplayMode(display, w, h, data.uiscreen, uimode, addRotation);
slouken@6520
   241
        }
slouken@7191
   242
    }
slouken@6518
   243
}
slouken@6518
   244
slouken@6518
   245
int
slouken@6518
   246
UIKit_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
slouken@6518
   247
{
slime73@9510
   248
    @autoreleasepool {
slime73@9510
   249
        SDL_DisplayData *data = (__bridge SDL_DisplayData *) display->driverdata;
slime73@10340
   250
slime73@10340
   251
#if !TARGET_OS_TV
slime73@9510
   252
        SDL_DisplayModeData *modedata = (__bridge SDL_DisplayModeData *)mode->driverdata;
slime73@9510
   253
        [data.uiscreen setCurrentMode:modedata.uiscreenmode];
slime73@10340
   254
#endif
slouken@6518
   255
slime73@9510
   256
        if (data.uiscreen == [UIScreen mainScreen]) {
slime73@9532
   257
            /* [UIApplication setStatusBarOrientation:] no longer works reliably
slime73@9532
   258
             * in recent iOS versions, so we can't rotate the screen when setting
slime73@9532
   259
             * the display mode. */
slime73@9506
   260
            if (mode->w > mode->h) {
slime73@9510
   261
                if (!UIKit_IsDisplayLandscape(data.uiscreen)) {
slime73@9532
   262
                    return SDL_SetError("Screen orientation does not match display mode size");
slime73@9506
   263
                }
slime73@9506
   264
            } else if (mode->w < mode->h) {
slime73@9510
   265
                if (UIKit_IsDisplayLandscape(data.uiscreen)) {
slime73@9532
   266
                    return SDL_SetError("Screen orientation does not match display mode size");
slime73@9506
   267
                }
slouken@6520
   268
            }
slouken@6518
   269
        }
slouken@6518
   270
    }
slouken@8891
   271
slouken@6518
   272
    return 0;
slouken@6518
   273
}
slouken@6518
   274
icculus@10019
   275
int
icculus@10019
   276
UIKit_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect)
icculus@10019
   277
{
slime73@10340
   278
    @autoreleasepool {
slime73@10340
   279
        int displayIndex = (int) (display - _this->displays);
slime73@10340
   280
        SDL_DisplayData *data = (__bridge SDL_DisplayData *) display->driverdata;
slime73@11147
   281
        CGRect frame = data.uiscreen.bounds;
slime73@10340
   282
slime73@10340
   283
        /* the default function iterates displays to make a fake offset,
slime73@10340
   284
         as if all the displays were side-by-side, which is fine for iOS. */
slime73@10340
   285
        if (SDL_GetDisplayBounds(displayIndex, rect) < 0) {
slime73@10340
   286
            return -1;
slime73@10340
   287
        }
slime73@10340
   288
slime73@11147
   289
#if !TARGET_OS_TV && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0
slime73@10340
   290
        if (!UIKit_IsSystemVersionAtLeast(7.0)) {
slime73@10340
   291
            frame = [data.uiscreen applicationFrame];
slime73@10340
   292
        }
slime73@10340
   293
#endif
slime73@10340
   294
slime73@10340
   295
        rect->x += frame.origin.x;
slime73@10340
   296
        rect->y += frame.origin.y;
slime73@10340
   297
        rect->w = frame.size.width;
slime73@10340
   298
        rect->h = frame.size.height;
icculus@10019
   299
    }
icculus@10019
   300
icculus@10019
   301
    return 0;
icculus@10019
   302
}
icculus@10019
   303
slouken@6518
   304
void
slouken@6518
   305
UIKit_QuitModes(_THIS)
slouken@6518
   306
{
slouken@7191
   307
    /* Release Objective-C objects, so higher level doesn't free() them. */
slouken@6518
   308
    int i, j;
slime73@9506
   309
    @autoreleasepool {
slime73@9506
   310
        for (i = 0; i < _this->num_displays; i++) {
slime73@9506
   311
            SDL_VideoDisplay *display = &_this->displays[i];
slouken@6518
   312
slime73@9506
   313
            UIKit_FreeDisplayModeData(&display->desktop_mode);
slime73@9506
   314
            for (j = 0; j < display->num_display_modes; j++) {
slime73@9506
   315
                SDL_DisplayMode *mode = &display->display_modes[j];
slime73@9506
   316
                UIKit_FreeDisplayModeData(mode);
slime73@9506
   317
            }
slime73@9506
   318
slime73@9510
   319
            if (display->driverdata != NULL) {
slime73@9510
   320
                CFRelease(display->driverdata);
slime73@9510
   321
                display->driverdata = NULL;
slime73@9510
   322
            }
slouken@6518
   323
        }
slouken@6518
   324
    }
slouken@6518
   325
}
slouken@6518
   326
slouken@12183
   327
#if !TARGET_OS_TV
slouken@12144
   328
void SDL_OnApplicationDidChangeStatusBarOrientation()
slouken@12144
   329
{
slouken@12144
   330
    BOOL isLandscape = UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation);
slouken@12144
   331
    SDL_VideoDisplay *display = SDL_GetDisplay(0);
slouken@12144
   332
slouken@12144
   333
    if (display) {
slouken@12144
   334
        SDL_DisplayMode *desktopmode = &display->desktop_mode;
slouken@12144
   335
        SDL_DisplayMode *currentmode = &display->current_mode;
slouken@12144
   336
        SDL_DisplayOrientation orientation = SDL_ORIENTATION_UNKNOWN;
slouken@12144
   337
slouken@12144
   338
        /* The desktop display mode should be kept in sync with the screen
slouken@12144
   339
         * orientation so that updating a window's fullscreen state to
slouken@12144
   340
         * SDL_WINDOW_FULLSCREEN_DESKTOP keeps the window dimensions in the
slouken@12144
   341
         * correct orientation. */
slouken@12144
   342
        if (isLandscape != (desktopmode->w > desktopmode->h)) {
slouken@12144
   343
            int height = desktopmode->w;
slouken@12144
   344
            desktopmode->w = desktopmode->h;
slouken@12144
   345
            desktopmode->h = height;
slouken@12144
   346
        }
slouken@12144
   347
slouken@12144
   348
        /* Same deal with the current mode + SDL_GetCurrentDisplayMode. */
slouken@12144
   349
        if (isLandscape != (currentmode->w > currentmode->h)) {
slouken@12144
   350
            int height = currentmode->w;
slouken@12144
   351
            currentmode->w = currentmode->h;
slouken@12144
   352
            currentmode->h = height;
slouken@12144
   353
        }
slouken@12144
   354
slouken@12144
   355
        switch ([UIApplication sharedApplication].statusBarOrientation) {
slouken@12144
   356
        case UIInterfaceOrientationPortrait:
slouken@12144
   357
            orientation = SDL_ORIENTATION_PORTRAIT;
slouken@12144
   358
            break;
slouken@12144
   359
        case UIInterfaceOrientationPortraitUpsideDown:
slouken@12144
   360
            orientation = SDL_ORIENTATION_PORTRAIT_FLIPPED;
slouken@12144
   361
            break;
slouken@12144
   362
        case UIInterfaceOrientationLandscapeLeft:
slouken@12144
   363
            /* Bug: UIInterfaceOrientationLandscapeLeft/Right are reversed - http://openradar.appspot.com/7216046 */
slouken@12144
   364
            orientation = SDL_ORIENTATION_LANDSCAPE_FLIPPED;
slouken@12144
   365
            break;
slouken@12144
   366
        case UIInterfaceOrientationLandscapeRight:
slouken@12144
   367
            /* Bug: UIInterfaceOrientationLandscapeLeft/Right are reversed - http://openradar.appspot.com/7216046 */
slouken@12144
   368
            orientation = SDL_ORIENTATION_LANDSCAPE;
slouken@12144
   369
            break;
slouken@12144
   370
        default:
slouken@12144
   371
            break;
slouken@12144
   372
        }
slouken@12144
   373
        SDL_SendDisplayEvent(display, SDL_DISPLAYEVENT_ORIENTATION, orientation);
slouken@12144
   374
    }
slouken@12144
   375
}
slouken@12183
   376
#endif /* !TARGET_OS_TV */
slouken@12144
   377
slouken@6518
   378
#endif /* SDL_VIDEO_DRIVER_UIKIT */
slouken@6518
   379
slouken@6518
   380
/* vi: set ts=4 sw=4 expandtab: */