From c644da5b20bdc83908249145d6e5e26f80747e31 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 2 May 2010 05:08:12 -0400 Subject: [PATCH] Much improved multi-display support for iPad. Fixes most issues and limitations, I think. --- src/video/uikit/SDL_uikitappdelegate.h | 5 -- src/video/uikit/SDL_uikitappdelegate.m | 50 +++++++++---- src/video/uikit/SDL_uikitopengles.m | 12 +-- src/video/uikit/SDL_uikitvideo.h | 4 + src/video/uikit/SDL_uikitvideo.m | 44 +++++------ src/video/uikit/SDL_uikitwindow.h | 1 - src/video/uikit/SDL_uikitwindow.m | 100 ++++++++++++++++++------- 7 files changed, 138 insertions(+), 78 deletions(-) diff --git a/src/video/uikit/SDL_uikitappdelegate.h b/src/video/uikit/SDL_uikitappdelegate.h index ae086f1b3..359d37774 100644 --- a/src/video/uikit/SDL_uikitappdelegate.h +++ b/src/video/uikit/SDL_uikitappdelegate.h @@ -25,13 +25,8 @@ /* *INDENT-OFF* */ @interface SDLUIKitDelegate:NSObject { - SDL_Window *window; - UIWindow *uiwindow; } -@property (readwrite, assign) SDL_Window *window; -@property (readwrite, retain) UIWindow *uiwindow; - +(SDLUIKitDelegate *)sharedAppDelegate; @end diff --git a/src/video/uikit/SDL_uikitappdelegate.m b/src/video/uikit/SDL_uikitappdelegate.m index 865e3d3e4..ac2992d32 100644 --- a/src/video/uikit/SDL_uikitappdelegate.m +++ b/src/video/uikit/SDL_uikitappdelegate.m @@ -20,6 +20,8 @@ slouken@libsdl.org */ +#import "../SDL_sysvideo.h" + #import "SDL_uikitappdelegate.h" #import "SDL_uikitopenglview.h" #import "SDL_events_c.h" @@ -55,9 +57,6 @@ int main(int argc, char **argv) { @implementation SDLUIKitDelegate -@synthesize window; -@synthesize uiwindow; - /* convenience method */ +(SDLUIKitDelegate *)sharedAppDelegate { /* the delegate is set in UIApplicationMain(), which is garaunteed to be called before this method */ @@ -66,8 +65,6 @@ +(SDLUIKitDelegate *)sharedAppDelegate { - (id)init { self = [super init]; - window = NULL; - uiwindow = nil; return self; } @@ -106,21 +103,42 @@ - (void)applicationWillTerminate:(UIApplication *)application { - (void) applicationWillResignActive:(UIApplication*)application { -// NSLog(@"%@", NSStringFromSelector(_cmd)); - SDL_SendWindowEvent(self.window, SDL_WINDOWEVENT_MINIMIZED, 0, 0); + //NSLog(@"%@", NSStringFromSelector(_cmd)); + + // Send every window on every screen a MINIMIZED event. + SDL_VideoDevice *_this = SDL_GetVideoDevice(); + if (!_this) { + return; + } + + int i; + for (i = 0; i < _this->num_displays; i++) { + const SDL_VideoDisplay *display = &_this->displays[i]; + SDL_Window *window; + for (window = display->windows; window != nil; window = window->next) { + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_MINIMIZED, 0, 0); + } + } } - (void) applicationDidBecomeActive:(UIApplication*)application { -// NSLog(@"%@", NSStringFromSelector(_cmd)); - SDL_SendWindowEvent(self.window, SDL_WINDOWEVENT_RESTORED, 0, 0); -} - - - --(void)dealloc { - [uiwindow release]; - [super dealloc]; + //NSLog(@"%@", NSStringFromSelector(_cmd)); + + // Send every window on every screen a RESTORED event. + SDL_VideoDevice *_this = SDL_GetVideoDevice(); + if (!_this) { + return; + } + + int i; + for (i = 0; i < _this->num_displays; i++) { + const SDL_VideoDisplay *display = &_this->displays[i]; + SDL_Window *window; + for (window = display->windows; window != nil; window = window->next) { + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESTORED, 0, 0); + } + } } @end diff --git a/src/video/uikit/SDL_uikitopengles.m b/src/video/uikit/SDL_uikitopengles.m index 79d551e3c..76c3c02a1 100644 --- a/src/video/uikit/SDL_uikitopengles.m +++ b/src/video/uikit/SDL_uikitopengles.m @@ -99,13 +99,13 @@ void UIKit_GL_SwapWindow(_THIS, SDL_Window * window) SDL_GLContext UIKit_GL_CreateContext(_THIS, SDL_Window * window) { - SDL_uikitopenglview *view; + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + UIScreen *uiscreen = (UIScreen *) window->display->driverdata; + UIWindow *uiwindow = data->uiwindow; - SDL_WindowData *data = (SDL_WindowData *)window->driverdata; - - /* construct our view, passing in SDL's OpenGL configuration data */ - view = [[SDL_uikitopenglview alloc] initWithFrame: [[UIScreen mainScreen] applicationFrame] \ + /* construct our view, passing in SDL's OpenGL configuration data */ + view = [[SDL_uikitopenglview alloc] initWithFrame: [uiwindow bounds] \ retainBacking: _this->gl_config.retained_backing \ rBits: _this->gl_config.red_size \ gBits: _this->gl_config.green_size \ @@ -116,7 +116,7 @@ SDL_GLContext UIKit_GL_CreateContext(_THIS, SDL_Window * window) data->view = view; /* add the view to our window */ - [data->uiwindow addSubview: view ]; + [uiwindow addSubview: view ]; /* Don't worry, the window retained the view */ [view release]; diff --git a/src/video/uikit/SDL_uikitvideo.h b/src/video/uikit/SDL_uikitvideo.h index 12142b25f..398749a3b 100644 --- a/src/video/uikit/SDL_uikitvideo.h +++ b/src/video/uikit/SDL_uikitvideo.h @@ -26,6 +26,10 @@ #include "../SDL_sysvideo.h" +#include + +extern BOOL SDL_UIKit_supports_multiple_displays; + #endif /* _SDL_uikitvideo_h */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/uikit/SDL_uikitvideo.m b/src/video/uikit/SDL_uikitvideo.m index 7f7747e3f..1d9a6e75a 100644 --- a/src/video/uikit/SDL_uikitvideo.m +++ b/src/video/uikit/SDL_uikitvideo.m @@ -49,7 +49,7 @@ static int UIKit_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode); static void UIKit_VideoQuit(_THIS); -static BOOL supports_multiple_displays = NO; +BOOL SDL_UIKit_supports_multiple_displays = NO; /* DUMMY driver bootstrap functions */ @@ -124,14 +124,14 @@ static void UIKit_DeleteDevice(SDL_VideoDevice * device) static void UIKit_GetDisplayModes(_THIS, SDL_VideoDisplay * display) { - const UIScreen *screen = (UIScreen *) display->driverdata; + UIScreen *uiscreen = (UIScreen *) display->driverdata; SDL_DisplayMode mode; SDL_zero(mode); // availableModes showed up in 3.2 (the iPad and later). We should only // land here for at least that version of the OS. - if (!supports_multiple_displays) { - const CGRect rect = [screen bounds]; + if (!SDL_UIKit_supports_multiple_displays) { + const CGRect rect = [uiscreen bounds]; mode.format = SDL_PIXELFORMAT_ABGR8888; mode.w = (int) rect.size.width; mode.h = (int) rect.size.height; @@ -141,7 +141,7 @@ static void UIKit_DeleteDevice(SDL_VideoDevice * device) return; } - const NSArray *modes = [screen availableModes]; + const NSArray *modes = [uiscreen availableModes]; const NSUInteger mode_count = [modes count]; NSUInteger i; for (i = 0; i < mode_count; i++) { @@ -159,11 +159,10 @@ static void UIKit_DeleteDevice(SDL_VideoDevice * device) static void -UIKit_AddDisplay(UIScreen *screen, int w, int h) +UIKit_AddDisplay(UIScreen *uiscreen, int w, int h) { SDL_VideoDisplay display; SDL_DisplayMode mode; - SDL_zero(mode); mode.format = SDL_PIXELFORMAT_ABGR8888; mode.w = w; @@ -173,8 +172,9 @@ static void UIKit_DeleteDevice(SDL_VideoDevice * device) SDL_zero(display); display.desktop_mode = mode; display.current_mode = mode; - display.driverdata = screen; - [screen retain]; + + [uiscreen retain]; + display.driverdata = uiscreen; SDL_AddVideoDisplay(&display); } @@ -187,25 +187,25 @@ static void UIKit_DeleteDevice(SDL_VideoDevice * device) NSString *reqSysVer = @"3.2"; NSString *currSysVer = [[UIDevice currentDevice] systemVersion]; if ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending) - supports_multiple_displays = YES; + SDL_UIKit_supports_multiple_displays = YES; // 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 // external displays. - if (!supports_multiple_displays) { + if (!SDL_UIKit_supports_multiple_displays) { // Just give 'em the whole main screen. - UIScreen *screen = [UIScreen mainScreen]; - const CGRect rect = [screen bounds]; - UIKit_AddDisplay(screen, (int)rect.size.width, (int)rect.size.height); + UIScreen *uiscreen = [UIScreen mainScreen]; + const CGRect rect = [uiscreen bounds]; + UIKit_AddDisplay(uiscreen, (int)rect.size.width, (int)rect.size.height); } else { const NSArray *screens = [UIScreen screens]; const NSUInteger screen_count = [screens count]; NSUInteger i; for (i = 0; i < screen_count; i++) { // the main screen is the first element in the array. - UIScreen *screen = (UIScreen *) [screens objectAtIndex:i]; - const CGSize size = [[screen currentMode] size]; - UIKit_AddDisplay(screen, (int) size.width, (int) size.height); + UIScreen *uiscreen = (UIScreen *) [screens objectAtIndex:i]; + const CGSize size = [[uiscreen currentMode] size]; + UIKit_AddDisplay(uiscreen, (int) size.width, (int) size.height); } } @@ -216,13 +216,13 @@ static void UIKit_DeleteDevice(SDL_VideoDevice * device) static int UIKit_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode) { - UIScreen *screen = (UIScreen *) display->driverdata; - if (!supports_multiple_displays) { + UIScreen *uiscreen = (UIScreen *) 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; - [screen setCurrentMode:uimode]; + [uiscreen setCurrentMode:uimode]; } return 0; @@ -235,8 +235,8 @@ static void UIKit_DeleteDevice(SDL_VideoDevice * device) int i, j; for (i = 0; i < _this->num_displays; i++) { SDL_VideoDisplay *display = &_this->displays[i]; - UIScreen *screen = (UIScreen *) display->driverdata; - [((UIScreen *) display->driverdata) release]; + UIScreen *uiscreen = (UIScreen *) display->driverdata; + [uiscreen release]; display->driverdata = NULL; for (j = 0; j < display->num_display_modes; j++) { SDL_DisplayMode *mode = &display->display_modes[j]; diff --git a/src/video/uikit/SDL_uikitwindow.h b/src/video/uikit/SDL_uikitwindow.h index 925b4d45e..f0f64f3b0 100644 --- a/src/video/uikit/SDL_uikitwindow.h +++ b/src/video/uikit/SDL_uikitwindow.h @@ -36,7 +36,6 @@ extern void UIKit_DestroyWindow(_THIS, SDL_Window * window); struct SDL_WindowData { - SDL_Window *window; UIWindow *uiwindow; SDL_uikitopenglview *view; }; diff --git a/src/video/uikit/SDL_uikitwindow.m b/src/video/uikit/SDL_uikitwindow.m index 252b2f5f1..f2337809e 100644 --- a/src/video/uikit/SDL_uikitwindow.m +++ b/src/video/uikit/SDL_uikitwindow.m @@ -23,6 +23,7 @@ #include "SDL_video.h" #include "SDL_mouse.h" +#include "SDL_assert.h" #include "../SDL_sysvideo.h" #include "../SDL_pixels_c.h" #include "../../events/SDL_events_c.h" @@ -38,8 +39,10 @@ #include #include -static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bool created) { - +static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bool created) +{ + SDL_VideoDisplay *display = window->display; + UIScreen *uiscreen = (UIScreen *) display->driverdata; SDL_WindowData *data; /* Allocate the window data */ @@ -48,7 +51,6 @@ static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bo SDL_OutOfMemory(); return -1; } - data->window = window; data->uiwindow = uiwindow; data->view = nil; @@ -68,12 +70,15 @@ static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bo window->flags |= SDL_WINDOW_SHOWN; /* only one window on iPod touch, always shown */ window->flags |= SDL_WINDOW_INPUT_FOCUS; /* always has input focus */ - /* SDL_WINDOW_BORDERLESS controls whether status bar is hidden */ - if (window->flags & SDL_WINDOW_BORDERLESS) { - [UIApplication sharedApplication].statusBarHidden = YES; - } - else { - [UIApplication sharedApplication].statusBarHidden = NO; + // 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 (window->flags & SDL_WINDOW_BORDERLESS) { + [UIApplication sharedApplication].statusBarHidden = YES; + } else { + [UIApplication sharedApplication].statusBarHidden = NO; + } } return 0; @@ -82,41 +87,80 @@ static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bo int UIKit_CreateWindow(_THIS, SDL_Window *window) { - /* We currently only handle single window applications on iPhone */ - if (nil != [SDLUIKitDelegate sharedAppDelegate].window) { - SDL_SetError("Window already exists, no multi-window support."); + SDL_VideoDisplay *display = window->display; + UIScreen *uiscreen = (UIScreen *) display->driverdata; + + // SDL currently puts this window at the start of display's linked list. We rely on this. + SDL_assert(display->windows == window); + + /* We currently only handle a single window per display on iPhone */ + if (window->next != NULL) { + SDL_SetError("Only one window allowed per display."); return -1; } - + + // Non-mainscreen windows must be force to borderless, as there's no + // status bar there, and we want to get the right dimensions later in + // this function. + if ([UIScreen mainScreen] != uiscreen) { + window->flags |= SDL_WINDOW_BORDERLESS; + } + + // If monitor has a resolution of 0x0 (hasn't been explicitly set by the + // 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]; + if ((origsize.width == 0.0f) && (origsize.height == 0.0f)) { + if (display->num_display_modes == 0) { + _this->GetDisplayModes(_this, display); + } + + int i; + const SDL_DisplayMode *bestmode = NULL; + for (i = display->num_display_modes; i >= 0; i--) { + const SDL_DisplayMode *mode = &display->display_modes[i]; + if ((mode->w >= window->w) && (mode->h >= window->h)) + bestmode = mode; + } + + if (bestmode) { + UIScreenMode *uimode = (UIScreenMode *) bestmode->driverdata; + [uiscreen setCurrentMode:uimode]; + display->desktop_mode = *bestmode; + display->current_mode = *bestmode; + } + } + } + /* ignore the size user requested, and make a fullscreen window */ - UIWindow *uiwindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - + // !!! FIXME: can we have a smaller view? + UIWindow *uiwindow = [UIWindow alloc]; + if (window->flags & SDL_WINDOW_BORDERLESS) + uiwindow = [uiwindow initWithFrame:[uiscreen bounds]]; + else + uiwindow = [uiwindow initWithFrame:[uiscreen applicationFrame]]; + + if (SDL_UIKit_supports_multiple_displays) { + [uiwindow setScreen:uiscreen]; + } + if (SetupWindowData(_this, window, uiwindow, SDL_TRUE) < 0) { [uiwindow release]; return -1; } - // This saves the main window in the app delegate so event callbacks can do stuff on the window. - // This assumes a single window application design and needs to be fixed for multiple windows. - [SDLUIKitDelegate sharedAppDelegate].window = window; - [SDLUIKitDelegate sharedAppDelegate].uiwindow = uiwindow; - [uiwindow release]; /* release the window (the app delegate has retained it) */ - return 1; } void UIKit_DestroyWindow(_THIS, SDL_Window * window) { - /* don't worry, the delegate will automatically release the window */ - SDL_WindowData *data = (SDL_WindowData *)window->driverdata; if (data) { - SDL_free( window->driverdata ); + [data->uiwindow release]; + SDL_free(data); + window->driverdata = NULL; } - - /* this will also destroy the window */ - [SDLUIKitDelegate sharedAppDelegate].window = NULL; - [SDLUIKitDelegate sharedAppDelegate].uiwindow = nil; } /* vi: set ts=4 sw=4 expandtab: */