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