Much improved multi-display support for iPad.
authorRyan C. Gordon <icculus@icculus.org>
Sun, 02 May 2010 05:08:12 -0400
changeset 4446 8b03a20b320f
parent 4445 06becafcac89
child 4447 947201caa46e
Much improved multi-display support for iPad. Fixes most issues and limitations, I think.
src/video/uikit/SDL_uikitappdelegate.h
src/video/uikit/SDL_uikitappdelegate.m
src/video/uikit/SDL_uikitopengles.m
src/video/uikit/SDL_uikitvideo.h
src/video/uikit/SDL_uikitvideo.m
src/video/uikit/SDL_uikitwindow.h
src/video/uikit/SDL_uikitwindow.m
--- a/src/video/uikit/SDL_uikitappdelegate.h	Sat May 01 13:50:56 2010 -0400
+++ b/src/video/uikit/SDL_uikitappdelegate.h	Sun May 02 05:08:12 2010 -0400
@@ -25,13 +25,8 @@
 
 /* *INDENT-OFF* */
 @interface SDLUIKitDelegate:NSObject<UIApplicationDelegate> {
-    SDL_Window *window;
-    UIWindow *uiwindow;
 }
 
-@property (readwrite, assign) SDL_Window *window;
-@property (readwrite, retain) UIWindow *uiwindow;
-
 +(SDLUIKitDelegate *)sharedAppDelegate;
 
 @end
--- a/src/video/uikit/SDL_uikitappdelegate.m	Sat May 01 13:50:56 2010 -0400
+++ b/src/video/uikit/SDL_uikitappdelegate.m	Sun May 02 05:08:12 2010 -0400
@@ -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 @@
 
 @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 @@
 
 - (id)init {
 	self = [super init];
-	window = NULL;
-	uiwindow = nil;
 	return self;
 }
 
@@ -106,21 +103,42 @@
 
 - (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);
-}
+    //NSLog(@"%@", NSStringFromSelector(_cmd));
 
+    // Send every window on every screen a RESTORED event.
+    SDL_VideoDevice *_this = SDL_GetVideoDevice();
+    if (!_this) {
+        return;
+    }
 
-
--(void)dealloc {
-	[uiwindow release];
-	[super dealloc];
+    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
--- a/src/video/uikit/SDL_uikitopengles.m	Sat May 01 13:50:56 2010 -0400
+++ b/src/video/uikit/SDL_uikitopengles.m	Sun May 02 05:08:12 2010 -0400
@@ -99,13 +99,13 @@
 
 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 @@
 	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];
--- a/src/video/uikit/SDL_uikitvideo.h	Sat May 01 13:50:56 2010 -0400
+++ b/src/video/uikit/SDL_uikitvideo.h	Sun May 02 05:08:12 2010 -0400
@@ -26,6 +26,10 @@
 
 #include "../SDL_sysvideo.h"
 
+#include <UIKit/UIKit.h>
+
+extern BOOL SDL_UIKit_supports_multiple_displays;
+
 #endif /* _SDL_uikitvideo_h */
 
 /* vi: set ts=4 sw=4 expandtab: */
--- a/src/video/uikit/SDL_uikitvideo.m	Sat May 01 13:50:56 2010 -0400
+++ b/src/video/uikit/SDL_uikitvideo.m	Sun May 02 05:08:12 2010 -0400
@@ -49,7 +49,7 @@
                                 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_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 @@
         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_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 @@
     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 @@
     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 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 @@
     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];
--- a/src/video/uikit/SDL_uikitwindow.h	Sat May 01 13:50:56 2010 -0400
+++ b/src/video/uikit/SDL_uikitwindow.h	Sun May 02 05:08:12 2010 -0400
@@ -36,7 +36,6 @@
 
 struct SDL_WindowData
 {
-    SDL_Window *window;
     UIWindow *uiwindow;
     SDL_uikitopenglview *view;
 };
--- a/src/video/uikit/SDL_uikitwindow.m	Sat May 01 13:50:56 2010 -0400
+++ b/src/video/uikit/SDL_uikitwindow.m	Sun May 02 05:08:12 2010 -0400
@@ -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 <UIKit/UIKit.h>
 #include <Foundation/Foundation.h>
 
-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 @@
         SDL_OutOfMemory();
         return -1;
     }
-    data->window = window;
     data->uiwindow = uiwindow;
     data->view = nil;
 
@@ -68,12 +70,15 @@
     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 @@
 
 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: */