# HG changeset patch # User Tim Angus # Date 1327501796 0 # Node ID ed2cf4669e553dd7005da3abbd987211dc488907 # Parent a4be1e781020c14bb5edd5db94eb6d3d813ec8a9 * On iOS, fix support for the case where [UIScreen scale] is not 1.0 (retina) * Allow selection of non-retina modes on retina devices diff -r a4be1e781020 -r ed2cf4669e55 src/video/uikit/SDL_uikitopengles.m --- a/src/video/uikit/SDL_uikitopengles.m Sun Jan 22 23:51:46 2012 -0500 +++ b/src/video/uikit/SDL_uikitopengles.m Wed Jan 25 14:29:56 2012 +0000 @@ -103,10 +103,14 @@ { SDL_uikitopenglview *view; SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); + SDL_DisplayData *displaydata = display->driverdata; + SDL_DisplayModeData *displaymodedata = display->current_mode.driverdata; UIWindow *uiwindow = data->uiwindow; /* construct our view, passing in SDL's OpenGL configuration data */ view = [[SDL_uikitopenglview alloc] initWithFrame: [uiwindow bounds] + scale: displaymodedata->scale retainBacking: _this->gl_config.retained_backing rBits: _this->gl_config.red_size gBits: _this->gl_config.green_size @@ -135,9 +139,10 @@ } /* Make this window the current mouse focus for touch input */ - /* !!! FIXME: only do this if this is the primary screen. */ - SDL_SetMouseFocus(window); - SDL_SetKeyboardFocus(window); + if (displaydata->uiscreen == [UIScreen mainScreen]) { + SDL_SetMouseFocus(window); + SDL_SetKeyboardFocus(window); + } return view; } diff -r a4be1e781020 -r ed2cf4669e55 src/video/uikit/SDL_uikitopenglview.h --- a/src/video/uikit/SDL_uikitopenglview.h Sun Jan 22 23:51:46 2012 -0500 +++ b/src/video/uikit/SDL_uikitopenglview.h Wed Jan 25 14:29:56 2012 +0000 @@ -54,6 +54,7 @@ - (void)setCurrentContext; - (id)initWithFrame:(CGRect)frame + scale:(CGFloat)scale retainBacking:(BOOL)retained rBits:(int)rBits gBits:(int)gBits diff -r a4be1e781020 -r ed2cf4669e55 src/video/uikit/SDL_uikitopenglview.m --- a/src/video/uikit/SDL_uikitopenglview.m Sun Jan 22 23:51:46 2012 -0500 +++ b/src/video/uikit/SDL_uikitopenglview.m Wed Jan 25 14:29:56 2012 +0000 @@ -37,6 +37,7 @@ } - (id)initWithFrame:(CGRect)frame + scale:(CGFloat)scale retainBacking:(BOOL)retained rBits:(int)rBits gBits:(int)gBits @@ -79,10 +80,9 @@ return nil; } - // !!! FIXME: use the screen this is on! - /* Use the main screen scale (for retina display support) */ + /* Set the appropriate scale (for retina display support) */ if ([self respondsToSelector:@selector(contentScaleFactor)]) - self.contentScaleFactor = [UIScreen mainScreen].scale; + self.contentScaleFactor = scale; /* create the buffers */ glGenFramebuffersOES(1, &viewFramebuffer); diff -r a4be1e781020 -r ed2cf4669e55 src/video/uikit/SDL_uikitvideo.h --- a/src/video/uikit/SDL_uikitvideo.h Sun Jan 22 23:51:46 2012 -0500 +++ b/src/video/uikit/SDL_uikitvideo.h Wed Jan 25 14:29:56 2012 +0000 @@ -25,6 +25,22 @@ extern BOOL SDL_UIKit_supports_multiple_displays; +typedef struct SDL_DisplayData SDL_DisplayData; + +struct SDL_DisplayData +{ + UIScreen *uiscreen; + CGFloat scale; +}; + +typedef struct SDL_DisplayModeData SDL_DisplayModeData; + +struct SDL_DisplayModeData +{ + UIScreenMode *uiscreenmode; + CGFloat scale; +}; + #endif /* _SDL_uikitvideo_h */ /* vi: set ts=4 sw=4 expandtab: */ diff -r a4be1e781020 -r ed2cf4669e55 src/video/uikit/SDL_uikitvideo.m --- a/src/video/uikit/SDL_uikitvideo.m Sun Jan 22 23:51:46 2012 -0500 +++ b/src/video/uikit/SDL_uikitvideo.m Wed Jan 25 14:29:56 2012 +0000 @@ -120,79 +120,167 @@ */ +static int +UIKit_AddSingleDisplayMode(SDL_VideoDisplay * display, int w, int h, + UIScreenMode * uiscreenmode, CGFloat scale) +{ + SDL_DisplayMode mode; + SDL_zero(mode); + + SDL_DisplayModeData *data = NULL; + + if (uiscreenmode != nil) { + /* Allocate the display mode data */ + data = (SDL_DisplayModeData *) SDL_malloc(sizeof(*data)); + if (!data) { + SDL_OutOfMemory(); + return -1; + } + + data->uiscreenmode = uiscreenmode; + data->scale = scale; + } + + mode.format = SDL_PIXELFORMAT_ABGR8888; + mode.refresh_rate = 0; + mode.driverdata = data; + + mode.w = w; + mode.h = h; + if (SDL_AddDisplayMode(display, &mode)) { + if (uiscreenmode != nil) { + [uiscreenmode retain]; + } + + return 0; + } + + SDL_free(data); + + return -1; +} + +static int +UIKit_AddDisplayMode(SDL_VideoDisplay * display, int w, int h, CGFloat scale, + UIScreenMode * uiscreenmode, BOOL rotated) +{ + if (UIKit_AddSingleDisplayMode(display, w, h, uiscreenmode, scale) < 0) { + return -1; + } + + if (rotated) { + // Add the rotated version + if (UIKit_AddSingleDisplayMode(display, h, w, uiscreenmode, scale) < 0) { + return -1; + } + } + + return 0; +} + static void UIKit_GetDisplayModes(_THIS, SDL_VideoDisplay * display) { - UIScreen *uiscreen = (UIScreen *) display->driverdata; - SDL_DisplayMode mode; - SDL_zero(mode); + SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata; - // availableModes showed up in 3.2 (the iPad and later). We should only - // land here for at least that version of the OS. - if (!SDL_UIKit_supports_multiple_displays) { - const CGRect rect = [uiscreen bounds]; - mode.format = SDL_PIXELFORMAT_ABGR8888; - mode.refresh_rate = 0; - mode.driverdata = NULL; - - mode.w = (int) rect.size.width; - mode.h = (int) rect.size.height; - SDL_AddDisplayMode(display, &mode); - - mode.w = (int) rect.size.height; // swap the orientation, add again. - mode.h = (int) rect.size.width; - SDL_AddDisplayMode(display, &mode); - return; - } - - for (UIScreenMode *uimode in [uiscreen availableModes]) { - CGSize size = [uimode size]; - mode.format = SDL_PIXELFORMAT_ABGR8888; - mode.refresh_rate = 0; - mode.driverdata = uimode; - mode.w = (int) size.width; - mode.h = (int) size.height; - if (SDL_AddDisplayMode(display, &mode)) - [uimode retain]; // retain is needed because of mode.driverdata - - if (uiscreen == [UIScreen mainScreen]) { - // Add the mode with swapped width/height - mode.w = (int) size.height; - mode.h = (int) size.width; - if (SDL_AddDisplayMode(display, &mode)) - [uimode retain]; + if (SDL_UIKit_supports_multiple_displays) { + // availableModes showed up in 3.2 (the iPad and later). We should only + // land here for at least that version of the OS. + for (UIScreenMode *uimode in [data->uiscreen availableModes]) { + BOOL mainscreen = (data->uiscreen == [UIScreen mainScreen]); + CGSize size = [uimode size]; + int w = (int)size.width; + int h = (int)size.height; + + // Add the native screen resolution. + UIKit_AddDisplayMode(display, w, h, data->scale, uimode, mainscreen); + + if (data->scale != 1.0f) { + // Add the native screen resolution divided by its scale. + // This is so devices capable of e.g. 640x960 also advertise + // 320x480. + UIKit_AddDisplayMode(display, + (int)(w / data->scale), (int)(h / data->scale), + 1.0f, uimode, mainscreen); + } } - } + } else { + const CGRect rect = [data->uiscreen bounds]; + UIKit_AddDisplayMode(display, + (int)rect.size.width, (int)rect.size.height, + 1.0f, nil, YES); + } } -static void -UIKit_AddDisplay(UIScreen *uiscreen, int w, int h) +static int +UIKit_AddDisplay(UIScreen *uiscreen, CGSize size) { + // When dealing with UIKit all coordinates are specified in terms of + // what Apple refers to as points. On earlier devices without the + // so called "Retina" display, there is a one to one mapping between + // points and pixels. In other cases [UIScreen scale] indicates the + // relationship between points and pixels. Since SDL has no notion + // of points, we must compensate in all cases where dealing with such + // units. + CGFloat scale; + if ([UIScreen instancesRespondToSelector:@selector(scale)]) { + scale = [uiscreen scale]; // iOS >= 4.0 + } else { + scale = 1.0f; // iOS < 4.0 + } + SDL_VideoDisplay display; SDL_DisplayMode mode; SDL_zero(mode); mode.format = SDL_PIXELFORMAT_ABGR8888; - mode.w = w; - mode.h = h; + mode.w = (int)(size.width * scale); + mode.h = (int)(size.height * scale); mode.refresh_rate = 0; // UIScreenMode showed up in 3.2 (the iPad and later). We're // misusing this supports_multiple_displays flag here for that. if (SDL_UIKit_supports_multiple_displays) { - UIScreenMode *uimode = [uiscreen currentMode]; - [uimode retain]; // once for the desktop_mode - [uimode retain]; // once for the current_mode - mode.driverdata = uimode; + SDL_DisplayModeData *data; + + /* Allocate the mode data */ + data = (SDL_DisplayModeData *) SDL_malloc(sizeof(*data)); + if (!data) { + SDL_OutOfMemory(); + return -1; + } + + data->uiscreenmode = [uiscreen currentMode]; + [data->uiscreenmode retain]; // once for the desktop_mode + [data->uiscreenmode retain]; // once for the current_mode + + data->scale = scale; + + mode.driverdata = data; } SDL_zero(display); display.desktop_mode = mode; display.current_mode = mode; + + SDL_DisplayData *data; + /* Allocate the display data */ + data = (SDL_DisplayData *) SDL_malloc(sizeof(*data)); + if (!data) { + SDL_free(mode.driverdata); + SDL_OutOfMemory(); + return -1; + } + [uiscreen retain]; - display.driverdata = uiscreen; + data->uiscreen = uiscreen; + data->scale = scale; + + display.driverdata = data; SDL_AddVideoDisplay(&display); + + return 0; } @@ -207,7 +295,10 @@ // Add the main screen. UIScreen *uiscreen = [UIScreen mainScreen]; const CGSize size = [uiscreen bounds].size; - UIKit_AddDisplay(uiscreen, (int)size.width, (int)size.height); + + if (UIKit_AddDisplay(uiscreen, size) < 0) { + return -1; + } // If this is iPhoneOS < 3.2, all devices are one screen, 320x480 pixels. // The iPad added both a larger main screen and the ability to use @@ -217,7 +308,9 @@ // Only add the other screens if (uiscreen != [UIScreen mainScreen]) { const CGSize size = [uiscreen bounds].size; - UIKit_AddDisplay(uiscreen, (int)size.width, (int)size.height); + if (UIKit_AddDisplay(uiscreen, size) < 0) { + return -1; + } } } } @@ -229,15 +322,15 @@ static int UIKit_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode) { - UIScreen *uiscreen = (UIScreen *) display->driverdata; + SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata; if (!SDL_UIKit_supports_multiple_displays) { // Not on at least iPhoneOS 3.2 (versions prior to iPad). SDL_assert(mode->driverdata == NULL); } else { - UIScreenMode *uimode = (UIScreenMode *) mode->driverdata; - [uiscreen setCurrentMode:uimode]; + SDL_DisplayModeData *modedata = (SDL_DisplayModeData *)mode->driverdata; + [data->uiscreen setCurrentMode:modedata->uiscreenmode]; - CGSize size = [uimode size]; + CGSize size = [modedata->uiscreenmode size]; if (size.width >= size.height) { [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:NO]; } else { @@ -255,8 +348,9 @@ // Not on at least iPhoneOS 3.2 (versions prior to iPad). SDL_assert(mode->driverdata == NULL); } else { - UIScreenMode *uimode = (UIScreenMode *) mode->driverdata; - [uimode release]; + SDL_DisplayModeData *data = (SDL_DisplayModeData *)mode->driverdata; + [data->uiscreenmode release]; + SDL_free(data); mode->driverdata = NULL; } } @@ -268,8 +362,9 @@ int i, j; for (i = 0; i < _this->num_displays; i++) { SDL_VideoDisplay *display = &_this->displays[i]; - UIScreen *uiscreen = (UIScreen *) display->driverdata; - [uiscreen release]; + SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata; + [data->uiscreen release]; + SDL_free(data); display->driverdata = NULL; UIKit_ReleaseUIScreenMode(&display->desktop_mode); UIKit_ReleaseUIScreenMode(&display->current_mode); diff -r a4be1e781020 -r ed2cf4669e55 src/video/uikit/SDL_uikitviewcontroller.m --- a/src/video/uikit/SDL_uikitviewcontroller.m Sun Jan 22 23:51:46 2012 -0500 +++ b/src/video/uikit/SDL_uikitviewcontroller.m Wed Jan 25 14:29:56 2012 +0000 @@ -113,11 +113,10 @@ const UIInterfaceOrientation toInterfaceOrientation = [self interfaceOrientation]; SDL_WindowData *data = self->window->driverdata; UIWindow *uiwindow = data->uiwindow; - UIScreen *uiscreen; - if (SDL_UIKit_supports_multiple_displays) - uiscreen = [uiwindow screen]; - else - uiscreen = [UIScreen mainScreen]; + SDL_VideoDisplay *display = SDL_GetDisplayForWindow(self->window); + SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata; + SDL_DisplayModeData *displaymodedata = (SDL_DisplayModeData *) display->current_mode.driverdata; + UIScreen *uiscreen = displaydata->uiscreen; const int noborder = (self->window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_BORDERLESS)); CGRect frame = noborder ? [uiscreen bounds] : [uiscreen applicationFrame]; const CGSize size = frame.size; @@ -141,6 +140,9 @@ return; } + w = (int)(w * displaymodedata->scale); + h = (int)(h * displaymodedata->scale); + [uiwindow setFrame:frame]; [data->view setFrame:frame]; [data->view updateFrame]; diff -r a4be1e781020 -r ed2cf4669e55 src/video/uikit/SDL_uikitwindow.h --- a/src/video/uikit/SDL_uikitwindow.h Sun Jan 22 23:51:46 2012 -0500 +++ b/src/video/uikit/SDL_uikitwindow.h Wed Jan 25 14:29:56 2012 +0000 @@ -22,6 +22,7 @@ #define _SDL_uikitwindow_h #include "../SDL_sysvideo.h" +#import "SDL_uikitvideo.h" #import "SDL_uikitopenglview.h" #import "SDL_uikitviewcontroller.h" diff -r a4be1e781020 -r ed2cf4669e55 src/video/uikit/SDL_uikitwindow.m --- a/src/video/uikit/SDL_uikitwindow.m Sun Jan 22 23:51:46 2012 -0500 +++ b/src/video/uikit/SDL_uikitwindow.m Wed Jan 25 14:29:56 2012 +0000 @@ -46,7 +46,8 @@ static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bool created) { SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); - UIScreen *uiscreen = (UIScreen *) display->driverdata; + SDL_DisplayModeData *displaymodedata = (SDL_DisplayModeData *) display->current_mode.driverdata; + SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata; SDL_WindowData *data; /* Allocate the window data */ @@ -64,24 +65,28 @@ window->x = 0; window->y = 0; + /* Get frame dimensions in pixels */ + int width = (int)(uiwindow.frame.size.width * displaymodedata->scale); + int height = (int)(uiwindow.frame.size.height * displaymodedata->scale); + /* We can pick either width or height here and we'll rotate the screen to match, so we pick the closest to what we wanted. */ if (window->w >= window->h) { if (uiwindow.frame.size.width > uiwindow.frame.size.height) { - window->w = (int)uiwindow.frame.size.width; - window->h = (int)uiwindow.frame.size.height; + window->w = width; + window->h = height; } else { - window->w = (int)uiwindow.frame.size.height; - window->h = (int)uiwindow.frame.size.width; + window->w = height; + window->h = width; } } else { if (uiwindow.frame.size.width > uiwindow.frame.size.height) { - window->w = (int)uiwindow.frame.size.height; - window->h = (int)uiwindow.frame.size.width; + window->w = height; + window->h = width; } else { - window->w = (int)uiwindow.frame.size.width; - window->h = (int)uiwindow.frame.size.height; + window->w = width; + window->h = height; } } } @@ -95,7 +100,7 @@ // SDL_WINDOW_BORDERLESS controls whether status bar is hidden. // This is only set if the window is on the main screen. Other screens // just force the window to have the borderless flag. - if ([UIScreen mainScreen] != uiscreen) { + if ([UIScreen mainScreen] != displaydata->uiscreen) { window->flags &= ~SDL_WINDOW_RESIZABLE; // window is NEVER resizeable window->flags &= ~SDL_WINDOW_INPUT_FOCUS; // never has input focus window->flags |= SDL_WINDOW_BORDERLESS; // never has a status bar. @@ -131,8 +136,8 @@ UIKit_CreateWindow(_THIS, SDL_Window *window) { SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); - UIScreen *uiscreen = (UIScreen *) display->driverdata; - const BOOL external = ([UIScreen mainScreen] != uiscreen); + SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata; + const BOOL external = ([UIScreen mainScreen] != data->uiscreen); // SDL currently puts this window at the start of display's linked list. We rely on this. SDL_assert(_this->windows == window); @@ -154,7 +159,7 @@ // user, so it's in standby), try to force the display to a resolution // that most closely matches the desired window size. if (SDL_UIKit_supports_multiple_displays) { - const CGSize origsize = [[uiscreen currentMode] size]; + const CGSize origsize = [[data->uiscreen currentMode] size]; if ((origsize.width == 0.0f) && (origsize.height == 0.0f)) { if (display->num_display_modes == 0) { _this->GetDisplayModes(_this, display); @@ -169,8 +174,8 @@ } if (bestmode) { - UIScreenMode *uimode = (UIScreenMode *) bestmode->driverdata; - [uiscreen setCurrentMode:uimode]; + SDL_DisplayModeData *modedata = (SDL_DisplayModeData *)bestmode->driverdata; + [data->uiscreen setCurrentMode:modedata->uiscreenmode]; // desktop_mode doesn't change here (the higher level will // use it to set all the screens back to their defaults @@ -186,16 +191,16 @@ // !!! FIXME: can we have a smaller view? UIWindow *uiwindow = [UIWindow alloc]; if (window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_BORDERLESS)) - uiwindow = [uiwindow initWithFrame:[uiscreen bounds]]; + uiwindow = [uiwindow initWithFrame:[data->uiscreen bounds]]; else - uiwindow = [uiwindow initWithFrame:[uiscreen applicationFrame]]; + uiwindow = [uiwindow initWithFrame:[data->uiscreen applicationFrame]]; // put the window on an external display if appropriate. This implicitly // does [uiwindow setframe:[uiscreen bounds]], so don't do it on the // main display, where we land by default, as that would eat the // status bar real estate. if (external) { - [uiwindow setScreen:uiscreen]; + [uiwindow setScreen:data->uiscreen]; } if (SetupWindowData(_this, window, uiwindow, SDL_TRUE) < 0) { @@ -210,35 +215,40 @@ void UIKit_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen) { - UIScreen *uiscreen = (UIScreen *) display->driverdata; + SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata; + SDL_DisplayModeData *displaymodedata = (SDL_DisplayModeData *) display->current_mode.driverdata; UIWindow *uiwindow = ((SDL_WindowData *) window->driverdata)->uiwindow; if (fullscreen) { [UIApplication sharedApplication].statusBarHidden = YES; - uiwindow.frame = [uiscreen bounds]; + uiwindow.frame = [displaydata->uiscreen bounds]; } else { [UIApplication sharedApplication].statusBarHidden = NO; - uiwindow.frame = [uiscreen applicationFrame]; + uiwindow.frame = [displaydata->uiscreen applicationFrame]; } + /* Get frame dimensions in pixels */ + int width = (int)(uiwindow.frame.size.width * displaymodedata->scale); + int height = (int)(uiwindow.frame.size.height * displaymodedata->scale); + /* We can pick either width or height here and we'll rotate the screen to match, so we pick the closest to what we wanted. */ if (window->w >= window->h) { if (uiwindow.frame.size.width > uiwindow.frame.size.height) { - window->w = (int)uiwindow.frame.size.width; - window->h = (int)uiwindow.frame.size.height; + window->w = width; + window->h = height; } else { - window->w = (int)uiwindow.frame.size.height; - window->h = (int)uiwindow.frame.size.width; + window->w = height; + window->h = width; } } else { if (uiwindow.frame.size.width > uiwindow.frame.size.height) { - window->w = (int)uiwindow.frame.size.height; - window->h = (int)uiwindow.frame.size.width; + window->w = height; + window->h = width; } else { - window->w = (int)uiwindow.frame.size.width; - window->h = (int)uiwindow.frame.size.height; + window->w = width; + window->h = height; } } }