src/video/uikit/SDL_uikitwindow.m
author Alex Szpakowski <slime73@gmail.com>
Wed, 08 Apr 2015 15:35:07 -0300
branchiOS-improvements
changeset 9537 ff30c198864e
parent 9532 318042c16b76
child 9540 32ddc92d78cf
permissions -rw-r--r--
Merged default into iOS-improvements
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
slime73@9532
    40
#import "SDL_uikitview.h"
slouken@2765
    41
slouken@2765
    42
#include <Foundation/Foundation.h>
slouken@2765
    43
slime73@9510
    44
@implementation SDL_WindowData
slime73@9510
    45
slime73@9510
    46
@synthesize uiwindow;
slime73@9510
    47
@synthesize viewcontroller;
slime73@9532
    48
@synthesize views;
slime73@9532
    49
slime73@9532
    50
- (instancetype)init
slime73@9532
    51
{
slime73@9532
    52
    if ((self = [super init])) {
slime73@9532
    53
        views = [NSMutableArray new];
slime73@9532
    54
    }
slime73@9532
    55
slime73@9532
    56
    return self;
slime73@9532
    57
}
slime73@9532
    58
slime73@9532
    59
@end
slime73@9532
    60
slime73@9532
    61
@interface SDL_uikitwindow : UIWindow
slime73@9532
    62
slime73@9532
    63
- (void)layoutSubviews;
slime73@9532
    64
slime73@9532
    65
@end
slime73@9532
    66
slime73@9532
    67
@implementation SDL_uikitwindow
slime73@9532
    68
slime73@9532
    69
- (void)layoutSubviews
slime73@9532
    70
{
slime73@9532
    71
    /* Workaround to fix window orientation issues in iOS 8+. */
slime73@9532
    72
    self.frame = self.screen.bounds;
slime73@9532
    73
    [super layoutSubviews];
slime73@9532
    74
}
slime73@9510
    75
slime73@9510
    76
@end
slime73@9510
    77
slime73@9505
    78
slime73@9528
    79
static int SetupWindowData(_THIS, SDL_Window *window, UIWindow *uiwindow, SDL_bool created)
icculus@4446
    80
{
slouken@5246
    81
    SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
slime73@9510
    82
    SDL_DisplayData *displaydata = (__bridge SDL_DisplayData *) display->driverdata;
slime73@9532
    83
    SDL_uikitview *view;
slime73@9532
    84
slime73@9532
    85
    CGRect frame = UIKit_ComputeViewFrame(window, displaydata.uiscreen);
slime73@9532
    86
    int width  = (int) frame.size.width;
slime73@9532
    87
    int height = (int) frame.size.height;
kees@6001
    88
slime73@9510
    89
    SDL_WindowData *data = [[SDL_WindowData alloc] init];
slouken@2765
    90
    if (!data) {
icculus@7037
    91
        return SDL_OutOfMemory();
slouken@2765
    92
    }
slime73@9510
    93
slime73@9532
    94
    window->driverdata = (void *) CFBridgingRetain(data);
slime73@9532
    95
slime73@9510
    96
    data.uiwindow = uiwindow;
slouken@3685
    97
slime73@9532
    98
    /* only one window on iOS, always shown */
slime73@9532
    99
    window->flags &= ~SDL_WINDOW_HIDDEN;
slouken@6437
   100
slime73@9532
   101
    if (displaydata.uiscreen == [UIScreen mainScreen]) {
slime73@9532
   102
        window->flags |= SDL_WINDOW_INPUT_FOCUS;  /* always has input focus */
slime73@9532
   103
    } else {
slime73@9532
   104
        window->flags &= ~SDL_WINDOW_RESIZABLE;  /* window is NEVER resizable */
slime73@9532
   105
        window->flags &= ~SDL_WINDOW_INPUT_FOCUS;  /* never has input focus */
slime73@9532
   106
        window->flags |= SDL_WINDOW_BORDERLESS;  /* never has a status bar. */
slime73@9532
   107
    }
slime73@9532
   108
slime73@9532
   109
    if (displaydata.uiscreen == [UIScreen mainScreen]) {
slime73@9532
   110
        NSUInteger orients = UIKit_GetSupportedOrientations(window);
slime73@9532
   111
        BOOL supportsLandscape = (orients & UIInterfaceOrientationMaskLandscape) != 0;
slime73@9532
   112
        BOOL supportsPortrait = (orients & (UIInterfaceOrientationMaskPortrait|UIInterfaceOrientationMaskPortraitUpsideDown)) != 0;
slouken@6520
   113
slouken@7191
   114
        /* Make sure the width/height are oriented correctly */
slime73@9532
   115
        if ((width > height && !supportsLandscape) || (height > width && !supportsPortrait)) {
slouken@6520
   116
            int temp = width;
slouken@6520
   117
            width = height;
slouken@6520
   118
            height = temp;
slouken@6077
   119
        }
slouken@2765
   120
    }
kees@6001
   121
slime73@9532
   122
    window->x = 0;
slime73@9532
   123
    window->y = 0;
slime73@9532
   124
    window->w = width;
slime73@9532
   125
    window->h = height;
icculus@5520
   126
slime73@9532
   127
    /* The View Controller will handle rotating the view when the device
slime73@9532
   128
     * orientation changes. This will trigger resize events, if appropriate. */
slime73@9532
   129
    data.viewcontroller = [[SDL_uikitviewcontroller alloc] initWithSDLWindow:window];
slouken@2765
   130
slime73@9532
   131
    /* The window will initially contain a generic view so resizes, touch events,
slime73@9532
   132
     * etc. can be handled without an active OpenGL view/context. */
slime73@9532
   133
    view = [[SDL_uikitview alloc] initWithFrame:frame];
slime73@9532
   134
slime73@9532
   135
    /* Sets this view as the controller's view, and adds the view to the window
slime73@9532
   136
     * heirarchy. */
slime73@9532
   137
    [view setSDLWindow:window];
slime73@9532
   138
slime73@9532
   139
    /* Make this window the current mouse focus for touch input */
slime73@9510
   140
    if (displaydata.uiscreen == [UIScreen mainScreen]) {
slime73@9532
   141
        SDL_SetMouseFocus(window);
slime73@9532
   142
        SDL_SetKeyboardFocus(window);
slouken@6511
   143
    }
icculus@5518
   144
slouken@2765
   145
    return 0;
slouken@2765
   146
}
slouken@2765
   147
slouken@5056
   148
int
slouken@5246
   149
UIKit_CreateWindow(_THIS, SDL_Window *window)
slouken@5246
   150
{
slime73@9506
   151
    @autoreleasepool {
slime73@9506
   152
        SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
slime73@9510
   153
        SDL_DisplayData *data = (__bridge SDL_DisplayData *) display->driverdata;
slime73@9532
   154
        const CGSize origsize = data.uiscreen.currentMode.size;
icculus@4446
   155
slime73@9506
   156
        /* SDL currently puts this window at the start of display's linked list. We rely on this. */
slime73@9506
   157
        SDL_assert(_this->windows == window);
icculus@4446
   158
slime73@9506
   159
        /* We currently only handle a single window per display on iOS */
slime73@9506
   160
        if (window->next != NULL) {
slime73@9506
   161
            return SDL_SetError("Only one window allowed per display.");
slouken@8891
   162
        }
icculus@4446
   163
slime73@9506
   164
        /* If monitor has a resolution of 0x0 (hasn't been explicitly set by the
slime73@9506
   165
         * user, so it's in standby), try to force the display to a resolution
slime73@9532
   166
         * that most closely matches the desired window size. */
slime73@9506
   167
        if ((origsize.width == 0.0f) && (origsize.height == 0.0f)) {
slime73@9506
   168
            if (display->num_display_modes == 0) {
slime73@9506
   169
                _this->GetDisplayModes(_this, display);
slime73@9506
   170
            }
slime73@9506
   171
slime73@9506
   172
            int i;
slime73@9506
   173
            const SDL_DisplayMode *bestmode = NULL;
slime73@9506
   174
            for (i = display->num_display_modes; i >= 0; i--) {
slime73@9506
   175
                const SDL_DisplayMode *mode = &display->display_modes[i];
slime73@9532
   176
                if ((mode->w >= window->w) && (mode->h >= window->h)) {
slime73@9506
   177
                    bestmode = mode;
slime73@9532
   178
                }
slime73@9506
   179
            }
slime73@9506
   180
slime73@9506
   181
            if (bestmode) {
slime73@9510
   182
                SDL_DisplayModeData *modedata = (__bridge SDL_DisplayModeData *)bestmode->driverdata;
slime73@9510
   183
                [data.uiscreen setCurrentMode:modedata.uiscreenmode];
slime73@9506
   184
slime73@9506
   185
                /* desktop_mode doesn't change here (the higher level will
slime73@9506
   186
                 * use it to set all the screens back to their defaults
slime73@9532
   187
                 * upon window destruction, SDL_Quit(), etc. */
slime73@9506
   188
                display->current_mode = *bestmode;
slime73@9506
   189
            }
slouken@8891
   190
        }
icculus@4446
   191
slime73@9510
   192
        if (data.uiscreen == [UIScreen mainScreen]) {
slime73@9523
   193
            NSUInteger orientations = UIKit_GetSupportedOrientations(window);
slime73@9523
   194
            UIApplication *app = [UIApplication sharedApplication];
slime73@9523
   195
slime73@9506
   196
            if (window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_BORDERLESS)) {
slime73@9523
   197
                app.statusBarHidden = YES;
slime73@9506
   198
            } else {
slime73@9523
   199
                app.statusBarHidden = NO;
slouken@6520
   200
            }
slime73@9506
   201
        }
kees@6001
   202
slime73@9506
   203
        /* ignore the size user requested, and make a fullscreen window */
slime73@9506
   204
        /* !!! FIXME: can we have a smaller view? */
slime73@9532
   205
        UIWindow *uiwindow = [[SDL_uikitwindow alloc] initWithFrame:data.uiscreen.bounds];
slouken@7862
   206
slime73@9532
   207
        /* put the window on an external display if appropriate. */
slime73@9532
   208
        if (data.uiscreen != [UIScreen mainScreen]) {
slime73@9510
   209
            [uiwindow setScreen:data.uiscreen];
slime73@9506
   210
        }
slime73@9506
   211
slime73@9506
   212
        if (SetupWindowData(_this, window, uiwindow, SDL_TRUE) < 0) {
slime73@9506
   213
            return -1;
slime73@9506
   214
        }
kees@6001
   215
    }
kees@6001
   216
slouken@3685
   217
    return 1;
slouken@2765
   218
}
slouken@2765
   219
slouken@5056
   220
void
slime73@9529
   221
UIKit_SetWindowTitle(_THIS, SDL_Window * window)
slime73@9529
   222
{
slime73@9529
   223
    @autoreleasepool {
slime73@9532
   224
        SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
slime73@9537
   225
        data.viewcontroller.title = @(window->title);
slime73@9529
   226
    }
slime73@9529
   227
}
slime73@9529
   228
slime73@9529
   229
void
slouken@6379
   230
UIKit_ShowWindow(_THIS, SDL_Window * window)
slouken@6379
   231
{
slime73@9506
   232
    @autoreleasepool {
slime73@9532
   233
        SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
slime73@9532
   234
        [data.uiwindow makeKeyAndVisible];
slime73@9506
   235
    }
slouken@6379
   236
}
slouken@6379
   237
slouken@6379
   238
void
slouken@6379
   239
UIKit_HideWindow(_THIS, SDL_Window * window)
slouken@6379
   240
{
slime73@9506
   241
    @autoreleasepool {
slime73@9532
   242
        SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
slime73@9532
   243
        data.uiwindow.hidden = YES;
slime73@9506
   244
    }
slouken@6379
   245
}
slouken@6379
   246
slouken@6379
   247
void
icculus@6419
   248
UIKit_RaiseWindow(_THIS, SDL_Window * window)
icculus@6419
   249
{
slouken@7191
   250
    /* We don't currently offer a concept of "raising" the SDL window, since
slime73@9532
   251
     * we only allow one per display, in the iOS fashion.
slouken@7191
   252
     * However, we use this entry point to rebind the context to the view
slime73@9532
   253
     * during OnWindowRestored processing. */
icculus@6419
   254
    _this->GL_MakeCurrent(_this, _this->current_glwin, _this->current_glctx);
icculus@6419
   255
}
icculus@6419
   256
slime73@9505
   257
static void
slime73@9505
   258
UIKit_UpdateWindowBorder(_THIS, SDL_Window * window)
slouken@6073
   259
{
slime73@9532
   260
    SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
slime73@9532
   261
    SDL_uikitviewcontroller *viewcontroller = data.viewcontroller;
slouken@6073
   262
slime73@9532
   263
    if (data.uiwindow.screen == [UIScreen mainScreen]) {
slime73@9505
   264
        if (window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS)) {
slime73@9505
   265
            [UIApplication sharedApplication].statusBarHidden = YES;
slime73@9505
   266
        } else {
slime73@9505
   267
            [UIApplication sharedApplication].statusBarHidden = NO;
slime73@9505
   268
        }
slouken@6437
   269
slime73@9505
   270
        /* iOS 7+ won't update the status bar until we tell it to. */
slime73@9505
   271
        if ([viewcontroller respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) {
slime73@9505
   272
            [viewcontroller setNeedsStatusBarAppearanceUpdate];
slime73@9505
   273
        }
slime73@9491
   274
    }
slime73@9491
   275
slime73@9497
   276
    /* Update the view's frame to account for the status bar change. */
slime73@9532
   277
    viewcontroller.view.frame = UIKit_ComputeViewFrame(window, data.uiwindow.screen);
slime73@9532
   278
    [viewcontroller.view setNeedsLayout];
slime73@9532
   279
    [viewcontroller.view layoutIfNeeded];
slouken@6073
   280
}
slouken@6073
   281
slouken@6073
   282
void
slime73@9505
   283
UIKit_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered)
slime73@9505
   284
{
slime73@9506
   285
    @autoreleasepool {
slime73@9506
   286
        UIKit_UpdateWindowBorder(_this, window);
slime73@9506
   287
    }
slime73@9505
   288
}
slime73@9505
   289
slime73@9505
   290
void
slime73@9505
   291
UIKit_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
slime73@9505
   292
{
slime73@9506
   293
    @autoreleasepool {
slime73@9506
   294
        UIKit_UpdateWindowBorder(_this, window);
slime73@9506
   295
    }
slime73@9505
   296
}
slime73@9505
   297
slime73@9505
   298
void
kees@6003
   299
UIKit_DestroyWindow(_THIS, SDL_Window * window)
kees@6003
   300
{
slime73@9506
   301
    @autoreleasepool {
slime73@9510
   302
        if (window->driverdata != NULL) {
slime73@9532
   303
            SDL_WindowData *data = (SDL_WindowData *) CFBridgingRelease(window->driverdata);
slime73@9532
   304
            [data.viewcontroller stopAnimation];
slime73@9506
   305
        }
slouken@3685
   306
    }
slouken@8978
   307
    window->driverdata = NULL;
slouken@2765
   308
}
slouken@2765
   309
slouken@5056
   310
SDL_bool
slouken@5056
   311
UIKit_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
slouken@5056
   312
{
slime73@9506
   313
    @autoreleasepool {
slime73@9532
   314
        SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
slouken@5056
   315
slime73@9506
   316
        if (info->version.major <= SDL_MAJOR_VERSION) {
slime73@9506
   317
            info->subsystem = SDL_SYSWM_UIKIT;
slime73@9532
   318
            info->info.uikit.window = data.uiwindow;
slime73@9506
   319
            return SDL_TRUE;
slime73@9506
   320
        } else {
slime73@9506
   321
            SDL_SetError("Application not compiled with SDL %d.%d\n",
slime73@9506
   322
                         SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
slime73@9506
   323
            return SDL_FALSE;
slime73@9506
   324
        }
slouken@5056
   325
    }
slouken@5056
   326
}
slouken@5056
   327
slime73@9523
   328
NSUInteger
slime73@9523
   329
UIKit_GetSupportedOrientations(SDL_Window * window)
slime73@9523
   330
{
slime73@9523
   331
    const char *hint = SDL_GetHint(SDL_HINT_ORIENTATIONS);
slime73@9523
   332
    NSUInteger orientationMask = 0;
slime73@9523
   333
slime73@9523
   334
    @autoreleasepool {
slime73@9523
   335
        if (hint != NULL) {
slime73@9523
   336
            NSArray *orientations = [@(hint) componentsSeparatedByString:@" "];
slime73@9523
   337
slime73@9523
   338
            if ([orientations containsObject:@"LandscapeLeft"]) {
slime73@9523
   339
                orientationMask |= UIInterfaceOrientationMaskLandscapeLeft;
slime73@9523
   340
            }
slime73@9523
   341
            if ([orientations containsObject:@"LandscapeRight"]) {
slime73@9523
   342
                orientationMask |= UIInterfaceOrientationMaskLandscapeRight;
slime73@9523
   343
            }
slime73@9523
   344
            if ([orientations containsObject:@"Portrait"]) {
slime73@9523
   345
                orientationMask |= UIInterfaceOrientationMaskPortrait;
slime73@9523
   346
            }
slime73@9523
   347
            if ([orientations containsObject:@"PortraitUpsideDown"]) {
slime73@9523
   348
                orientationMask |= UIInterfaceOrientationMaskPortraitUpsideDown;
slime73@9523
   349
            }
slime73@9523
   350
        }
slime73@9523
   351
slime73@9523
   352
        if (orientationMask == 0 && (window->flags & SDL_WINDOW_RESIZABLE)) {
slime73@9523
   353
            /* any orientation is okay. */
slime73@9523
   354
            orientationMask = UIInterfaceOrientationMaskAll;
slime73@9523
   355
        }
slime73@9523
   356
slime73@9523
   357
        if (orientationMask == 0) {
slime73@9523
   358
            if (window->w >= window->h) {
slime73@9523
   359
                orientationMask |= UIInterfaceOrientationMaskLandscape;
slime73@9523
   360
            }
slime73@9523
   361
            if (window->h >= window->w) {
slime73@9523
   362
                orientationMask |= (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown);
slime73@9523
   363
            }
slime73@9523
   364
        }
slime73@9523
   365
slime73@9523
   366
        /* Don't allow upside-down orientation on the phone, so answering calls is in the natural orientation */
slime73@9532
   367
        if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) {
slime73@9523
   368
            orientationMask &= ~UIInterfaceOrientationMaskPortraitUpsideDown;
slime73@9523
   369
        }
slime73@9523
   370
    }
slime73@9523
   371
slime73@9523
   372
    return orientationMask;
slime73@9523
   373
}
slime73@9523
   374
slouken@6342
   375
int
slouken@6342
   376
SDL_iPhoneSetAnimationCallback(SDL_Window * window, int interval, void (*callback)(void*), void *callbackParam)
slouken@6342
   377
{
slime73@9532
   378
    if (!window || !window->driverdata) {
slime73@9532
   379
        return SDL_SetError("Invalid window");
slime73@9532
   380
    }
slime73@9532
   381
slime73@9510
   382
    @autoreleasepool {
slime73@9532
   383
        SDL_WindowData *data = (__bridge SDL_WindowData *)window->driverdata;
slime73@9532
   384
        [data.viewcontroller setAnimationCallback:interval
slime73@9532
   385
                                         callback:callback
slime73@9532
   386
                                    callbackParam:callbackParam];
slime73@9506
   387
    }
slime73@9506
   388
slouken@6342
   389
    return 0;
slouken@6342
   390
}
slouken@6342
   391
slouken@6044
   392
#endif /* SDL_VIDEO_DRIVER_UIKIT */
slouken@6044
   393
slouken@2765
   394
/* vi: set ts=4 sw=4 expandtab: */