src/video/uikit/SDL_uikitviewcontroller.m
author Alex Szpakowski
Tue, 05 May 2015 16:20:11 -0300
changeset 9585 8339700453c6
parent 9532 318042c16b76
child 9619 b94b6d0bff0f
permissions -rw-r--r--
Fixed the window offset on iOS when resuming an app with a borderless or fullscreen window that has the on-screen keyboard visible.
kees@5640
     1
/*
slouken@6044
     2
  Simple DirectMedia Layer
slouken@8149
     3
  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
kees@6001
     4
slouken@6044
     5
  This software is provided 'as-is', without any express or implied
slouken@6044
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@6044
     7
  arising from the use of this software.
slouken@6044
     8
slouken@6044
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@6044
    10
  including commercial applications, and to alter it and redistribute it
slouken@6044
    11
  freely, subject to the following restrictions:
kees@6001
    12
slouken@6044
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@6044
    14
     claim that you wrote the original software. If you use this software
slouken@6044
    15
     in a product, an acknowledgment in the product documentation would be
slouken@6044
    16
     appreciated but is not required.
slouken@6044
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@6044
    18
     misrepresented as being the original software.
slouken@6044
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@6044
    20
*/
icculus@8093
    21
#include "../../SDL_internal.h"
kees@6001
    22
slouken@6044
    23
#if SDL_VIDEO_DRIVER_UIKIT
kees@5640
    24
kees@5640
    25
#include "SDL_video.h"
kees@5640
    26
#include "SDL_assert.h"
kees@5640
    27
#include "SDL_hints.h"
kees@5640
    28
#include "../SDL_sysvideo.h"
kees@5640
    29
#include "../../events/SDL_events_c.h"
kees@5640
    30
slime73@9532
    31
#import "SDL_uikitviewcontroller.h"
slime73@9532
    32
#import "SDL_uikitmessagebox.h"
icculus@6093
    33
#include "SDL_uikitvideo.h"
slouken@6518
    34
#include "SDL_uikitmodes.h"
slouken@6518
    35
#include "SDL_uikitwindow.h"
slouken@6518
    36
slime73@9532
    37
#if SDL_IPHONE_KEYBOARD
slime73@9532
    38
#include "keyinfotable.h"
slime73@9532
    39
#endif
kees@5640
    40
slime73@9532
    41
@implementation SDL_uikitviewcontroller {
slime73@9532
    42
    CADisplayLink *displayLink;
slime73@9532
    43
    int animationInterval;
slime73@9532
    44
    void (*animationCallback)(void*);
slime73@9532
    45
    void *animationCallbackParam;
slime73@9532
    46
slime73@9532
    47
#if SDL_IPHONE_KEYBOARD
slime73@9532
    48
    UITextField *textField;
slime73@9532
    49
#endif
slime73@9532
    50
}
kees@5640
    51
kees@6011
    52
@synthesize window;
kees@6011
    53
slime73@9532
    54
- (instancetype)initWithSDLWindow:(SDL_Window *)_window
kees@6003
    55
{
slime73@9505
    56
    if (self = [super initWithNibName:nil bundle:nil]) {
slime73@9505
    57
        self.window = _window;
slime73@9532
    58
slime73@9532
    59
#if SDL_IPHONE_KEYBOARD
slime73@9532
    60
        [self initKeyboard];
slime73@9532
    61
#endif
kees@5640
    62
    }
kees@5640
    63
    return self;
kees@5640
    64
}
kees@5640
    65
slime73@9532
    66
- (void)dealloc
slime73@9532
    67
{
slime73@9532
    68
#if SDL_IPHONE_KEYBOARD
slime73@9532
    69
    [self deinitKeyboard];
slime73@9532
    70
#endif
slime73@9532
    71
}
slime73@9532
    72
slime73@9532
    73
- (void)setAnimationCallback:(int)interval
slime73@9532
    74
                    callback:(void (*)(void*))callback
slime73@9532
    75
               callbackParam:(void*)callbackParam
slime73@9532
    76
{
slime73@9532
    77
    [self stopAnimation];
slime73@9532
    78
slime73@9532
    79
    animationInterval = interval;
slime73@9532
    80
    animationCallback = callback;
slime73@9532
    81
    animationCallbackParam = callbackParam;
slime73@9532
    82
slime73@9532
    83
    if (animationCallback) {
slime73@9532
    84
        [self startAnimation];
slime73@9532
    85
    }
slime73@9532
    86
}
slime73@9532
    87
slime73@9532
    88
- (void)startAnimation
slime73@9532
    89
{
slime73@9532
    90
    displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(doLoop:)];
slime73@9532
    91
    [displayLink setFrameInterval:animationInterval];
slime73@9532
    92
    [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
slime73@9532
    93
}
slime73@9532
    94
slime73@9532
    95
- (void)stopAnimation
slime73@9532
    96
{
slime73@9532
    97
    [displayLink invalidate];
slime73@9532
    98
    displayLink = nil;
slime73@9532
    99
}
slime73@9532
   100
slime73@9532
   101
- (void)doLoop:(CADisplayLink*)sender
slime73@9532
   102
{
slime73@9532
   103
    /* Don't run the game loop while a messagebox is up */
slime73@9532
   104
    if (!UIKit_ShowingMessageBox()) {
slime73@9532
   105
        animationCallback(animationCallbackParam);
slime73@9532
   106
    }
slime73@9532
   107
}
slime73@9532
   108
slouken@6440
   109
- (void)loadView
slouken@6440
   110
{
slime73@9532
   111
    /* Do nothing. */
slouken@6440
   112
}
slouken@6440
   113
slouken@6451
   114
- (void)viewDidLayoutSubviews
slouken@6440
   115
{
slime73@9505
   116
    const CGSize size = self.view.bounds.size;
slime73@9494
   117
    int w = (int) size.width;
slime73@9494
   118
    int h = (int) size.height;
slouken@7191
   119
slime73@9494
   120
    SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, w, h);
slouken@6440
   121
}
slouken@6440
   122
slouken@6451
   123
- (NSUInteger)supportedInterfaceOrientations
kees@6003
   124
{
slime73@9523
   125
    return UIKit_GetSupportedOrientations(window);
kees@5640
   126
}
kees@5640
   127
slouken@6451
   128
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orient
kees@6003
   129
{
slime73@9532
   130
    return ([self supportedInterfaceOrientations] & (1 << orient)) != 0;
kees@5640
   131
}
kees@5640
   132
slouken@7862
   133
- (BOOL)prefersStatusBarHidden
slouken@7862
   134
{
slime73@9529
   135
    return (window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_BORDERLESS)) != 0;
slouken@7862
   136
}
slouken@7862
   137
slime73@9492
   138
- (UIStatusBarStyle)preferredStatusBarStyle
slime73@9492
   139
{
slime73@9523
   140
    /* We assume most SDL apps don't have a bright white background. */
slime73@9492
   141
    return UIStatusBarStyleLightContent;
slime73@9492
   142
}
slime73@9492
   143
slime73@9532
   144
/*
slime73@9532
   145
 ---- Keyboard related functionality below this line ----
slime73@9532
   146
 */
slime73@9532
   147
#if SDL_IPHONE_KEYBOARD
slime73@9532
   148
slime73@9532
   149
@synthesize textInputRect;
slime73@9532
   150
@synthesize keyboardHeight;
slime73@9532
   151
@synthesize keyboardVisible;
slime73@9532
   152
slime73@9532
   153
/* Set ourselves up as a UITextFieldDelegate */
slime73@9532
   154
- (void)initKeyboard
slime73@9532
   155
{
slime73@9532
   156
    textField = [[UITextField alloc] initWithFrame:CGRectZero];
slime73@9532
   157
    textField.delegate = self;
slime73@9532
   158
    /* placeholder so there is something to delete! */
slime73@9532
   159
    textField.text = @" ";
slime73@9532
   160
slime73@9532
   161
    /* set UITextInputTrait properties, mostly to defaults */
slime73@9532
   162
    textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
slime73@9532
   163
    textField.autocorrectionType = UITextAutocorrectionTypeNo;
slime73@9532
   164
    textField.enablesReturnKeyAutomatically = NO;
slime73@9532
   165
    textField.keyboardAppearance = UIKeyboardAppearanceDefault;
slime73@9532
   166
    textField.keyboardType = UIKeyboardTypeDefault;
slime73@9532
   167
    textField.returnKeyType = UIReturnKeyDefault;
slime73@9532
   168
    textField.secureTextEntry = NO;
slime73@9532
   169
slime73@9532
   170
    textField.hidden = YES;
slime73@9532
   171
    keyboardVisible = NO;
slime73@9532
   172
slime73@9532
   173
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
slime73@9532
   174
    [center addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
slime73@9532
   175
    [center addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
slime73@9532
   176
}
slime73@9532
   177
slime73@9532
   178
- (void)setView:(UIView *)view
slime73@9532
   179
{
slime73@9532
   180
    [super setView:view];
slime73@9532
   181
slime73@9532
   182
    [view addSubview:textField];
slime73@9532
   183
slime73@9532
   184
    if (keyboardVisible) {
slime73@9532
   185
        [self showKeyboard];
slime73@9532
   186
    }
slime73@9532
   187
}
slime73@9532
   188
slime73@9532
   189
- (void)deinitKeyboard
slime73@9532
   190
{
slime73@9532
   191
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
slime73@9532
   192
    [center removeObserver:self name:UIKeyboardWillShowNotification object:nil];
slime73@9532
   193
    [center removeObserver:self name:UIKeyboardWillHideNotification object:nil];
slime73@9532
   194
}
slime73@9532
   195
slime73@9532
   196
/* reveal onscreen virtual keyboard */
slime73@9532
   197
- (void)showKeyboard
slime73@9532
   198
{
slime73@9532
   199
    keyboardVisible = YES;
slime73@9532
   200
    if (textField.window) {
slime73@9532
   201
        [textField becomeFirstResponder];
slime73@9532
   202
    }
slime73@9532
   203
}
slime73@9532
   204
slime73@9532
   205
/* hide onscreen virtual keyboard */
slime73@9532
   206
- (void)hideKeyboard
slime73@9532
   207
{
slime73@9532
   208
    keyboardVisible = NO;
slime73@9532
   209
    [textField resignFirstResponder];
slime73@9532
   210
}
slime73@9532
   211
slime73@9532
   212
- (void)keyboardWillShow:(NSNotification *)notification
slime73@9532
   213
{
slime73@9532
   214
    CGRect kbrect = [[notification userInfo][UIKeyboardFrameBeginUserInfoKey] CGRectValue];
slime73@9532
   215
slime73@9585
   216
    /* The keyboard rect is in the coordinate space of the screen/window, but we
slime73@9585
   217
     * want its height in the coordinate space of the view. */
slime73@9585
   218
    kbrect = [self.view convertRect:kbrect fromView:nil];
slime73@9532
   219
slime73@9585
   220
    [self setKeyboardHeight:(int)kbrect.size.height];
slime73@9532
   221
}
slime73@9532
   222
slime73@9532
   223
- (void)keyboardWillHide:(NSNotification *)notification
slime73@9532
   224
{
slime73@9532
   225
    [self setKeyboardHeight:0];
slime73@9532
   226
}
slime73@9532
   227
slime73@9532
   228
- (void)updateKeyboard
slime73@9532
   229
{
slime73@9532
   230
    CGAffineTransform t = self.view.transform;
slime73@9532
   231
    CGPoint offset = CGPointMake(0.0, 0.0);
slime73@9585
   232
    CGRect frame = UIKit_ComputeViewFrame(window, self.view.window.screen);
slime73@9532
   233
slime73@9532
   234
    if (self.keyboardHeight) {
slime73@9585
   235
        int rectbottom = self.textInputRect.y + self.textInputRect.h;
slime73@9585
   236
        int keybottom = self.view.bounds.size.height - self.keyboardHeight;
slime73@9585
   237
        if (keybottom < rectbottom) {
slime73@9585
   238
            offset.y = keybottom - rectbottom;
slime73@9532
   239
        }
slime73@9532
   240
    }
slime73@9532
   241
slime73@9585
   242
    /* Apply this view's transform (except any translation) to the offset, in
slime73@9585
   243
     * order to orient it correctly relative to the frame's coordinate space. */
slime73@9532
   244
    t.tx = 0.0;
slime73@9532
   245
    t.ty = 0.0;
slime73@9532
   246
    offset = CGPointApplyAffineTransform(offset, t);
slime73@9532
   247
slime73@9585
   248
    /* Apply the updated offset to the view's frame. */
slime73@9585
   249
    frame.origin.x += offset.x;
slime73@9585
   250
    frame.origin.y += offset.y;
slime73@9532
   251
slime73@9585
   252
    self.view.frame = frame;
slime73@9532
   253
}
slime73@9532
   254
slime73@9532
   255
- (void)setKeyboardHeight:(int)height
slime73@9532
   256
{
slime73@9532
   257
    keyboardVisible = height > 0;
slime73@9532
   258
    keyboardHeight = height;
slime73@9532
   259
    [self updateKeyboard];
slime73@9532
   260
}
slime73@9532
   261
slime73@9532
   262
/* UITextFieldDelegate method.  Invoked when user types something. */
slime73@9532
   263
- (BOOL)textField:(UITextField *)_textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
slime73@9532
   264
{
slime73@9532
   265
    NSUInteger len = string.length;
slime73@9532
   266
slime73@9532
   267
    if (len == 0) {
slime73@9532
   268
        /* it wants to replace text with nothing, ie a delete */
slime73@9532
   269
        SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_BACKSPACE);
slime73@9532
   270
        SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_BACKSPACE);
slime73@9532
   271
    } else {
slime73@9532
   272
        /* go through all the characters in the string we've been sent and
slime73@9532
   273
         * convert them to key presses */
slime73@9532
   274
        int i;
slime73@9532
   275
        for (i = 0; i < len; i++) {
slime73@9532
   276
            unichar c = [string characterAtIndex:i];
slime73@9532
   277
            Uint16 mod = 0;
slime73@9532
   278
            SDL_Scancode code;
slime73@9532
   279
slime73@9532
   280
            if (c < 127) {
slime73@9532
   281
                /* figure out the SDL_Scancode and SDL_keymod for this unichar */
slime73@9532
   282
                code = unicharToUIKeyInfoTable[c].code;
slime73@9532
   283
                mod  = unicharToUIKeyInfoTable[c].mod;
slime73@9532
   284
            } else {
slime73@9532
   285
                /* we only deal with ASCII right now */
slime73@9532
   286
                code = SDL_SCANCODE_UNKNOWN;
slime73@9532
   287
                mod = 0;
slime73@9532
   288
            }
slime73@9532
   289
slime73@9532
   290
            if (mod & KMOD_SHIFT) {
slime73@9532
   291
                /* If character uses shift, press shift down */
slime73@9532
   292
                SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_LSHIFT);
slime73@9532
   293
            }
slime73@9532
   294
slime73@9532
   295
            /* send a keydown and keyup even for the character */
slime73@9532
   296
            SDL_SendKeyboardKey(SDL_PRESSED, code);
slime73@9532
   297
            SDL_SendKeyboardKey(SDL_RELEASED, code);
slime73@9532
   298
slime73@9532
   299
            if (mod & KMOD_SHIFT) {
slime73@9532
   300
                /* If character uses shift, press shift back up */
slime73@9532
   301
                SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LSHIFT);
slime73@9532
   302
            }
slime73@9532
   303
        }
slime73@9532
   304
slime73@9532
   305
        SDL_SendKeyboardText([string UTF8String]);
slime73@9532
   306
    }
slime73@9532
   307
slime73@9532
   308
    return NO; /* don't allow the edit! (keep placeholder text there) */
slime73@9532
   309
}
slime73@9532
   310
slime73@9532
   311
/* Terminates the editing session */
slime73@9532
   312
- (BOOL)textFieldShouldReturn:(UITextField*)_textField
slime73@9532
   313
{
slime73@9532
   314
    SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_RETURN);
slime73@9532
   315
    SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RETURN);
slime73@9532
   316
    SDL_StopTextInput();
slime73@9532
   317
    return YES;
slime73@9532
   318
}
slime73@9532
   319
slime73@9532
   320
#endif
slime73@9532
   321
slouken@6451
   322
@end
slouken@6451
   323
slime73@9532
   324
/* iPhone keyboard addition functions */
slime73@9532
   325
#if SDL_IPHONE_KEYBOARD
slime73@9532
   326
slime73@9532
   327
static SDL_uikitviewcontroller *
slime73@9532
   328
GetWindowViewController(SDL_Window * window)
slime73@9532
   329
{
slime73@9532
   330
    if (!window || !window->driverdata) {
slime73@9532
   331
        SDL_SetError("Invalid window");
slime73@9532
   332
        return nil;
slime73@9532
   333
    }
slime73@9532
   334
slime73@9532
   335
    SDL_WindowData *data = (__bridge SDL_WindowData *)window->driverdata;
slime73@9532
   336
slime73@9532
   337
    return data.viewcontroller;
slime73@9532
   338
}
slime73@9532
   339
slime73@9532
   340
SDL_bool
slime73@9532
   341
UIKit_HasScreenKeyboardSupport(_THIS)
slime73@9532
   342
{
slime73@9532
   343
    return SDL_TRUE;
slime73@9532
   344
}
slime73@9532
   345
slime73@9532
   346
void
slime73@9532
   347
UIKit_ShowScreenKeyboard(_THIS, SDL_Window *window)
slime73@9532
   348
{
slime73@9532
   349
    @autoreleasepool {
slime73@9532
   350
        SDL_uikitviewcontroller *vc = GetWindowViewController(window);
slime73@9532
   351
        [vc showKeyboard];
slime73@9532
   352
    }
slime73@9532
   353
}
slime73@9532
   354
slime73@9532
   355
void
slime73@9532
   356
UIKit_HideScreenKeyboard(_THIS, SDL_Window *window)
slime73@9532
   357
{
slime73@9532
   358
    @autoreleasepool {
slime73@9532
   359
        SDL_uikitviewcontroller *vc = GetWindowViewController(window);
slime73@9532
   360
        [vc hideKeyboard];
slime73@9532
   361
    }
slime73@9532
   362
}
slime73@9532
   363
slime73@9532
   364
SDL_bool
slime73@9532
   365
UIKit_IsScreenKeyboardShown(_THIS, SDL_Window *window)
slime73@9532
   366
{
slime73@9532
   367
    @autoreleasepool {
slime73@9532
   368
        SDL_uikitviewcontroller *vc = GetWindowViewController(window);
slime73@9532
   369
        if (vc != nil) {
slime73@9532
   370
            return vc.isKeyboardVisible;
slime73@9532
   371
        }
slime73@9532
   372
        return SDL_FALSE;
slime73@9532
   373
    }
slime73@9532
   374
}
slime73@9532
   375
slime73@9532
   376
void
slime73@9532
   377
UIKit_SetTextInputRect(_THIS, SDL_Rect *rect)
slime73@9532
   378
{
slime73@9532
   379
    if (!rect) {
slime73@9532
   380
        SDL_InvalidParamError("rect");
slime73@9532
   381
        return;
slime73@9532
   382
    }
slime73@9532
   383
slime73@9532
   384
    @autoreleasepool {
slime73@9532
   385
        SDL_uikitviewcontroller *vc = GetWindowViewController(SDL_GetFocusWindow());
slime73@9532
   386
        if (vc != nil) {
slime73@9532
   387
            vc.textInputRect = *rect;
slime73@9532
   388
slime73@9532
   389
            if (vc.keyboardVisible) {
slime73@9532
   390
                [vc updateKeyboard];
slime73@9532
   391
            }
slime73@9532
   392
        }
slime73@9532
   393
    }
slime73@9532
   394
}
slime73@9532
   395
slime73@9532
   396
slime73@9532
   397
#endif /* SDL_IPHONE_KEYBOARD */
slime73@9532
   398
slouken@6044
   399
#endif /* SDL_VIDEO_DRIVER_UIKIT */
slouken@6044
   400
slouken@6440
   401
/* vi: set ts=4 sw=4 expandtab: */