src/video/uikit/SDL_uikitviewcontroller.m
author Sam Lantinga
Fri, 27 Jan 2017 06:05:50 -0800
changeset 10856 486aa38c6a88
parent 10737 3406a0f8b041
child 11147 5b2841f73a33
permissions -rw-r--r--
Added Thrustmaster Wheel FFB entry to the list of wheels
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 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 #include "SDL_uikitopengles.h"
    37 
    38 #if SDL_IPHONE_KEYBOARD
    39 #include "keyinfotable.h"
    40 #endif
    41 
    42 #if TARGET_OS_TV
    43 static void
    44 SDL_AppleTVControllerUIHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
    45 {
    46     @autoreleasepool {
    47         SDL_uikitviewcontroller *viewcontroller = (__bridge SDL_uikitviewcontroller *) userdata;
    48         viewcontroller.controllerUserInteractionEnabled = hint && (*hint != '0');
    49     }
    50 }
    51 #endif
    52 
    53 @implementation SDL_uikitviewcontroller {
    54     CADisplayLink *displayLink;
    55     int animationInterval;
    56     void (*animationCallback)(void*);
    57     void *animationCallbackParam;
    58 
    59 #if SDL_IPHONE_KEYBOARD
    60     UITextField *textField;
    61     BOOL rotatingOrientation;
    62 #endif
    63 }
    64 
    65 @synthesize window;
    66 
    67 - (instancetype)initWithSDLWindow:(SDL_Window *)_window
    68 {
    69     if (self = [super initWithNibName:nil bundle:nil]) {
    70         self.window = _window;
    71 
    72 #if SDL_IPHONE_KEYBOARD
    73         [self initKeyboard];
    74         rotatingOrientation = FALSE;
    75 #endif
    76 
    77 #if TARGET_OS_TV
    78         SDL_AddHintCallback(SDL_HINT_APPLE_TV_CONTROLLER_UI_EVENTS,
    79                             SDL_AppleTVControllerUIHintChanged,
    80                             (__bridge void *) self);
    81 #endif
    82     }
    83     return self;
    84 }
    85 
    86 - (void)dealloc
    87 {
    88 #if SDL_IPHONE_KEYBOARD
    89     [self deinitKeyboard];
    90 #endif
    91 
    92 #if TARGET_OS_TV
    93     SDL_DelHintCallback(SDL_HINT_APPLE_TV_CONTROLLER_UI_EVENTS,
    94                         SDL_AppleTVControllerUIHintChanged,
    95                         (__bridge void *) self);
    96 #endif
    97 }
    98 
    99 - (void)setAnimationCallback:(int)interval
   100                     callback:(void (*)(void*))callback
   101                callbackParam:(void*)callbackParam
   102 {
   103     [self stopAnimation];
   104 
   105     animationInterval = interval;
   106     animationCallback = callback;
   107     animationCallbackParam = callbackParam;
   108 
   109     if (animationCallback) {
   110         [self startAnimation];
   111     }
   112 }
   113 
   114 - (void)startAnimation
   115 {
   116     displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(doLoop:)];
   117     [displayLink setFrameInterval:animationInterval];
   118     [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
   119 }
   120 
   121 - (void)stopAnimation
   122 {
   123     [displayLink invalidate];
   124     displayLink = nil;
   125 }
   126 
   127 - (void)doLoop:(CADisplayLink*)sender
   128 {
   129     /* Don't run the game loop while a messagebox is up */
   130     if (!UIKit_ShowingMessageBox()) {
   131         /* See the comment in the function definition. */
   132         UIKit_GL_RestoreCurrentContext();
   133 
   134         animationCallback(animationCallbackParam);
   135     }
   136 }
   137 
   138 - (void)loadView
   139 {
   140     /* Do nothing. */
   141 }
   142 
   143 - (void)viewDidLayoutSubviews
   144 {
   145     const CGSize size = self.view.bounds.size;
   146     int w = (int) size.width;
   147     int h = (int) size.height;
   148 
   149     SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, w, h);
   150 }
   151 
   152 #if !TARGET_OS_TV
   153 - (NSUInteger)supportedInterfaceOrientations
   154 {
   155     return UIKit_GetSupportedOrientations(window);
   156 }
   157 
   158 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orient
   159 {
   160     return ([self supportedInterfaceOrientations] & (1 << orient)) != 0;
   161 }
   162 
   163 - (BOOL)prefersStatusBarHidden
   164 {
   165     return (window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_BORDERLESS)) != 0;
   166 }
   167 #endif
   168 
   169 /*
   170  ---- Keyboard related functionality below this line ----
   171  */
   172 #if SDL_IPHONE_KEYBOARD
   173 
   174 @synthesize textInputRect;
   175 @synthesize keyboardHeight;
   176 @synthesize keyboardVisible;
   177 
   178 /* Set ourselves up as a UITextFieldDelegate */
   179 - (void)initKeyboard
   180 {
   181     textField = [[UITextField alloc] initWithFrame:CGRectZero];
   182     textField.delegate = self;
   183     /* placeholder so there is something to delete! */
   184     textField.text = @" ";
   185 
   186     /* set UITextInputTrait properties, mostly to defaults */
   187     textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
   188     textField.autocorrectionType = UITextAutocorrectionTypeNo;
   189     textField.enablesReturnKeyAutomatically = NO;
   190     textField.keyboardAppearance = UIKeyboardAppearanceDefault;
   191     textField.keyboardType = UIKeyboardTypeDefault;
   192     textField.returnKeyType = UIReturnKeyDefault;
   193     textField.secureTextEntry = NO;
   194 
   195     textField.hidden = YES;
   196     keyboardVisible = NO;
   197 
   198 #if !TARGET_OS_TV
   199     NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
   200     [center addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
   201     [center addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
   202 #endif
   203 }
   204 
   205 - (void)setView:(UIView *)view
   206 {
   207     [super setView:view];
   208 
   209     [view addSubview:textField];
   210 
   211     if (keyboardVisible) {
   212         [self showKeyboard];
   213     }
   214 }
   215 
   216 /* willRotateToInterfaceOrientation and didRotateFromInterfaceOrientation are deprecated in iOS 8+ in favor of viewWillTransitionToSize */
   217 #if TARGET_OS_TV || __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000
   218 - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
   219 {
   220     [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
   221     rotatingOrientation = TRUE;
   222     [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {}
   223                                  completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
   224                                      rotatingOrientation = FALSE;
   225                                  }];
   226 }
   227 #else
   228 - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
   229     [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
   230     rotatingOrientation = TRUE;
   231 }
   232 
   233 - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
   234     [super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
   235     rotatingOrientation = FALSE;
   236 }
   237 #endif /* TARGET_OS_TV || __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000 */
   238 
   239 - (void)deinitKeyboard
   240 {
   241 #if !TARGET_OS_TV
   242     NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
   243     [center removeObserver:self name:UIKeyboardWillShowNotification object:nil];
   244     [center removeObserver:self name:UIKeyboardWillHideNotification object:nil];
   245 #endif
   246 }
   247 
   248 /* reveal onscreen virtual keyboard */
   249 - (void)showKeyboard
   250 {
   251     keyboardVisible = YES;
   252     if (textField.window) {
   253         [textField becomeFirstResponder];
   254     }
   255 }
   256 
   257 /* hide onscreen virtual keyboard */
   258 - (void)hideKeyboard
   259 {
   260     keyboardVisible = NO;
   261     [textField resignFirstResponder];
   262 }
   263 
   264 - (void)keyboardWillShow:(NSNotification *)notification
   265 {
   266 #if !TARGET_OS_TV
   267     CGRect kbrect = [[notification userInfo][UIKeyboardFrameBeginUserInfoKey] CGRectValue];
   268 
   269     /* The keyboard rect is in the coordinate space of the screen/window, but we
   270      * want its height in the coordinate space of the view. */
   271     kbrect = [self.view convertRect:kbrect fromView:nil];
   272 
   273     [self setKeyboardHeight:(int)kbrect.size.height];
   274 #endif
   275 }
   276 
   277 - (void)keyboardWillHide:(NSNotification *)notification
   278 {
   279     if (!rotatingOrientation)
   280         SDL_StopTextInput();
   281     [self setKeyboardHeight:0];
   282 }
   283 
   284 - (void)updateKeyboard
   285 {
   286     CGAffineTransform t = self.view.transform;
   287     CGPoint offset = CGPointMake(0.0, 0.0);
   288     CGRect frame = UIKit_ComputeViewFrame(window, self.view.window.screen);
   289 
   290     if (self.keyboardHeight) {
   291         int rectbottom = self.textInputRect.y + self.textInputRect.h;
   292         int keybottom = self.view.bounds.size.height - self.keyboardHeight;
   293         if (keybottom < rectbottom) {
   294             offset.y = keybottom - rectbottom;
   295         }
   296     }
   297 
   298     /* Apply this view's transform (except any translation) to the offset, in
   299      * order to orient it correctly relative to the frame's coordinate space. */
   300     t.tx = 0.0;
   301     t.ty = 0.0;
   302     offset = CGPointApplyAffineTransform(offset, t);
   303 
   304     /* Apply the updated offset to the view's frame. */
   305     frame.origin.x += offset.x;
   306     frame.origin.y += offset.y;
   307 
   308     self.view.frame = frame;
   309 }
   310 
   311 - (void)setKeyboardHeight:(int)height
   312 {
   313     keyboardVisible = height > 0;
   314     keyboardHeight = height;
   315     [self updateKeyboard];
   316 }
   317 
   318 /* UITextFieldDelegate method.  Invoked when user types something. */
   319 - (BOOL)textField:(UITextField *)_textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
   320 {
   321     NSUInteger len = string.length;
   322 
   323     if (len == 0) {
   324         /* it wants to replace text with nothing, ie a delete */
   325         SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_BACKSPACE);
   326         SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_BACKSPACE);
   327     } else {
   328         /* go through all the characters in the string we've been sent and
   329          * convert them to key presses */
   330         int i;
   331         for (i = 0; i < len; i++) {
   332             unichar c = [string characterAtIndex:i];
   333             Uint16 mod = 0;
   334             SDL_Scancode code;
   335 
   336             if (c < 127) {
   337                 /* figure out the SDL_Scancode and SDL_keymod for this unichar */
   338                 code = unicharToUIKeyInfoTable[c].code;
   339                 mod  = unicharToUIKeyInfoTable[c].mod;
   340             } else {
   341                 /* we only deal with ASCII right now */
   342                 code = SDL_SCANCODE_UNKNOWN;
   343                 mod = 0;
   344             }
   345 
   346             if (mod & KMOD_SHIFT) {
   347                 /* If character uses shift, press shift down */
   348                 SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_LSHIFT);
   349             }
   350 
   351             /* send a keydown and keyup even for the character */
   352             SDL_SendKeyboardKey(SDL_PRESSED, code);
   353             SDL_SendKeyboardKey(SDL_RELEASED, code);
   354 
   355             if (mod & KMOD_SHIFT) {
   356                 /* If character uses shift, press shift back up */
   357                 SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LSHIFT);
   358             }
   359         }
   360 
   361         SDL_SendKeyboardText([string UTF8String]);
   362     }
   363 
   364     return NO; /* don't allow the edit! (keep placeholder text there) */
   365 }
   366 
   367 /* Terminates the editing session */
   368 - (BOOL)textFieldShouldReturn:(UITextField*)_textField
   369 {
   370     SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_RETURN);
   371     SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RETURN);
   372     SDL_StopTextInput();
   373     return YES;
   374 }
   375 
   376 #endif
   377 
   378 @end
   379 
   380 /* iPhone keyboard addition functions */
   381 #if SDL_IPHONE_KEYBOARD
   382 
   383 static SDL_uikitviewcontroller *
   384 GetWindowViewController(SDL_Window * window)
   385 {
   386     if (!window || !window->driverdata) {
   387         SDL_SetError("Invalid window");
   388         return nil;
   389     }
   390 
   391     SDL_WindowData *data = (__bridge SDL_WindowData *)window->driverdata;
   392 
   393     return data.viewcontroller;
   394 }
   395 
   396 SDL_bool
   397 UIKit_HasScreenKeyboardSupport(_THIS)
   398 {
   399     return SDL_TRUE;
   400 }
   401 
   402 void
   403 UIKit_ShowScreenKeyboard(_THIS, SDL_Window *window)
   404 {
   405     @autoreleasepool {
   406         SDL_uikitviewcontroller *vc = GetWindowViewController(window);
   407         [vc showKeyboard];
   408     }
   409 }
   410 
   411 void
   412 UIKit_HideScreenKeyboard(_THIS, SDL_Window *window)
   413 {
   414     @autoreleasepool {
   415         SDL_uikitviewcontroller *vc = GetWindowViewController(window);
   416         [vc hideKeyboard];
   417     }
   418 }
   419 
   420 SDL_bool
   421 UIKit_IsScreenKeyboardShown(_THIS, SDL_Window *window)
   422 {
   423     @autoreleasepool {
   424         SDL_uikitviewcontroller *vc = GetWindowViewController(window);
   425         if (vc != nil) {
   426             return vc.isKeyboardVisible;
   427         }
   428         return SDL_FALSE;
   429     }
   430 }
   431 
   432 void
   433 UIKit_SetTextInputRect(_THIS, SDL_Rect *rect)
   434 {
   435     if (!rect) {
   436         SDL_InvalidParamError("rect");
   437         return;
   438     }
   439 
   440     @autoreleasepool {
   441         SDL_uikitviewcontroller *vc = GetWindowViewController(SDL_GetFocusWindow());
   442         if (vc != nil) {
   443             vc.textInputRect = *rect;
   444 
   445             if (vc.keyboardVisible) {
   446                 [vc updateKeyboard];
   447             }
   448         }
   449     }
   450 }
   451 
   452 
   453 #endif /* SDL_IPHONE_KEYBOARD */
   454 
   455 #endif /* SDL_VIDEO_DRIVER_UIKIT */
   456 
   457 /* vi: set ts=4 sw=4 expandtab: */