src/video/uikit/SDL_uikitwindow.m
author Alex Szpakowski <slime73@gmail.com>
Wed, 23 Jul 2014 03:05:31 -0300
branchiOS-improvements
changeset 9501 574db299498f
parent 9500 cbf5c5ecf5ac
child 9502 933ed557b7c1
permissions -rw-r--r--
More cleanup of the iOS Objective-C code.
slouken@2765
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@8149
     3
  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
slouken@2765
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@2765
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@2765
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@2765
    20
*/
icculus@8093
    21
#include "../../SDL_internal.h"
slouken@2765
    22
slouken@6044
    23
#if SDL_VIDEO_DRIVER_UIKIT
slouken@6044
    24
slouken@5056
    25
#include "SDL_syswm.h"
slouken@2765
    26
#include "SDL_video.h"
slouken@2765
    27
#include "SDL_mouse.h"
icculus@4446
    28
#include "SDL_assert.h"
tim@5554
    29
#include "SDL_hints.h"
slouken@2765
    30
#include "../SDL_sysvideo.h"
slouken@2765
    31
#include "../SDL_pixels_c.h"
slouken@2765
    32
#include "../../events/SDL_events_c.h"
slouken@2765
    33
slouken@2765
    34
#include "SDL_uikitvideo.h"
slouken@2765
    35
#include "SDL_uikitevents.h"
slouken@6518
    36
#include "SDL_uikitmodes.h"
slouken@2765
    37
#include "SDL_uikitwindow.h"
slouken@2765
    38
#import "SDL_uikitappdelegate.h"
slouken@2765
    39
slouken@2765
    40
#import "SDL_uikitopenglview.h"
slouken@2765
    41
slouken@2765
    42
#include <Foundation/Foundation.h>
slouken@2765
    43
icculus@5520
    44
icculus@4446
    45
static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bool created)
icculus@4446
    46
{
slouken@5246
    47
    SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
tim@6267
    48
    SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
slouken@2765
    49
    SDL_WindowData *data;
kees@6001
    50
slouken@2765
    51
    /* Allocate the window data */
slouken@2765
    52
    data = (SDL_WindowData *)SDL_malloc(sizeof(*data));
slouken@2765
    53
    if (!data) {
icculus@7037
    54
        return SDL_OutOfMemory();
slouken@2765
    55
    }
slouken@2765
    56
    data->uiwindow = uiwindow;
icculus@5520
    57
    data->viewcontroller = nil;
slouken@3685
    58
    data->view = nil;
slouken@3685
    59
slouken@2765
    60
    /* Fill in the SDL window with the window data */
slouken@3685
    61
    {
slouken@2765
    62
        window->x = 0;
slouken@2765
    63
        window->y = 0;
slouken@6077
    64
slouken@6437
    65
        CGRect bounds;
slouken@6437
    66
        if (window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_BORDERLESS)) {
slouken@6437
    67
            bounds = [displaydata->uiscreen bounds];
slouken@6437
    68
        } else {
slouken@6437
    69
            bounds = [displaydata->uiscreen applicationFrame];
slouken@6437
    70
        }
slouken@6437
    71
slime73@9488
    72
        /* Get frame dimensions */
slime73@9488
    73
        int width = (int) bounds.size.width;
slime73@9488
    74
        int height = (int) bounds.size.height;
slouken@6520
    75
slouken@7191
    76
        /* Make sure the width/height are oriented correctly */
slouken@6520
    77
        if (UIKit_IsDisplayLandscape(displaydata->uiscreen) != (width > height)) {
slouken@6520
    78
            int temp = width;
slouken@6520
    79
            width = height;
slouken@6520
    80
            height = temp;
slouken@6077
    81
        }
slouken@6520
    82
slouken@6520
    83
        window->w = width;
slouken@6520
    84
        window->h = height;
slouken@2765
    85
    }
kees@6001
    86
slouken@3685
    87
    window->driverdata = data;
icculus@5520
    88
slouken@6262
    89
    /* only one window on iOS, always shown */
slouken@6262
    90
    window->flags &= ~SDL_WINDOW_HIDDEN;
slouken@2765
    91
slouken@7191
    92
    /* SDL_WINDOW_BORDERLESS controls whether status bar is hidden.
slouken@7191
    93
     * This is only set if the window is on the main screen. Other screens
slouken@7191
    94
     *  just force the window to have the borderless flag.
slouken@7191
    95
     */
slouken@6520
    96
    if (displaydata->uiscreen == [UIScreen mainScreen]) {
slouken@7191
    97
        window->flags |= SDL_WINDOW_INPUT_FOCUS;  /* always has input focus */
slouken@7191
    98
slouken@7862
    99
        /* This was setup earlier for our window, and in iOS 7 is controlled by the view, not the application
slouken@6520
   100
        if ([UIApplication sharedApplication].statusBarHidden) {
slouken@6520
   101
            window->flags |= SDL_WINDOW_BORDERLESS;
icculus@4446
   102
        } else {
slouken@6520
   103
            window->flags &= ~SDL_WINDOW_BORDERLESS;
icculus@4446
   104
        }
slouken@7862
   105
        */
slouken@6511
   106
    } else {
slouken@7191
   107
        window->flags &= ~SDL_WINDOW_RESIZABLE;  /* window is NEVER resizeable */
slouken@7191
   108
        window->flags &= ~SDL_WINDOW_INPUT_FOCUS;  /* never has input focus */
slouken@7191
   109
        window->flags |= SDL_WINDOW_BORDERLESS;  /* never has a status bar. */
slouken@6511
   110
    }
icculus@5518
   111
slouken@7191
   112
    /* The View Controller will handle rotating the view when the
slouken@7191
   113
     * device orientation changes. This will trigger resize events, if
slouken@7191
   114
     * appropriate.
slouken@7191
   115
     */
slime73@9495
   116
    data->viewcontroller = [[SDL_uikitviewcontroller alloc] initWithSDLWindow:window];
slouken@7191
   117
    [data->viewcontroller setTitle:@"SDL App"];  /* !!! FIXME: hook up SDL_SetWindowTitle() */
icculus@5518
   118
slouken@2765
   119
    return 0;
slouken@2765
   120
}
slouken@2765
   121
slouken@5056
   122
int
slouken@5246
   123
UIKit_CreateWindow(_THIS, SDL_Window *window)
slouken@5246
   124
{
slouken@5246
   125
    SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
tim@6267
   126
    SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata;
tim@6267
   127
    const BOOL external = ([UIScreen mainScreen] != data->uiscreen);
slouken@8891
   128
    const CGSize origsize = [[data->uiscreen currentMode] size];
icculus@4446
   129
slouken@7191
   130
    /* SDL currently puts this window at the start of display's linked list. We rely on this. */
slouken@5251
   131
    SDL_assert(_this->windows == window);
icculus@4446
   132
icculus@5520
   133
    /* We currently only handle a single window per display on iOS */
icculus@4446
   134
    if (window->next != NULL) {
icculus@7037
   135
        return SDL_SetError("Only one window allowed per display.");
slouken@3685
   136
    }
icculus@4446
   137
slouken@7191
   138
    /* If monitor has a resolution of 0x0 (hasn't been explicitly set by the
slouken@7191
   139
     * user, so it's in standby), try to force the display to a resolution
slouken@7191
   140
     * that most closely matches the desired window size.
slouken@7191
   141
     */
slouken@8891
   142
    if ((origsize.width == 0.0f) && (origsize.height == 0.0f)) {
slouken@8891
   143
        if (display->num_display_modes == 0) {
slouken@8891
   144
            _this->GetDisplayModes(_this, display);
slouken@8891
   145
        }
icculus@4446
   146
slouken@8891
   147
        int i;
slouken@8891
   148
        const SDL_DisplayMode *bestmode = NULL;
slouken@8891
   149
        for (i = display->num_display_modes; i >= 0; i--) {
slouken@8891
   150
            const SDL_DisplayMode *mode = &display->display_modes[i];
slouken@8891
   151
            if ((mode->w >= window->w) && (mode->h >= window->h))
slouken@8891
   152
                bestmode = mode;
slouken@8891
   153
        }
icculus@4446
   154
slouken@8891
   155
        if (bestmode) {
slouken@8891
   156
            SDL_DisplayModeData *modedata = (SDL_DisplayModeData *)bestmode->driverdata;
slouken@8891
   157
            [data->uiscreen setCurrentMode:modedata->uiscreenmode];
icculus@5997
   158
slouken@8891
   159
            /* desktop_mode doesn't change here (the higher level will
slouken@8891
   160
             * use it to set all the screens back to their defaults
slouken@8891
   161
             * upon window destruction, SDL_Quit(), etc.
slouken@8891
   162
             */
slouken@8891
   163
            display->current_mode = *bestmode;
icculus@4446
   164
        }
icculus@4446
   165
    }
slouken@7191
   166
slouken@6520
   167
    if (data->uiscreen == [UIScreen mainScreen]) {
slouken@6520
   168
        if (window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_BORDERLESS)) {
slouken@6520
   169
            [UIApplication sharedApplication].statusBarHidden = YES;
slouken@6520
   170
        } else {
slouken@6520
   171
            [UIApplication sharedApplication].statusBarHidden = NO;
slouken@6520
   172
        }
slouken@6520
   173
    }
slouken@7191
   174
slouken@6520
   175
    if (!(window->flags & SDL_WINDOW_RESIZABLE)) {
slouken@6520
   176
        if (window->w > window->h) {
slouken@6520
   177
            if (!UIKit_IsDisplayLandscape(data->uiscreen)) {
slouken@6520
   178
                [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:NO];
slouken@6520
   179
            }
slouken@6520
   180
        } else if (window->w < window->h) {
slouken@6520
   181
            if (UIKit_IsDisplayLandscape(data->uiscreen)) {
slouken@6520
   182
                [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait animated:NO];
slouken@6520
   183
            }
slouken@6520
   184
        }
slouken@6520
   185
    }
icculus@4446
   186
slouken@3685
   187
    /* ignore the size user requested, and make a fullscreen window */
slouken@7191
   188
    /* !!! FIXME: can we have a smaller view? */
slime73@9488
   189
    UIWindow *uiwindow = [[UIWindow alloc] initWithFrame:[data->uiscreen bounds]];
kees@6001
   190
slouken@7191
   191
    /* put the window on an external display if appropriate. This implicitly
slouken@7191
   192
     * does [uiwindow setframe:[uiscreen bounds]], so don't do it on the
slouken@7191
   193
     * main display, where we land by default, as that would eat the
slouken@7191
   194
     * status bar real estate.
slouken@7191
   195
     */
icculus@5529
   196
    if (external) {
tim@6267
   197
        [uiwindow setScreen:data->uiscreen];
icculus@4446
   198
    }
slouken@7862
   199
slouken@3685
   200
    if (SetupWindowData(_this, window, uiwindow, SDL_TRUE) < 0) {
slouken@2765
   201
        [uiwindow release];
slouken@2765
   202
        return -1;
kees@6001
   203
    }
kees@6001
   204
slouken@3685
   205
    return 1;
kees@6001
   206
slouken@2765
   207
}
slouken@2765
   208
slouken@5056
   209
void
slouken@6379
   210
UIKit_ShowWindow(_THIS, SDL_Window * window)
slouken@6379
   211
{
slouken@6379
   212
    UIWindow *uiwindow = ((SDL_WindowData *) window->driverdata)->uiwindow;
slouken@6379
   213
slouken@6379
   214
    [uiwindow makeKeyAndVisible];
slouken@6379
   215
}
slouken@6379
   216
slouken@6379
   217
void
slouken@6379
   218
UIKit_HideWindow(_THIS, SDL_Window * window)
slouken@6379
   219
{
slouken@6379
   220
    UIWindow *uiwindow = ((SDL_WindowData *) window->driverdata)->uiwindow;
slouken@6379
   221
slouken@6379
   222
    uiwindow.hidden = YES;
slouken@6379
   223
}
slouken@6379
   224
slouken@6379
   225
void
icculus@6419
   226
UIKit_RaiseWindow(_THIS, SDL_Window * window)
icculus@6419
   227
{
slouken@7191
   228
    /* We don't currently offer a concept of "raising" the SDL window, since
slouken@7191
   229
     *  we only allow one per display, in the iOS fashion.
slouken@7191
   230
     * However, we use this entry point to rebind the context to the view
slouken@7191
   231
     *  during OnWindowRestored processing.
slouken@7191
   232
     */
icculus@6419
   233
    _this->GL_MakeCurrent(_this, _this->current_glwin, _this->current_glctx);
icculus@6419
   234
}
icculus@6419
   235
icculus@6419
   236
void
slouken@6073
   237
UIKit_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
slouken@6073
   238
{
tim@6267
   239
    SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata;
slime73@9491
   240
    SDL_WindowData *windowdata = (SDL_WindowData *) window->driverdata;
slime73@9491
   241
    SDL_uikitviewcontroller *viewcontroller = windowdata->viewcontroller;
slime73@9497
   242
    CGRect bounds;
slouken@6073
   243
slime73@9495
   244
    if (fullscreen || (window->flags & SDL_WINDOW_BORDERLESS)) {
slouken@6073
   245
        [UIApplication sharedApplication].statusBarHidden = YES;
slouken@6073
   246
    } else {
slouken@6073
   247
        [UIApplication sharedApplication].statusBarHidden = NO;
slouken@6437
   248
    }
slouken@6437
   249
slime73@9500
   250
    /* iOS 7+ won't update the status bar until we tell it to. */
slime73@9491
   251
    if ([viewcontroller respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) {
slime73@9491
   252
        [viewcontroller setNeedsStatusBarAppearanceUpdate];
slime73@9491
   253
    }
slime73@9491
   254
slime73@9497
   255
    if (fullscreen || (window->flags & SDL_WINDOW_BORDERLESS)) {
slouken@6437
   256
        bounds = [displaydata->uiscreen bounds];
slouken@6437
   257
    } else {
slouken@6437
   258
        bounds = [displaydata->uiscreen applicationFrame];
slouken@6073
   259
    }
slouken@6077
   260
slime73@9497
   261
    /* Update the view's frame to account for the status bar change. */
slime73@9497
   262
    windowdata->view.frame = bounds;
slime73@9497
   263
    [windowdata->view setNeedsLayout];
slime73@9497
   264
    [windowdata->view layoutIfNeeded];
slime73@9497
   265
slime73@9488
   266
    /* Get frame dimensions */
slime73@9488
   267
    int width = (int) bounds.size.width;
slime73@9488
   268
    int height = (int) bounds.size.height;
tim@6267
   269
slouken@6077
   270
    /* We can pick either width or height here and we'll rotate the
slouken@6077
   271
       screen to match, so we pick the closest to what we wanted.
slouken@6077
   272
     */
slouken@6077
   273
    if (window->w >= window->h) {
slime73@9497
   274
        window->w = SDL_max(width, height);
slime73@9497
   275
        window->h = SDL_min(width, height);
slouken@6077
   276
    } else {
slime73@9497
   277
        window->w = SDL_min(width, height);
slime73@9497
   278
        window->h = SDL_max(width, height);
slouken@6077
   279
    }
slouken@6073
   280
}
slouken@6073
   281
slouken@6073
   282
void
kees@6003
   283
UIKit_DestroyWindow(_THIS, SDL_Window * window)
kees@6003
   284
{
slouken@3685
   285
    SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
slouken@8905
   286
slouken@3685
   287
    if (data) {
icculus@5520
   288
        [data->viewcontroller release];
icculus@4446
   289
        [data->uiwindow release];
icculus@4446
   290
        SDL_free(data);
slouken@3685
   291
    }
slouken@8978
   292
    window->driverdata = NULL;
slouken@2765
   293
}
slouken@2765
   294
slouken@5056
   295
SDL_bool
slouken@5056
   296
UIKit_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
slouken@5056
   297
{
slouken@5056
   298
    UIWindow *uiwindow = ((SDL_WindowData *) window->driverdata)->uiwindow;
slouken@5056
   299
slouken@5056
   300
    if (info->version.major <= SDL_MAJOR_VERSION) {
slouken@5056
   301
        info->subsystem = SDL_SYSWM_UIKIT;
slouken@5056
   302
        info->info.uikit.window = uiwindow;
slouken@5056
   303
        return SDL_TRUE;
slouken@5056
   304
    } else {
slouken@5056
   305
        SDL_SetError("Application not compiled with SDL %d.%d\n",
slouken@5056
   306
                     SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
slouken@5056
   307
        return SDL_FALSE;
slouken@5056
   308
    }
slouken@5056
   309
}
slouken@5056
   310
slouken@6342
   311
int
slouken@6342
   312
SDL_iPhoneSetAnimationCallback(SDL_Window * window, int interval, void (*callback)(void*), void *callbackParam)
slouken@6342
   313
{
slouken@6342
   314
    SDL_WindowData *data = window ? (SDL_WindowData *)window->driverdata : NULL;
slouken@6342
   315
slouken@6342
   316
    if (!data || !data->view) {
icculus@7037
   317
        return SDL_SetError("Invalid window or view not set");
slouken@6342
   318
    }
slouken@6342
   319
slouken@6342
   320
    [data->view setAnimationCallback:interval callback:callback callbackParam:callbackParam];
slouken@6342
   321
    return 0;
slouken@6342
   322
}
slouken@6342
   323
slouken@6044
   324
#endif /* SDL_VIDEO_DRIVER_UIKIT */
slouken@6044
   325
slouken@2765
   326
/* vi: set ts=4 sw=4 expandtab: */