src/video/uikit/SDL_uikitvideo.m
author Tim Angus
Wed, 25 Jan 2012 14:29:56 +0000
changeset 6267 ed2cf4669e55
parent 6138 4c64952a58fb
child 6292 4c25a7b9e819
permissions -rwxr-xr-x
* On iOS, fix support for the case where [UIScreen scale] is not 1.0 (retina)
* Allow selection of non-retina modes on retina devices
slouken@2765
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@6138
     3
  Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
slouken@2765
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@2765
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@2765
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@2765
    20
*/
slouken@6044
    21
#include "SDL_config.h"
slouken@6044
    22
slouken@6044
    23
#if SDL_VIDEO_DRIVER_UIKIT
icculus@4443
    24
icculus@4443
    25
#import <UIKit/UIKit.h>
icculus@4443
    26
slouken@2765
    27
#include "SDL_video.h"
slouken@2765
    28
#include "SDL_mouse.h"
slouken@2765
    29
#include "../SDL_sysvideo.h"
slouken@2765
    30
#include "../SDL_pixels_c.h"
slouken@2765
    31
#include "../../events/SDL_events_c.h"
slouken@2765
    32
slouken@2765
    33
#include "SDL_uikitvideo.h"
slouken@2765
    34
#include "SDL_uikitevents.h"
slouken@2765
    35
#include "SDL_uikitwindow.h"
slouken@2765
    36
#include "SDL_uikitopengles.h"
slouken@2765
    37
icculus@4443
    38
#include "SDL_assert.h"
icculus@4443
    39
slouken@2765
    40
#define UIKITVID_DRIVER_NAME "uikit"
slouken@2765
    41
slouken@2765
    42
/* Initialization/Query functions */
slouken@2765
    43
static int UIKit_VideoInit(_THIS);
icculus@4443
    44
static void UIKit_GetDisplayModes(_THIS, SDL_VideoDisplay * sdl_display);
slouken@3643
    45
static int UIKit_SetDisplayMode(_THIS, SDL_VideoDisplay * display,
slouken@3643
    46
                                SDL_DisplayMode * mode);
slouken@2765
    47
static void UIKit_VideoQuit(_THIS);
slouken@2765
    48
icculus@4446
    49
BOOL SDL_UIKit_supports_multiple_displays = NO;
icculus@4443
    50
slouken@2765
    51
/* DUMMY driver bootstrap functions */
slouken@2765
    52
slouken@2765
    53
static int
slouken@2765
    54
UIKit_Available(void)
slouken@2765
    55
{
kees@6003
    56
    return 1;
slouken@2765
    57
}
slouken@2765
    58
slouken@2765
    59
static void UIKit_DeleteDevice(SDL_VideoDevice * device)
slouken@2765
    60
{
slouken@2765
    61
    SDL_free(device);
slouken@2765
    62
}
slouken@2765
    63
slouken@2765
    64
static SDL_VideoDevice *
slouken@2765
    65
UIKit_CreateDevice(int devindex)
slouken@2765
    66
{
slouken@2765
    67
    SDL_VideoDevice *device;
slouken@2765
    68
slouken@2765
    69
    /* Initialize all variables that we clean on shutdown */
slouken@2765
    70
    device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
slouken@2765
    71
    if (!device) {
slouken@2765
    72
        SDL_OutOfMemory();
slouken@2765
    73
        if (device) {
slouken@2765
    74
            SDL_free(device);
slouken@2765
    75
        }
slouken@2765
    76
        return (0);
slouken@2765
    77
    }
slouken@2765
    78
slouken@2765
    79
    /* Set the function pointers */
slouken@2765
    80
    device->VideoInit = UIKit_VideoInit;
slouken@2765
    81
    device->VideoQuit = UIKit_VideoQuit;
icculus@4443
    82
    device->GetDisplayModes = UIKit_GetDisplayModes;
slouken@2765
    83
    device->SetDisplayMode = UIKit_SetDisplayMode;
slouken@2765
    84
    device->PumpEvents = UIKit_PumpEvents;
slouken@5131
    85
    device->CreateWindow = UIKit_CreateWindow;
slouken@6073
    86
    device->SetWindowFullscreen = UIKit_SetWindowFullscreen;
slouken@5131
    87
    device->DestroyWindow = UIKit_DestroyWindow;
slouken@5056
    88
    device->GetWindowWMInfo = UIKit_GetWindowWMInfo;
kees@6001
    89
kees@6001
    90
slouken@5131
    91
    /* OpenGL (ES) functions */
slouken@5131
    92
    device->GL_MakeCurrent        = UIKit_GL_MakeCurrent;
slouken@5131
    93
    device->GL_SwapWindow        = UIKit_GL_SwapWindow;
slouken@5131
    94
    device->GL_CreateContext    = UIKit_GL_CreateContext;
slouken@5131
    95
    device->GL_DeleteContext    = UIKit_GL_DeleteContext;
slouken@5131
    96
    device->GL_GetProcAddress   = UIKit_GL_GetProcAddress;
slouken@5131
    97
    device->GL_LoadLibrary        = UIKit_GL_LoadLibrary;
slouken@5131
    98
    device->free = UIKit_DeleteDevice;
slouken@2765
    99
slouken@5131
   100
    device->gl_config.accelerated = 1;
slouken@5131
   101
slouken@2765
   102
    return device;
slouken@2765
   103
}
slouken@2765
   104
slouken@2765
   105
VideoBootStrap UIKIT_bootstrap = {
slouken@2765
   106
    UIKITVID_DRIVER_NAME, "SDL UIKit video driver",
slouken@2765
   107
    UIKit_Available, UIKit_CreateDevice
slouken@2765
   108
};
slouken@2765
   109
slouken@2765
   110
icculus@4443
   111
/*
icculus@4443
   112
!!! FIXME:
icculus@4443
   113
icculus@4443
   114
The main screen should list a AxB mode for portrait orientation, and then
icculus@4443
   115
 also list BxA for landscape mode. When setting a given resolution, we should
icculus@4443
   116
 rotate the view's transform appropriately (extra credit if you check the
icculus@4443
   117
 accelerometer and rotate the display so it's never upside down).
icculus@4443
   118
icculus@4443
   119
  http://iphonedevelopment.blogspot.com/2008/10/starting-in-landscape-mode-without.html
icculus@4443
   120
icculus@4443
   121
*/
icculus@4443
   122
tim@6267
   123
static int
tim@6267
   124
UIKit_AddSingleDisplayMode(SDL_VideoDisplay * display, int w, int h,
tim@6267
   125
    UIScreenMode * uiscreenmode, CGFloat scale)
tim@6267
   126
{
tim@6267
   127
    SDL_DisplayMode mode;
tim@6267
   128
    SDL_zero(mode);
tim@6267
   129
    
tim@6267
   130
    SDL_DisplayModeData *data = NULL;
tim@6267
   131
tim@6267
   132
    if (uiscreenmode != nil) {
tim@6267
   133
        /* Allocate the display mode data */
tim@6267
   134
        data = (SDL_DisplayModeData *) SDL_malloc(sizeof(*data));
tim@6267
   135
        if (!data) {
tim@6267
   136
            SDL_OutOfMemory();
tim@6267
   137
            return -1;
tim@6267
   138
        }
tim@6267
   139
        
tim@6267
   140
        data->uiscreenmode = uiscreenmode;
tim@6267
   141
        data->scale = scale;
tim@6267
   142
    }
tim@6267
   143
    
tim@6267
   144
    mode.format = SDL_PIXELFORMAT_ABGR8888;
tim@6267
   145
    mode.refresh_rate = 0;
tim@6267
   146
    mode.driverdata = data;
tim@6267
   147
    
tim@6267
   148
    mode.w = w;
tim@6267
   149
    mode.h = h;
tim@6267
   150
    if (SDL_AddDisplayMode(display, &mode)) {
tim@6267
   151
        if (uiscreenmode != nil) {
tim@6267
   152
            [uiscreenmode retain];
tim@6267
   153
        }
tim@6267
   154
        
tim@6267
   155
        return 0;
tim@6267
   156
    }
tim@6267
   157
    
tim@6267
   158
    SDL_free(data);
tim@6267
   159
tim@6267
   160
    return -1;
tim@6267
   161
}
tim@6267
   162
tim@6267
   163
static int
tim@6267
   164
UIKit_AddDisplayMode(SDL_VideoDisplay * display, int w, int h, CGFloat scale,
tim@6267
   165
                     UIScreenMode * uiscreenmode, BOOL rotated)
tim@6267
   166
{
tim@6267
   167
    if (UIKit_AddSingleDisplayMode(display, w, h, uiscreenmode, scale) < 0) {
tim@6267
   168
        return -1;
tim@6267
   169
    }
tim@6267
   170
    
tim@6267
   171
    if (rotated) {
tim@6267
   172
        // Add the rotated version
tim@6267
   173
        if (UIKit_AddSingleDisplayMode(display, h, w, uiscreenmode, scale) < 0) {
tim@6267
   174
            return -1;
tim@6267
   175
        }
tim@6267
   176
    }
tim@6267
   177
    
tim@6267
   178
    return 0;
tim@6267
   179
}
tim@6267
   180
icculus@4443
   181
static void
icculus@4443
   182
UIKit_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
icculus@4443
   183
{
tim@6267
   184
    SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata;
kees@6009
   185
tim@6267
   186
    if (SDL_UIKit_supports_multiple_displays) {
tim@6267
   187
        // availableModes showed up in 3.2 (the iPad and later). We should only
tim@6267
   188
        //  land here for at least that version of the OS.
tim@6267
   189
        for (UIScreenMode *uimode in [data->uiscreen availableModes]) {
tim@6267
   190
            BOOL mainscreen = (data->uiscreen == [UIScreen mainScreen]);
tim@6267
   191
            CGSize size = [uimode size];
tim@6267
   192
            int w = (int)size.width;
tim@6267
   193
            int h = (int)size.height;
tim@6267
   194
            
tim@6267
   195
            // Add the native screen resolution.
tim@6267
   196
            UIKit_AddDisplayMode(display, w, h, data->scale, uimode, mainscreen);
tim@6267
   197
            
tim@6267
   198
            if (data->scale != 1.0f) {
tim@6267
   199
                // Add the native screen resolution divided by its scale.
tim@6267
   200
                // This is so devices capable of e.g. 640x960 also advertise
tim@6267
   201
                // 320x480.
tim@6267
   202
                UIKit_AddDisplayMode(display,
tim@6267
   203
                    (int)(w / data->scale), (int)(h / data->scale),
tim@6267
   204
                    1.0f, uimode, mainscreen);
tim@6267
   205
            }
icculus@5518
   206
        }
tim@6267
   207
    } else {
tim@6267
   208
        const CGRect rect = [data->uiscreen bounds];
tim@6267
   209
        UIKit_AddDisplayMode(display,
tim@6267
   210
            (int)rect.size.width, (int)rect.size.height,
tim@6267
   211
            1.0f, nil, YES);
tim@6267
   212
    } 
icculus@4443
   213
}
icculus@4443
   214
icculus@4443
   215
tim@6267
   216
static int
tim@6267
   217
UIKit_AddDisplay(UIScreen *uiscreen, CGSize size)
icculus@4443
   218
{
tim@6267
   219
    // When dealing with UIKit all coordinates are specified in terms of
tim@6267
   220
    // what Apple refers to as points. On earlier devices without the
tim@6267
   221
    // so called "Retina" display, there is a one to one mapping between
tim@6267
   222
    // points and pixels. In other cases [UIScreen scale] indicates the
tim@6267
   223
    // relationship between points and pixels. Since SDL has no notion
tim@6267
   224
    // of points, we must compensate in all cases where dealing with such
tim@6267
   225
    // units.
tim@6267
   226
    CGFloat scale;
tim@6267
   227
    if ([UIScreen instancesRespondToSelector:@selector(scale)]) {
tim@6267
   228
        scale = [uiscreen scale]; // iOS >= 4.0
tim@6267
   229
    } else {
tim@6267
   230
        scale = 1.0f; // iOS < 4.0
tim@6267
   231
    }
tim@6267
   232
	
icculus@4443
   233
    SDL_VideoDisplay display;
icculus@4443
   234
    SDL_DisplayMode mode;
icculus@4443
   235
    SDL_zero(mode);
icculus@4443
   236
    mode.format = SDL_PIXELFORMAT_ABGR8888;
tim@6267
   237
    mode.w = (int)(size.width * scale);
tim@6267
   238
    mode.h = (int)(size.height * scale);
icculus@4443
   239
    mode.refresh_rate = 0;
kees@6001
   240
kees@6018
   241
    // UIScreenMode showed up in 3.2 (the iPad and later). We're
kees@6018
   242
    //  misusing this supports_multiple_displays flag here for that.
slouken@6062
   243
    if (SDL_UIKit_supports_multiple_displays) {
tim@6267
   244
        SDL_DisplayModeData *data;
tim@6267
   245
        
tim@6267
   246
        /* Allocate the mode data */
tim@6267
   247
        data = (SDL_DisplayModeData *) SDL_malloc(sizeof(*data));
tim@6267
   248
        if (!data) {
tim@6267
   249
            SDL_OutOfMemory();
tim@6267
   250
            return -1;
tim@6267
   251
        }
tim@6267
   252
        
tim@6267
   253
        data->uiscreenmode = [uiscreen currentMode];
tim@6267
   254
        [data->uiscreenmode retain];  // once for the desktop_mode
tim@6267
   255
        [data->uiscreenmode retain];  // once for the current_mode
tim@6267
   256
        
tim@6267
   257
        data->scale = scale;
tim@6267
   258
tim@6267
   259
        mode.driverdata = data;
kees@6018
   260
    }
icculus@4443
   261
icculus@4443
   262
    SDL_zero(display);
icculus@4443
   263
    display.desktop_mode = mode;
icculus@4443
   264
    display.current_mode = mode;
tim@6267
   265
	
tim@6267
   266
    SDL_DisplayData *data;
icculus@4446
   267
tim@6267
   268
    /* Allocate the display data */
tim@6267
   269
    data = (SDL_DisplayData *) SDL_malloc(sizeof(*data));
tim@6267
   270
    if (!data) {
tim@6267
   271
        SDL_free(mode.driverdata);
tim@6267
   272
        SDL_OutOfMemory();
tim@6267
   273
        return -1;
tim@6267
   274
    }
tim@6267
   275
	
icculus@4446
   276
    [uiscreen retain];
tim@6267
   277
    data->uiscreen = uiscreen;
tim@6267
   278
    data->scale = scale;
tim@6267
   279
	
tim@6267
   280
    display.driverdata = data;
icculus@4443
   281
    SDL_AddVideoDisplay(&display);
tim@6267
   282
    
tim@6267
   283
    return 0;
icculus@4443
   284
}
icculus@4443
   285
icculus@4443
   286
slouken@2765
   287
int
slouken@2765
   288
UIKit_VideoInit(_THIS)
slouken@2765
   289
{
icculus@4443
   290
    _this->gl_config.driver_loaded = 1;
slouken@2765
   291
icculus@6089
   292
    // this tells us whether we are running on ios >= 3.2
icculus@6089
   293
    SDL_UIKit_supports_multiple_displays = [UIScreen instancesRespondToSelector:@selector(currentMode)];
slouken@2765
   294
kees@6013
   295
    // Add the main screen.
kees@6013
   296
    UIScreen *uiscreen = [UIScreen mainScreen];
kees@6013
   297
    const CGSize size = [uiscreen bounds].size;
tim@6267
   298
tim@6267
   299
    if (UIKit_AddDisplay(uiscreen, size) < 0) {
tim@6267
   300
        return -1;
tim@6267
   301
    }
kees@6013
   302
icculus@4443
   303
    // If this is iPhoneOS < 3.2, all devices are one screen, 320x480 pixels.
icculus@4443
   304
    //  The iPad added both a larger main screen and the ability to use
kees@6013
   305
    //  external displays. So, add the other displays (screens in UI speak).
kees@6013
   306
    if (SDL_UIKit_supports_multiple_displays) {
kees@6007
   307
        for (UIScreen *uiscreen in [UIScreen screens]) {
kees@6013
   308
            // Only add the other screens
kees@6013
   309
            if (uiscreen != [UIScreen mainScreen]) {
kees@6013
   310
                const CGSize size = [uiscreen bounds].size;
tim@6267
   311
                if (UIKit_AddDisplay(uiscreen, size) < 0) {
tim@6267
   312
                    return -1;
tim@6267
   313
                }
kees@6013
   314
            }
icculus@4443
   315
        }
icculus@4443
   316
    }
slouken@2765
   317
slouken@2765
   318
    /* We're done! */
slouken@2765
   319
    return 0;
slouken@2765
   320
}
slouken@2765
   321
slouken@2765
   322
static int
slouken@3643
   323
UIKit_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
slouken@2765
   324
{
tim@6267
   325
    SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata;
icculus@4446
   326
    if (!SDL_UIKit_supports_multiple_displays) {
icculus@4443
   327
        // Not on at least iPhoneOS 3.2 (versions prior to iPad).
icculus@4443
   328
        SDL_assert(mode->driverdata == NULL);
icculus@4443
   329
    } else {
tim@6267
   330
        SDL_DisplayModeData *modedata = (SDL_DisplayModeData *)mode->driverdata;
tim@6267
   331
        [data->uiscreen setCurrentMode:modedata->uiscreenmode];
slouken@6077
   332
tim@6267
   333
        CGSize size = [modedata->uiscreenmode size];
slouken@6077
   334
        if (size.width >= size.height) {
slouken@6077
   335
            [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:NO];
slouken@6077
   336
        } else {
slouken@6077
   337
            [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait animated:NO];
slouken@6077
   338
        }
icculus@4443
   339
    }
icculus@4443
   340
slouken@2765
   341
    return 0;
slouken@2765
   342
}
slouken@2765
   343
kees@6019
   344
static void
kees@6019
   345
UIKit_ReleaseUIScreenMode(SDL_DisplayMode * mode)
kees@6019
   346
{
kees@6019
   347
    if (!SDL_UIKit_supports_multiple_displays) {
kees@6019
   348
        // Not on at least iPhoneOS 3.2 (versions prior to iPad).
kees@6019
   349
        SDL_assert(mode->driverdata == NULL);
kees@6019
   350
    } else {
tim@6267
   351
        SDL_DisplayModeData *data = (SDL_DisplayModeData *)mode->driverdata;
tim@6267
   352
        [data->uiscreenmode release];
tim@6267
   353
        SDL_free(data);
kees@6019
   354
        mode->driverdata = NULL;
kees@6019
   355
    }
kees@6019
   356
}
kees@6019
   357
slouken@2765
   358
void
slouken@2765
   359
UIKit_VideoQuit(_THIS)
slouken@2765
   360
{
icculus@4443
   361
    // Release Objective-C objects, so higher level doesn't free() them.
icculus@4443
   362
    int i, j;
icculus@4443
   363
    for (i = 0; i < _this->num_displays; i++) {
icculus@4443
   364
        SDL_VideoDisplay *display = &_this->displays[i];
tim@6267
   365
        SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata;
tim@6267
   366
        [data->uiscreen release];
tim@6267
   367
        SDL_free(data);
icculus@4443
   368
        display->driverdata = NULL;
kees@6019
   369
        UIKit_ReleaseUIScreenMode(&display->desktop_mode);
kees@6019
   370
        UIKit_ReleaseUIScreenMode(&display->current_mode);
icculus@4443
   371
        for (j = 0; j < display->num_display_modes; j++) {
icculus@4443
   372
            SDL_DisplayMode *mode = &display->display_modes[j];
kees@6019
   373
            UIKit_ReleaseUIScreenMode(mode);
icculus@4443
   374
        }
icculus@4443
   375
    }
slouken@2765
   376
}
slouken@2765
   377
slouken@6044
   378
#endif /* SDL_VIDEO_DRIVER_UIKIT */
slouken@6044
   379
slouken@2765
   380
/* vi: set ts=4 sw=4 expandtab: */