src/video/uikit/SDL_uikitview.m
author Alex Szpakowski <slime73@gmail.com>
Tue, 29 Jul 2014 00:05:48 -0300
branchiOS-improvements
changeset 9505 6fc615dfc93f
parent 9503 bff6f1fecc5d
child 9506 18e3f94bd860
permissions -rw-r--r--
Added support for SDL_SetWindowBordered on iOS. Worked around a bug with rotating the device on iOS 8.
     1  /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2014 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_uikitview.h"
    26 
    27 #include "../../events/SDL_keyboard_c.h"
    28 #include "../../events/SDL_mouse_c.h"
    29 #include "../../events/SDL_touch_c.h"
    30 
    31 #if SDL_IPHONE_KEYBOARD
    32 #include "keyinfotable.h"
    33 #endif
    34 #include "SDL_uikitappdelegate.h"
    35 #include "SDL_uikitmodes.h"
    36 #include "SDL_uikitwindow.h"
    37 
    38 void _uikit_keyboard_init();
    39 
    40 @implementation SDL_uikitview {
    41 
    42     SDL_TouchID touchId;
    43     UITouch *leftFingerDown;
    44 #ifndef IPHONE_TOUCH_EFFICIENT_DANGEROUS
    45     UITouch *finger[MAX_SIMULTANEOUS_TOUCHES];
    46 #endif
    47 
    48 #if SDL_IPHONE_KEYBOARD
    49     UITextField *textField;
    50 #endif
    51 
    52 }
    53 
    54 - (id)initWithFrame:(CGRect)frame
    55 {
    56     if (self = [super initWithFrame: frame]) {
    57 #if SDL_IPHONE_KEYBOARD
    58         [self initializeKeyboard];
    59 #endif
    60 
    61         self.multipleTouchEnabled = YES;
    62 
    63         touchId = 1;
    64         SDL_AddTouch(touchId, "");
    65     }
    66 
    67     return self;
    68 
    69 }
    70 
    71 - (CGPoint)touchLocation:(UITouch *)touch shouldNormalize:(BOOL)normalize
    72 {
    73     CGPoint point = [touch locationInView: self];
    74 
    75     if (normalize) {
    76         CGRect bounds = self.bounds;
    77         point.x /= bounds.size.width;
    78         point.y /= bounds.size.height;
    79     }
    80 
    81     return point;
    82 }
    83 
    84 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    85 {
    86     for (UITouch *touch in touches) {
    87         if (!leftFingerDown) {
    88             CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO];
    89 
    90             /* send moved event */
    91             SDL_SendMouseMotion(viewcontroller.window, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y);
    92 
    93             /* send mouse down event */
    94             SDL_SendMouseButton(viewcontroller.window, SDL_TOUCH_MOUSEID, SDL_PRESSED, SDL_BUTTON_LEFT);
    95 
    96             leftFingerDown = touch;
    97         }
    98 
    99         CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
   100 #ifdef IPHONE_TOUCH_EFFICIENT_DANGEROUS
   101         /* FIXME: TODO: Using touch as the fingerId is potentially dangerous
   102          * It is also much more efficient than storing the UITouch pointer
   103          * and comparing it to the incoming event.
   104          */
   105         SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
   106                       SDL_TRUE, locationInView.x, locationInView.y, 1.0f);
   107 #else
   108         int i;
   109         for(i = 0; i < MAX_SIMULTANEOUS_TOUCHES; i++) {
   110             if (finger[i] == NULL) {
   111                 finger[i] = touch;
   112                 SDL_SendTouch(touchId, i,
   113                               SDL_TRUE, locationInView.x, locationInView.y, 1.0f);
   114                 break;
   115             }
   116         }
   117 #endif
   118     }
   119 }
   120 
   121 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
   122 {
   123     for (UITouch *touch in touches) {
   124         if (touch == leftFingerDown) {
   125             /* send mouse up */
   126             SDL_SendMouseButton(viewcontroller.window, SDL_TOUCH_MOUSEID, SDL_RELEASED, SDL_BUTTON_LEFT);
   127             leftFingerDown = nil;
   128         }
   129 
   130         CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
   131 #ifdef IPHONE_TOUCH_EFFICIENT_DANGEROUS
   132         SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
   133                       SDL_FALSE, locationInView.x, locationInView.y, 1.0f);
   134 #else
   135         int i;
   136         for (i = 0; i < MAX_SIMULTANEOUS_TOUCHES; i++) {
   137             if (finger[i] == touch) {
   138                 SDL_SendTouch(touchId, i,
   139                               SDL_FALSE, locationInView.x, locationInView.y, 1.0f);
   140                 finger[i] = NULL;
   141                 break;
   142             }
   143         }
   144 #endif
   145     }
   146 }
   147 
   148 - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
   149 {
   150     /*
   151         this can happen if the user puts more than 5 touches on the screen
   152         at once, or perhaps in other circumstances.  Usually (it seems)
   153         all active touches are canceled.
   154     */
   155     [self touchesEnded: touches withEvent: event];
   156 }
   157 
   158 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
   159 {
   160     for (UITouch *touch in touches) {
   161         if (touch == leftFingerDown) {
   162             CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO];
   163 
   164             /* send moved event */
   165             SDL_SendMouseMotion(viewcontroller.window, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y);
   166         }
   167 
   168         CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
   169 #ifdef IPHONE_TOUCH_EFFICIENT_DANGEROUS
   170         SDL_SendTouchMotion(touchId, (SDL_FingerID)((size_t)touch),
   171                             locationInView.x, locationInView.y, 1.0f);
   172 #else
   173         int i;
   174         for (i = 0; i < MAX_SIMULTANEOUS_TOUCHES; i++) {
   175             if (finger[i] == touch) {
   176                 SDL_SendTouchMotion(touchId, i,
   177                                     locationInView.x, locationInView.y, 1.0f);
   178                 break;
   179             }
   180         }
   181 #endif
   182     }
   183 }
   184 
   185 /*
   186     ---- Keyboard related functionality below this line ----
   187 */
   188 #if SDL_IPHONE_KEYBOARD
   189 
   190 @synthesize textInputRect;
   191 @synthesize keyboardHeight;
   192 @synthesize keyboardVisible;
   193 
   194 /* Set ourselves up as a UITextFieldDelegate */
   195 - (void)initializeKeyboard
   196 {
   197     textField = [[UITextField alloc] initWithFrame: CGRectZero];
   198     textField.delegate = self;
   199     /* placeholder so there is something to delete! */
   200     textField.text = @" ";
   201 
   202     /* set UITextInputTrait properties, mostly to defaults */
   203     textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
   204     textField.autocorrectionType = UITextAutocorrectionTypeNo;
   205     textField.enablesReturnKeyAutomatically = NO;
   206     textField.keyboardAppearance = UIKeyboardAppearanceDefault;
   207     textField.keyboardType = UIKeyboardTypeDefault;
   208     textField.returnKeyType = UIReturnKeyDefault;
   209     textField.secureTextEntry = NO;
   210 
   211     textField.hidden = YES;
   212     keyboardVisible = NO;
   213     /* add the UITextField (hidden) to our view */
   214     [self addSubview: textField];
   215     [textField release];
   216     
   217     _uikit_keyboard_init();
   218 }
   219 
   220 /* reveal onscreen virtual keyboard */
   221 - (void)showKeyboard
   222 {
   223     keyboardVisible = YES;
   224     [textField becomeFirstResponder];
   225 }
   226 
   227 /* hide onscreen virtual keyboard */
   228 - (void)hideKeyboard
   229 {
   230     keyboardVisible = NO;
   231     [textField resignFirstResponder];
   232 }
   233 
   234 /* UITextFieldDelegate method.  Invoked when user types something. */
   235 - (BOOL)textField:(UITextField *)_textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
   236 {
   237     if ([string length] == 0) {
   238         /* it wants to replace text with nothing, ie a delete */
   239         SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_BACKSPACE);
   240         SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_BACKSPACE);
   241     }
   242     else {
   243         /* go through all the characters in the string we've been sent
   244            and convert them to key presses */
   245         int i;
   246         for (i = 0; i < [string length]; i++) {
   247 
   248             unichar c = [string characterAtIndex: i];
   249 
   250             Uint16 mod = 0;
   251             SDL_Scancode code;
   252 
   253             if (c < 127) {
   254                 /* figure out the SDL_Scancode and SDL_keymod for this unichar */
   255                 code = unicharToUIKeyInfoTable[c].code;
   256                 mod  = unicharToUIKeyInfoTable[c].mod;
   257             }
   258             else {
   259                 /* we only deal with ASCII right now */
   260                 code = SDL_SCANCODE_UNKNOWN;
   261                 mod = 0;
   262             }
   263 
   264             if (mod & KMOD_SHIFT) {
   265                 /* If character uses shift, press shift down */
   266                 SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_LSHIFT);
   267             }
   268             /* send a keydown and keyup even for the character */
   269             SDL_SendKeyboardKey(SDL_PRESSED, code);
   270             SDL_SendKeyboardKey(SDL_RELEASED, code);
   271             if (mod & KMOD_SHIFT) {
   272                 /* If character uses shift, press shift back up */
   273                 SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LSHIFT);
   274             }
   275         }
   276         SDL_SendKeyboardText([string UTF8String]);
   277     }
   278     return NO; /* don't allow the edit! (keep placeholder text there) */
   279 }
   280 
   281 /* Terminates the editing session */
   282 - (BOOL)textFieldShouldReturn:(UITextField*)_textField
   283 {
   284     SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_RETURN);
   285     SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RETURN);
   286     SDL_StopTextInput();
   287     return YES;
   288 }
   289 
   290 #endif
   291 
   292 @end
   293 
   294 /* iPhone keyboard addition functions */
   295 #if SDL_IPHONE_KEYBOARD
   296 
   297 static SDL_uikitview * getWindowView(SDL_Window * window)
   298 {
   299     if (window == NULL) {
   300         SDL_SetError("Window does not exist");
   301         return nil;
   302     }
   303 
   304     SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
   305     SDL_uikitview *view = data != NULL ? data->view : nil;
   306 
   307     if (view == nil) {
   308         SDL_SetError("Window has no view");
   309     }
   310 
   311     return view;
   312 }
   313 
   314 SDL_bool UIKit_HasScreenKeyboardSupport(_THIS)
   315 {
   316     return SDL_TRUE;
   317 }
   318 
   319 void UIKit_ShowScreenKeyboard(_THIS, SDL_Window *window)
   320 {
   321     SDL_uikitview *view = getWindowView(window);
   322     if (view != nil) {
   323         [view showKeyboard];
   324     }
   325 }
   326 
   327 void UIKit_HideScreenKeyboard(_THIS, SDL_Window *window)
   328 {
   329     SDL_uikitview *view = getWindowView(window);
   330     if (view != nil) {
   331         [view hideKeyboard];
   332     }
   333 }
   334 
   335 SDL_bool UIKit_IsScreenKeyboardShown(_THIS, SDL_Window *window)
   336 {
   337     SDL_uikitview *view = getWindowView(window);
   338     if (view == nil) {
   339         return 0;
   340     }
   341 
   342     return view.isKeyboardVisible;
   343 }
   344 
   345 
   346 void _uikit_keyboard_update() {
   347     SDL_Window *window = SDL_GetFocusWindow();
   348     if (!window) { return; }
   349     SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
   350     if (!data) { return; }
   351     SDL_uikitview *view = data->view;
   352     if (!view) { return; }
   353     
   354     SDL_Rect r = view.textInputRect;
   355     int height = view.keyboardHeight;
   356     int offsetx = 0;
   357     int offsety = 0;
   358     if (height) {
   359         int sw,sh;
   360         SDL_GetWindowSize(window,&sw,&sh);
   361         int bottom = (r.y + r.h);
   362         int kbottom = sh - height;
   363         if (kbottom < bottom) {
   364             offsety = kbottom-bottom;
   365         }
   366     }
   367     UIInterfaceOrientation ui_orient = [[UIApplication sharedApplication] statusBarOrientation];
   368     if (ui_orient == UIInterfaceOrientationLandscapeLeft) {
   369         int tmp = offsetx; offsetx = offsety; offsety = tmp;
   370     }
   371     if (ui_orient == UIInterfaceOrientationLandscapeRight) {
   372         offsety = -offsety;
   373         int tmp = offsetx; offsetx = offsety; offsety = tmp;
   374     }
   375     if (ui_orient == UIInterfaceOrientationPortraitUpsideDown) {
   376         offsety = -offsety;
   377     }
   378 
   379     view.frame = CGRectMake(offsetx,offsety,view.frame.size.width,view.frame.size.height);
   380 }
   381 
   382 void _uikit_keyboard_set_height(int height) {
   383     SDL_uikitview *view = getWindowView(SDL_GetFocusWindow());
   384     if (view == nil) {
   385         return;
   386     }
   387 
   388     view.keyboardVisible = height > 0;
   389     view.keyboardHeight = height;
   390     _uikit_keyboard_update();
   391 }
   392 
   393 void _uikit_keyboard_init() {
   394     NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
   395     NSOperationQueue *queue = [NSOperationQueue mainQueue];
   396     [center addObserverForName:UIKeyboardWillShowNotification
   397                         object:nil
   398                          queue:queue
   399                     usingBlock:^(NSNotification *notification) {
   400                         int height = 0;
   401                         CGSize keyboardSize = [[notification userInfo][UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
   402                         height = keyboardSize.height;
   403                         UIInterfaceOrientation ui_orient = [[UIApplication sharedApplication] statusBarOrientation];
   404                         if (ui_orient == UIInterfaceOrientationLandscapeRight || ui_orient == UIInterfaceOrientationLandscapeLeft) {
   405                             height = keyboardSize.width;
   406                         }
   407                         _uikit_keyboard_set_height(height);
   408                     }
   409      ];
   410     [center addObserverForName:UIKeyboardDidHideNotification
   411                         object:nil
   412                          queue:queue
   413                     usingBlock:^(NSNotification *notification) {
   414                         _uikit_keyboard_set_height(0);
   415                     }
   416      ];
   417 }
   418 
   419 void
   420 UIKit_SetTextInputRect(_THIS, SDL_Rect *rect)
   421 {
   422     if (!rect) {
   423         SDL_InvalidParamError("rect");
   424         return;
   425     }
   426     
   427     SDL_uikitview *view = getWindowView(SDL_GetFocusWindow());
   428     if (view == nil) {
   429         return ;
   430     }
   431 
   432     view.textInputRect = *rect;
   433 }
   434 
   435 
   436 #endif /* SDL_IPHONE_KEYBOARD */
   437 
   438 #endif /* SDL_VIDEO_DRIVER_UIKIT */
   439 
   440 /* vi: set ts=4 sw=4 expandtab: */