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