| /* |
| Simple DirectMedia Layer |
| Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org> |
| |
| This software is provided 'as-is', without any express or implied |
| warranty. In no event will the authors be held liable for any damages |
| arising from the use of this software. |
| |
| Permission is granted to anyone to use this software for any purpose, |
| including commercial applications, and to alter it and redistribute it |
| freely, subject to the following restrictions: |
| |
| 1. The origin of this software must not be misrepresented; you must not |
| claim that you wrote the original software. If you use this software |
| in a product, an acknowledgment in the product documentation would be |
| appreciated but is not required. |
| 2. Altered source versions must be plainly marked as such, and must not be |
| misrepresented as being the original software. |
| 3. This notice may not be removed or altered from any source distribution. |
| */ |
| #include "SDL_config.h" |
| |
| #if SDL_VIDEO_DRIVER_UIKIT |
| |
| #include "SDL_syswm.h" |
| #include "SDL_video.h" |
| #include "SDL_mouse.h" |
| #include "SDL_assert.h" |
| #include "SDL_hints.h" |
| #include "../SDL_sysvideo.h" |
| #include "../SDL_pixels_c.h" |
| #include "../../events/SDL_events_c.h" |
| |
| #include "SDL_uikitvideo.h" |
| #include "SDL_uikitevents.h" |
| #include "SDL_uikitmodes.h" |
| #include "SDL_uikitwindow.h" |
| #import "SDL_uikitappdelegate.h" |
| |
| #import "SDL_uikitopenglview.h" |
| |
| #include <Foundation/Foundation.h> |
| |
| |
| |
| |
| static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bool created) |
| { |
| SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); |
| SDL_DisplayModeData *displaymodedata = (SDL_DisplayModeData *) display->current_mode.driverdata; |
| SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata; |
| SDL_WindowData *data; |
| |
| /* Allocate the window data */ |
| data = (SDL_WindowData *)SDL_malloc(sizeof(*data)); |
| if (!data) { |
| return SDL_OutOfMemory(); |
| } |
| data->uiwindow = uiwindow; |
| data->viewcontroller = nil; |
| data->view = nil; |
| |
| /* Fill in the SDL window with the window data */ |
| { |
| window->x = 0; |
| window->y = 0; |
| |
| CGRect bounds; |
| if (window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_BORDERLESS)) { |
| bounds = [displaydata->uiscreen bounds]; |
| } else { |
| bounds = [displaydata->uiscreen applicationFrame]; |
| } |
| |
| /* Get frame dimensions in pixels */ |
| int width = (int)(bounds.size.width * displaymodedata->scale); |
| int height = (int)(bounds.size.height * displaymodedata->scale); |
| |
| /* Make sure the width/height are oriented correctly */ |
| if (UIKit_IsDisplayLandscape(displaydata->uiscreen) != (width > height)) { |
| int temp = width; |
| width = height; |
| height = temp; |
| } |
| |
| window->w = width; |
| window->h = height; |
| } |
| |
| window->driverdata = data; |
| |
| /* only one window on iOS, always shown */ |
| window->flags &= ~SDL_WINDOW_HIDDEN; |
| |
| /* 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 (displaydata->uiscreen == [UIScreen mainScreen]) { |
| window->flags |= SDL_WINDOW_INPUT_FOCUS; /* always has input focus */ |
| |
| if ([UIApplication sharedApplication].statusBarHidden) { |
| window->flags |= SDL_WINDOW_BORDERLESS; |
| } else { |
| window->flags &= ~SDL_WINDOW_BORDERLESS; |
| } |
| } else { |
| 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. */ |
| } |
| |
| /* The View Controller will handle rotating the view when the |
| * device orientation changes. This will trigger resize events, if |
| * appropriate. |
| */ |
| SDL_uikitviewcontroller *controller; |
| controller = [SDL_uikitviewcontroller alloc]; |
| data->viewcontroller = [controller initWithSDLWindow:window]; |
| [data->viewcontroller setTitle:@"SDL App"]; /* !!! FIXME: hook up SDL_SetWindowTitle() */ |
| |
| return 0; |
| } |
| |
| int |
| UIKit_CreateWindow(_THIS, SDL_Window *window) |
| { |
| SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); |
| 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); |
| |
| /* We currently only handle a single window per display on iOS */ |
| if (window->next != NULL) { |
| return SDL_SetError("Only one window allowed per display."); |
| } |
| |
| /* 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 = [[data->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) { |
| 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 |
| * upon window destruction, SDL_Quit(), etc. |
| */ |
| display->current_mode = *bestmode; |
| } |
| } |
| } |
| |
| if (data->uiscreen == [UIScreen mainScreen]) { |
| if (window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_BORDERLESS)) { |
| [UIApplication sharedApplication].statusBarHidden = YES; |
| } else { |
| [UIApplication sharedApplication].statusBarHidden = NO; |
| } |
| } |
| |
| if (!(window->flags & SDL_WINDOW_RESIZABLE)) { |
| if (window->w > window->h) { |
| if (!UIKit_IsDisplayLandscape(data->uiscreen)) { |
| [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:NO]; |
| } |
| } else if (window->w < window->h) { |
| if (UIKit_IsDisplayLandscape(data->uiscreen)) { |
| [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait animated:NO]; |
| } |
| } |
| } |
| |
| /* ignore the size user requested, and make a fullscreen window */ |
| /* !!! FIXME: can we have a smaller view? */ |
| UIWindow *uiwindow = [UIWindow alloc]; |
| uiwindow = [uiwindow initWithFrame:[data->uiscreen bounds]]; |
| |
| /* 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:data->uiscreen]; |
| } |
| |
| if (SetupWindowData(_this, window, uiwindow, SDL_TRUE) < 0) { |
| [uiwindow release]; |
| return -1; |
| } |
| |
| return 1; |
| |
| } |
| |
| void |
| UIKit_ShowWindow(_THIS, SDL_Window * window) |
| { |
| UIWindow *uiwindow = ((SDL_WindowData *) window->driverdata)->uiwindow; |
| |
| [uiwindow makeKeyAndVisible]; |
| } |
| |
| void |
| UIKit_HideWindow(_THIS, SDL_Window * window) |
| { |
| UIWindow *uiwindow = ((SDL_WindowData *) window->driverdata)->uiwindow; |
| |
| uiwindow.hidden = YES; |
| } |
| |
| void |
| UIKit_RaiseWindow(_THIS, SDL_Window * window) |
| { |
| /* We don't currently offer a concept of "raising" the SDL window, since |
| * we only allow one per display, in the iOS fashion. |
| * However, we use this entry point to rebind the context to the view |
| * during OnWindowRestored processing. |
| */ |
| _this->GL_MakeCurrent(_this, _this->current_glwin, _this->current_glctx); |
| } |
| |
| void |
| UIKit_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen) |
| { |
| 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; |
| } else { |
| [UIApplication sharedApplication].statusBarHidden = NO; |
| } |
| |
| CGRect bounds; |
| if (fullscreen) { |
| bounds = [displaydata->uiscreen bounds]; |
| } else { |
| bounds = [displaydata->uiscreen applicationFrame]; |
| } |
| |
| /* Get frame dimensions in pixels */ |
| int width = (int)(bounds.size.width * displaymodedata->scale); |
| int height = (int)(bounds.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 (width > height) { |
| window->w = width; |
| window->h = height; |
| } else { |
| window->w = height; |
| window->h = width; |
| } |
| } else { |
| if (width > height) { |
| window->w = height; |
| window->h = width; |
| } else { |
| window->w = width; |
| window->h = height; |
| } |
| } |
| } |
| |
| void |
| 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; |
| } |
| } |
| |
| SDL_bool |
| UIKit_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info) |
| { |
| UIWindow *uiwindow = ((SDL_WindowData *) window->driverdata)->uiwindow; |
| |
| if (info->version.major <= SDL_MAJOR_VERSION) { |
| info->subsystem = SDL_SYSWM_UIKIT; |
| info->info.uikit.window = uiwindow; |
| return SDL_TRUE; |
| } else { |
| SDL_SetError("Application not compiled with SDL %d.%d\n", |
| SDL_MAJOR_VERSION, SDL_MINOR_VERSION); |
| return SDL_FALSE; |
| } |
| } |
| |
| int |
| SDL_iPhoneSetAnimationCallback(SDL_Window * window, int interval, void (*callback)(void*), void *callbackParam) |
| { |
| SDL_WindowData *data = window ? (SDL_WindowData *)window->driverdata : NULL; |
| |
| if (!data || !data->view) { |
| return SDL_SetError("Invalid window or view not set"); |
| } |
| |
| [data->view setAnimationCallback:interval callback:callback callbackParam:callbackParam]; |
| return 0; |
| } |
| |
| #endif /* SDL_VIDEO_DRIVER_UIKIT */ |
| |
| /* vi: set ts=4 sw=4 expandtab: */ |