src/video/uikit/SDL_uikitview.m
author Alex Szpakowski <slime73@gmail.com>
Wed, 23 Jul 2014 01:28:24 -0300
branchiOS-improvements
changeset 9499 6fe9b44b2d84
parent 9488 f15520430c94
child 9501 574db299498f
permissions -rw-r--r--
Updated the iOS Objective-C code to use NSDictionary/NSArray/NSNumber literals and subscripting, for improved code clarity.

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