From acb9f67bfe0058a5bc95eacd077fabac295b2893 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 28 Mar 2011 23:21:22 -0400 Subject: [PATCH] Added orientation rotation for iOS. --- src/video/uikit/SDL_uikitopengles.m | 18 ++++-- src/video/uikit/SDL_uikitopenglview.m | 3 + src/video/uikit/SDL_uikitview.h | 14 +++- src/video/uikit/SDL_uikitwindow.h | 1 + src/video/uikit/SDL_uikitwindow.m | 92 +++++++++++++++++++++++---- test/testgles.c | 2 +- test/testsprite2.c | 3 + 7 files changed, 114 insertions(+), 19 deletions(-) diff --git a/src/video/uikit/SDL_uikitopengles.m b/src/video/uikit/SDL_uikitopengles.m index 73bd328d3..d4ac4dc7c 100644 --- a/src/video/uikit/SDL_uikitopengles.m +++ b/src/video/uikit/SDL_uikitopengles.m @@ -117,13 +117,15 @@ SDL_GLContext UIKit_GL_CreateContext(_THIS, SDL_Window * window) majorVersion: _this->gl_config.major_version]; data->view = view; - + view->viewcontroller = data->viewcontroller; + if (view->viewcontroller != nil) { + [view->viewcontroller setView:view]; + [view->viewcontroller retain]; + } + /* add the view to our window */ [uiwindow addSubview: view ]; - - /* Don't worry, the window retained the view */ - [view release]; - + if ( UIKit_GL_MakeCurrent(_this, window, view) < 0 ) { UIKit_GL_DeleteContext(_this, view); return NULL; @@ -140,8 +142,12 @@ void UIKit_GL_DeleteContext(_THIS, SDL_GLContext context) { /* the delegate has retained the view, this will release him */ SDL_uikitopenglview *view = (SDL_uikitopenglview *)context; - /* this will also delete it */ + if (view->viewcontroller) { + [view->viewcontroller setView:nil]; + [view->viewcontroller release]; + } [view removeFromSuperview]; + [view release]; } /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/uikit/SDL_uikitopenglview.m b/src/video/uikit/SDL_uikitopenglview.m index bc179ff78..612de55bc 100644 --- a/src/video/uikit/SDL_uikitopenglview.m +++ b/src/video/uikit/SDL_uikitopenglview.m @@ -121,9 +121,12 @@ - (id)initWithFrame:(CGRect)frame \ } /* end create buffers */ + // !!! FIXME: use the screen this is on! /* Use the main screen scale (for retina display support) */ if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) self.contentScaleFactor = [UIScreen mainScreen].scale; + + self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; } return self; } diff --git a/src/video/uikit/SDL_uikitview.h b/src/video/uikit/SDL_uikitview.h index 5f29b54eb..e7de78e49 100644 --- a/src/video/uikit/SDL_uikitview.h +++ b/src/video/uikit/SDL_uikitview.h @@ -30,6 +30,16 @@ #define MAX_SIMULTANEOUS_TOUCHES 5 #endif +@interface SDL_uikitviewcontroller : UIViewController { +@private + SDL_Window *window; +} +- (id)initWithSDLWindow:(SDL_Window *)_window; +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orient; +- (void)loadView; +- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration; +@end + /* *INDENT-OFF* */ #if SDL_IPHONE_KEYBOARD @interface SDL_uikitview : UIView { @@ -48,7 +58,9 @@ UITextField *textField; BOOL keyboardVisible; #endif - + +@public + SDL_uikitviewcontroller *viewcontroller; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; diff --git a/src/video/uikit/SDL_uikitwindow.h b/src/video/uikit/SDL_uikitwindow.h index 6a36a59e9..b39bdae78 100644 --- a/src/video/uikit/SDL_uikitwindow.h +++ b/src/video/uikit/SDL_uikitwindow.h @@ -40,6 +40,7 @@ struct SDL_WindowData { UIWindow *uiwindow; SDL_uikitopenglview *view; + SDL_uikitviewcontroller *viewcontroller; }; diff --git a/src/video/uikit/SDL_uikitwindow.m b/src/video/uikit/SDL_uikitwindow.m index 946531313..0e54671b5 100644 --- a/src/video/uikit/SDL_uikitwindow.m +++ b/src/video/uikit/SDL_uikitwindow.m @@ -38,6 +38,58 @@ #include +@implementation SDL_uikitviewcontroller + +- (id)initWithSDLWindow:(SDL_Window *)_window { + [self init]; + self->window = _window; + return self; +} + +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orient { + return YES; +} + +- (void)loadView { + // do nothing. +} + +// Send a resized event when the orientation changes. +- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { + SDL_WindowData *data = self->window->driverdata; + UIWindow *uiwindow = data->uiwindow; + CGRect frame = [uiwindow frame]; + const CGSize size = frame.size; + int w, h; + + switch (toInterfaceOrientation) { + case UIInterfaceOrientationPortrait: + case UIInterfaceOrientationPortraitUpsideDown: + w = (size.width < size.height) ? size.width : size.height; + h = (size.width > size.height) ? size.width : size.height; + break; + + case UIInterfaceOrientationLandscapeLeft: + case UIInterfaceOrientationLandscapeRight: + w = (size.width > size.height) ? size.width : size.height; + h = (size.width < size.height) ? size.width : size.height; + break; + + default: + SDL_assert(0 && "Unexpected interface orientation!"); + return; + } + self->window->w = w; + self->window->h = h; + frame.size.width = w; + frame.size.height = h; + SDL_SendWindowEvent(self->window, SDL_WINDOWEVENT_RESIZED, w, h); +} + +@end + + + static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bool created) { SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); @@ -51,6 +103,7 @@ static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bo return -1; } data->uiwindow = uiwindow; + data->viewcontroller = nil; data->view = nil; /* Fill in the SDL window with the window data */ @@ -62,9 +115,11 @@ static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bo } window->driverdata = data; - + + // !!! FIXME: should we force this? Shouldn't specifying FULLSCREEN + // !!! FIXME: imply BORDERLESS? window->flags |= SDL_WINDOW_FULLSCREEN; /* window is always fullscreen */ - window->flags |= SDL_WINDOW_SHOWN; /* only one window on iPod touch, always shown */ + window->flags |= SDL_WINDOW_SHOWN; /* only one window on iOS, always shown */ // SDL_WINDOW_BORDERLESS controls whether status bar is hidden. // This is only set if the window is on the main screen. Other screens @@ -72,6 +127,7 @@ static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bo if ([UIScreen mainScreen] != 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. } else { window->flags |= SDL_WINDOW_INPUT_FOCUS; // always has input focus @@ -81,21 +137,34 @@ static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bo [UIApplication sharedApplication].statusBarHidden = NO; } - // Rotate the view if we have to, but only on the main screen - // (presumably, an external display doesn't report orientation). const CGSize uisize = [[uiscreen currentMode] size]; - if ( ((window->w > window->h) && (uisize.width < uisize.height)) || - ((window->w < window->h) && (uisize.width > uisize.height)) ) { - // !!! FIXME: flip orientation. - } + const UIDeviceOrientation o = [[UIDevice currentDevice] orientation]; + const BOOL landscape = (o == UIDeviceOrientationLandscapeLeft) || + (o == UIDeviceOrientationLandscapeRight); + const BOOL rotate = ( ((window->w > window->h) && (!landscape)) || + ((window->w < window->h) && (landscape)) ); if (window->flags & SDL_WINDOW_RESIZABLE) { - // !!! FIXME: register for orientation change alerts. + // The View Controller will handle rotating the view when the + // device orientation changes. We expose these as resize events. + SDL_uikitviewcontroller *controller; + controller = [SDL_uikitviewcontroller alloc]; + data->viewcontroller = [controller initWithSDLWindow:window]; + [data->viewcontroller setTitle:@"SDL App"]; // !!! FIXME: hook up SDL_SetWindowTitle() + // !!! FIXME: if (rotate), force a "resize" right at the start + } else { + // Rotate the view if we have to, but only on the main screen + // (presumably, an external display doesn't report orientation). + if (rotate) { + #define D2R(x) (M_PI * (x) / 180.0) // degrees to radians. + [uiwindow setTransform:CGAffineTransformIdentity]; + [uiwindow setTransform:CGAffineTransformMakeRotation(D2R(90))]; + #undef D2R + } } } return 0; - } int @@ -107,7 +176,7 @@ static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bo // SDL currently puts this window at the start of display's linked list. We rely on this. SDL_assert(_this->windows == window); - /* We currently only handle a single window per display on iPhone */ + /* We currently only handle a single window per display on iOS */ if (window->next != NULL) { SDL_SetError("Only one window allowed per display."); return -1; @@ -172,6 +241,7 @@ static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bo UIKit_DestroyWindow(_THIS, SDL_Window * window) { SDL_WindowData *data = (SDL_WindowData *)window->driverdata; if (data) { + [data->viewcontroller release]; [data->uiwindow release]; SDL_free(data); window->driverdata = NULL; diff --git a/test/testgles.c b/test/testgles.c index 99410f8b0..afb38429c 100644 --- a/test/testgles.c +++ b/test/testgles.c @@ -140,7 +140,7 @@ main(int argc, char *argv[]) } /* Set OpenGL parameters */ - state->window_flags |= SDL_WINDOW_OPENGL; + state->window_flags |= SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_BORDERLESS; state->gl_red_size = 5; state->gl_green_size = 5; state->gl_blue_size = 5; diff --git a/test/testsprite2.c b/test/testsprite2.c index 0b2032804..2190ea6ed 100644 --- a/test/testsprite2.c +++ b/test/testsprite2.c @@ -219,6 +219,9 @@ main(int argc, char *argv[]) if (!state) { return 1; } + + state->window_flags |= SDL_WINDOW_RESIZABLE; + for (i = 1; i < argc;) { int consumed;