src/video/uikit/SDL_uikitvideo.m
author Ryan C. Gordon
Fri, 14 Oct 2011 20:47:53 -0400
changeset 5997 69875bbf83d8
parent 5558 dd0f52bf2bfa
child 6014 7a8b72b88d86
permissions -rw-r--r--
Fix up some retain/release issues with UIScreenMode objects.

Fixes Bugzilla #1191.

This patch is based on work done by Vittorio Giovara.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2011 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 
    22 #import <UIKit/UIKit.h>
    23 
    24 #include "SDL_config.h"
    25 
    26 #include "SDL_video.h"
    27 #include "SDL_mouse.h"
    28 #include "../SDL_sysvideo.h"
    29 #include "../SDL_pixels_c.h"
    30 #include "../../events/SDL_events_c.h"
    31 
    32 #include "SDL_uikitvideo.h"
    33 #include "SDL_uikitevents.h"
    34 #include "SDL_uikitwindow.h"
    35 #include "SDL_uikitopengles.h"
    36 
    37 #include "SDL_assert.h"
    38 
    39 #define UIKITVID_DRIVER_NAME "uikit"
    40 
    41 /* Initialization/Query functions */
    42 static int UIKit_VideoInit(_THIS);
    43 static void UIKit_GetDisplayModes(_THIS, SDL_VideoDisplay * sdl_display);
    44 static int UIKit_SetDisplayMode(_THIS, SDL_VideoDisplay * display,
    45                                 SDL_DisplayMode * mode);
    46 static void UIKit_VideoQuit(_THIS);
    47 
    48 BOOL SDL_UIKit_supports_multiple_displays = NO;
    49 
    50 /* DUMMY driver bootstrap functions */
    51 
    52 static int
    53 UIKit_Available(void)
    54 {
    55     return (1);
    56 }
    57 
    58 static void UIKit_DeleteDevice(SDL_VideoDevice * device)
    59 {
    60     SDL_free(device);
    61 }
    62 
    63 static SDL_VideoDevice *
    64 UIKit_CreateDevice(int devindex)
    65 {
    66     SDL_VideoDevice *device;
    67 
    68     /* Initialize all variables that we clean on shutdown */
    69     device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
    70     if (!device) {
    71         SDL_OutOfMemory();
    72         if (device) {
    73             SDL_free(device);
    74         }
    75         return (0);
    76     }
    77 
    78     /* Set the function pointers */
    79     device->VideoInit = UIKit_VideoInit;
    80     device->VideoQuit = UIKit_VideoQuit;
    81     device->GetDisplayModes = UIKit_GetDisplayModes;
    82     device->SetDisplayMode = UIKit_SetDisplayMode;
    83     device->PumpEvents = UIKit_PumpEvents;
    84     device->CreateWindow = UIKit_CreateWindow;
    85     device->DestroyWindow = UIKit_DestroyWindow;
    86     device->GetWindowWMInfo = UIKit_GetWindowWMInfo;
    87     
    88     
    89     /* OpenGL (ES) functions */
    90     device->GL_MakeCurrent        = UIKit_GL_MakeCurrent;
    91     device->GL_SwapWindow        = UIKit_GL_SwapWindow;
    92     device->GL_CreateContext    = UIKit_GL_CreateContext;
    93     device->GL_DeleteContext    = UIKit_GL_DeleteContext;
    94     device->GL_GetProcAddress   = UIKit_GL_GetProcAddress;
    95     device->GL_LoadLibrary        = UIKit_GL_LoadLibrary;
    96     device->free = UIKit_DeleteDevice;
    97 
    98     device->gl_config.accelerated = 1;
    99 
   100     return device;
   101 }
   102 
   103 VideoBootStrap UIKIT_bootstrap = {
   104     UIKITVID_DRIVER_NAME, "SDL UIKit video driver",
   105     UIKit_Available, UIKit_CreateDevice
   106 };
   107 
   108 
   109 /*
   110 !!! FIXME:
   111 
   112 The main screen should list a AxB mode for portrait orientation, and then
   113  also list BxA for landscape mode. When setting a given resolution, we should
   114  rotate the view's transform appropriately (extra credit if you check the
   115  accelerometer and rotate the display so it's never upside down).
   116 
   117   http://iphonedevelopment.blogspot.com/2008/10/starting-in-landscape-mode-without.html
   118 
   119 */
   120 
   121 static CGSize
   122 UIKit_ForcePortrait(const CGSize size)
   123 {
   124     CGSize retval;
   125     if (size.width < size.height) { // portrait
   126         retval = size;
   127     } else {  // landscape
   128         retval.width = size.height;
   129         retval.height = size.width;
   130     }
   131     return retval;
   132 }
   133 
   134 static CGSize
   135 UIKit_ForceLandscape(const CGSize size)
   136 {
   137     CGSize retval;
   138     if (size.width > size.height) { // landscape
   139         retval = size;
   140     } else {  // portrait
   141         retval.width = size.height;
   142         retval.height = size.width;
   143     }
   144     return retval;
   145 }
   146 
   147 static void
   148 UIKit_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
   149 {
   150     UIScreen *uiscreen = (UIScreen *) display->driverdata;
   151     SDL_DisplayMode mode;
   152     SDL_zero(mode);
   153 
   154     // availableModes showed up in 3.2 (the iPad and later). We should only
   155     //  land here for at least that version of the OS.
   156     if (!SDL_UIKit_supports_multiple_displays) {
   157         const CGRect rect = [uiscreen bounds];
   158         mode.format = SDL_PIXELFORMAT_ABGR8888;
   159         mode.w = (int) rect.size.width;
   160         mode.h = (int) rect.size.height;
   161         mode.refresh_rate = 0;
   162         mode.driverdata = NULL;
   163         SDL_AddDisplayMode(display, &mode);
   164         mode.w = (int) rect.size.height;  // swap the orientation, add again.
   165         mode.h = (int) rect.size.width;
   166         SDL_AddDisplayMode(display, &mode);
   167         return;
   168     }
   169 
   170     const int ismain = (uiscreen == [UIScreen mainScreen]);
   171     const NSArray *modes = [uiscreen availableModes];
   172     const NSUInteger mode_count = [modes count];
   173     NSUInteger i;
   174     for (i = 0; i < mode_count; i++) {
   175         UIScreenMode *uimode = (UIScreenMode *) [modes objectAtIndex:i];
   176         CGSize size = [uimode size];
   177         mode.format = SDL_PIXELFORMAT_ABGR8888;
   178         mode.refresh_rate = 0;
   179         mode.driverdata = uimode;
   180         mode.w = (int) size.width;
   181         mode.h = (int) size.height;
   182         if (SDL_AddDisplayMode(display, &mode))
   183             [uimode retain];
   184 
   185         if (ismain) {
   186             // Add the mode twice, flipped to portrait and landscape.
   187             //  SDL_AddDisplayMode() will ignore duplicates.
   188             size = UIKit_ForcePortrait([uimode size]);
   189             mode.w = (int) size.width;
   190             mode.h = (int) size.height;
   191             if (SDL_AddDisplayMode(display, &mode))
   192                 [uimode retain];
   193 
   194             size = UIKit_ForceLandscape(size);
   195             mode.w = (int) size.width;
   196             mode.h = (int) size.height;
   197             if (SDL_AddDisplayMode(display, &mode))
   198                 [uimode retain];
   199         }
   200     }
   201 }
   202 
   203 
   204 static void
   205 UIKit_AddDisplay(UIScreen *uiscreen, UIScreenMode *uimode, int w, int h)
   206 {
   207     SDL_VideoDisplay display;
   208     SDL_DisplayMode mode;
   209     SDL_zero(mode);
   210     mode.format = SDL_PIXELFORMAT_ABGR8888;
   211     mode.w = w;
   212     mode.h = h;
   213     mode.refresh_rate = 0;
   214     
   215     [uimode retain];  // once for the desktop_mode
   216     [uimode retain];  // once for the current_mode
   217     mode.driverdata = uimode;
   218 
   219     SDL_zero(display);
   220     display.desktop_mode = mode;
   221     display.current_mode = mode;
   222 
   223     [uiscreen retain];
   224     display.driverdata = uiscreen;
   225     SDL_AddVideoDisplay(&display);
   226 }
   227 
   228 
   229 int
   230 UIKit_VideoInit(_THIS)
   231 {
   232     _this->gl_config.driver_loaded = 1;
   233 
   234     NSString *reqSysVer = @"3.2";
   235     NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
   236     if ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending)
   237         SDL_UIKit_supports_multiple_displays = YES;
   238 
   239     // If this is iPhoneOS < 3.2, all devices are one screen, 320x480 pixels.
   240     //  The iPad added both a larger main screen and the ability to use
   241     //  external displays.
   242     if (!SDL_UIKit_supports_multiple_displays) {
   243         // Just give 'em the whole main screen.
   244         UIScreen *uiscreen = [UIScreen mainScreen];
   245         UIScreenMode *uiscreenmode = [uiscreen currentMode];
   246         const CGRect rect = [uiscreen bounds];
   247         UIKit_AddDisplay(uiscreen, uiscreenmode, (int)rect.size.width, (int)rect.size.height);
   248     } else {
   249         const NSArray *screens = [UIScreen screens];
   250         const NSUInteger screen_count = [screens count];
   251         NSUInteger i;
   252         for (i = 0; i < screen_count; i++) {
   253             // the main screen is the first element in the array.
   254             UIScreen *uiscreen = (UIScreen *) [screens objectAtIndex:i];
   255             UIScreenMode *uiscreenmode = [uiscreen currentMode];
   256             const CGSize size = [[uiscreen currentMode] size];
   257             UIKit_AddDisplay(uiscreen, uiscreenmode, (int)size.width, (int)size.height);
   258         }
   259     }
   260 
   261     /* We're done! */
   262     return 0;
   263 }
   264 
   265 static int
   266 UIKit_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
   267 {
   268     UIScreen *uiscreen = (UIScreen *) display->driverdata;
   269     if (!SDL_UIKit_supports_multiple_displays) {
   270         // Not on at least iPhoneOS 3.2 (versions prior to iPad).
   271         SDL_assert(mode->driverdata == NULL);
   272     } else {
   273         UIScreenMode *uimode = (UIScreenMode *) mode->driverdata;
   274         [uiscreen setCurrentMode:uimode];
   275     }
   276 
   277     return 0;
   278 }
   279 
   280 void
   281 UIKit_VideoQuit(_THIS)
   282 {
   283     // Release Objective-C objects, so higher level doesn't free() them.
   284     int i, j;
   285     for (i = 0; i < _this->num_displays; i++) {
   286         SDL_VideoDisplay *display = &_this->displays[i];
   287         UIScreen *uiscreen = (UIScreen *) display->driverdata;
   288         [uiscreen release];
   289         display->driverdata = NULL;
   290         [((UIScreenMode *) display->desktop_mode.driverdata) release];
   291         display->desktop_mode.driverdata = NULL;
   292         [((UIScreenMode *) display->current_mode.driverdata) release];
   293         display->current_mode.driverdata = NULL;
   294         for (j = 0; j < display->num_display_modes; j++) {
   295             SDL_DisplayMode *mode = &display->display_modes[j];
   296             [((UIScreenMode *) mode->driverdata) release];
   297             mode->driverdata = NULL;
   298         }
   299     }
   300 }
   301 
   302 /* vi: set ts=4 sw=4 expandtab: */