src/video/uikit/SDL_uikitview.m
author Alex Szpakowski <slime73@gmail.com>
Tue, 29 Jul 2014 00:36:12 -0300
branchiOS-improvements
changeset 9506 18e3f94bd860
parent 9505 6fc615dfc93f
child 9510 e19faa3b5d88
permissions -rw-r--r--
Added missing autorelease pool blocks in UIKit backend code. Fixes memory leak issues, especially in SDL_video.
     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     @autoreleasepool {
   322         SDL_uikitview *view = getWindowView(window);
   323         if (view != nil) {
   324             [view showKeyboard];
   325         }
   326     }
   327 }
   328 
   329 void UIKit_HideScreenKeyboard(_THIS, SDL_Window *window)
   330 {
   331     @autoreleasepool {
   332         SDL_uikitview *view = getWindowView(window);
   333         if (view != nil) {
   334             [view hideKeyboard];
   335         }
   336     }
   337 }
   338 
   339 SDL_bool UIKit_IsScreenKeyboardShown(_THIS, SDL_Window *window)
   340 {
   341     @autoreleasepool {
   342         SDL_uikitview *view = getWindowView(window);
   343         if (view == nil) {
   344             return 0;
   345         }
   346 
   347         return view.isKeyboardVisible;
   348     }
   349 }
   350 
   351 
   352 void _uikit_keyboard_update() {
   353     SDL_Window *window = SDL_GetFocusWindow();
   354     if (!window) { return; }
   355     SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
   356     if (!data) { return; }
   357     SDL_uikitview *view = data->view;
   358     if (!view) { return; }
   359     
   360     SDL_Rect r = view.textInputRect;
   361     int height = view.keyboardHeight;
   362     int offsetx = 0;
   363     int offsety = 0;
   364     if (height) {
   365         int sw,sh;
   366         SDL_GetWindowSize(window,&sw,&sh);
   367         int bottom = (r.y + r.h);
   368         int kbottom = sh - height;
   369         if (kbottom < bottom) {
   370             offsety = kbottom-bottom;
   371         }
   372     }
   373     UIInterfaceOrientation ui_orient = [[UIApplication sharedApplication] statusBarOrientation];
   374     if (ui_orient == UIInterfaceOrientationLandscapeLeft) {
   375         int tmp = offsetx; offsetx = offsety; offsety = tmp;
   376     }
   377     if (ui_orient == UIInterfaceOrientationLandscapeRight) {
   378         offsety = -offsety;
   379         int tmp = offsetx; offsetx = offsety; offsety = tmp;
   380     }
   381     if (ui_orient == UIInterfaceOrientationPortraitUpsideDown) {
   382         offsety = -offsety;
   383     }
   384 
   385     view.frame = CGRectMake(offsetx,offsety,view.frame.size.width,view.frame.size.height);
   386 }
   387 
   388 void _uikit_keyboard_set_height(int height) {
   389     SDL_uikitview *view = getWindowView(SDL_GetFocusWindow());
   390     if (view == nil) {
   391         return;
   392     }
   393 
   394     view.keyboardVisible = height > 0;
   395     view.keyboardHeight = height;
   396     _uikit_keyboard_update();
   397 }
   398 
   399 void _uikit_keyboard_init() {
   400     NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
   401     NSOperationQueue *queue = [NSOperationQueue mainQueue];
   402     [center addObserverForName:UIKeyboardWillShowNotification
   403                         object:nil
   404                          queue:queue
   405                     usingBlock:^(NSNotification *notification) {
   406                         int height = 0;
   407                         CGSize keyboardSize = [[notification userInfo][UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
   408                         height = keyboardSize.height;
   409                         UIInterfaceOrientation ui_orient = [[UIApplication sharedApplication] statusBarOrientation];
   410                         if (ui_orient == UIInterfaceOrientationLandscapeRight || ui_orient == UIInterfaceOrientationLandscapeLeft) {
   411                             height = keyboardSize.width;
   412                         }
   413                         _uikit_keyboard_set_height(height);
   414                     }
   415      ];
   416     [center addObserverForName:UIKeyboardDidHideNotification
   417                         object:nil
   418                          queue:queue
   419                     usingBlock:^(NSNotification *notification) {
   420                         _uikit_keyboard_set_height(0);
   421                     }
   422      ];
   423 }
   424 
   425 void
   426 UIKit_SetTextInputRect(_THIS, SDL_Rect *rect)
   427 {
   428     if (!rect) {
   429         SDL_InvalidParamError("rect");
   430         return;
   431     }
   432 
   433     @autoreleasepool {
   434         SDL_uikitview *view = getWindowView(SDL_GetFocusWindow());
   435         if (view == nil) {
   436             return;
   437         }
   438 
   439         view.textInputRect = *rect;
   440     }
   441 }
   442 
   443 
   444 #endif /* SDL_IPHONE_KEYBOARD */
   445 
   446 #endif /* SDL_VIDEO_DRIVER_UIKIT */
   447 
   448 /* vi: set ts=4 sw=4 expandtab: */