src/video/uikit/SDL_uikitview.m
author DavidLudwig <dludwig@pobox.com>
Wed, 25 Jul 2012 20:56:42 -0400
changeset 8310 5d9ba2a65baf
parent 6342 ac83b73f0edd
child 8311 40f2e97c4051
permissions -rwxr-xr-x
Fix for iOS touch input coordinates being halved on Retina displays
     1  /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2012 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_config.h"
    22 
    23 #if SDL_VIDEO_DRIVER_UIKIT
    24 
    25 #import "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 #import "keyinfotable.h"
    33 #import "SDL_uikitappdelegate.h"
    34 #import "SDL_uikitwindow.h"
    35 #endif
    36 
    37 @implementation SDL_uikitview
    38 
    39 - (void)dealloc
    40 {
    41     [super dealloc];
    42 }
    43 
    44 - (id)initWithFrame:(CGRect)frame
    45 {
    46     self = [super initWithFrame: frame];
    47 
    48 #if SDL_IPHONE_KEYBOARD
    49     [self initializeKeyboard];
    50 #endif
    51 
    52 #ifdef FIXED_MULTITOUCH
    53     self.multipleTouchEnabled = YES;
    54 
    55     SDL_Touch touch;
    56     touch.id = 0; //TODO: Should be -1?
    57 
    58     //touch.driverdata = SDL_malloc(sizeof(EventTouchData));
    59     //EventTouchData* data = (EventTouchData*)(touch.driverdata);
    60 
    61     touch.x_min = 0;
    62     touch.x_max = 1;
    63     touch.native_xres = touch.x_max - touch.x_min;
    64     touch.y_min = 0;
    65     touch.y_max = 1;
    66     touch.native_yres = touch.y_max - touch.y_min;
    67     touch.pressure_min = 0;
    68     touch.pressure_max = 1;
    69     touch.native_pressureres = touch.pressure_max - touch.pressure_min;
    70 
    71 
    72     touchId = SDL_AddTouch(&touch, "IPHONE SCREEN");
    73 #endif
    74 
    75     return self;
    76 
    77 }
    78 
    79 - (CGPoint)touchLocation:(UITouch *)touch
    80 {
    81     CGPoint point = [touch locationInView: self];
    82     CGRect frame = [self frame];
    83 
    84     frame = CGRectApplyAffineTransform(frame, [self transform]);
    85     point.x /= frame.size.width;
    86     point.y /= frame.size.height;
    87     return point;
    88 }
    89 
    90 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    91 {
    92     NSEnumerator *enumerator = [touches objectEnumerator];
    93     UITouch *touch = (UITouch*)[enumerator nextObject];
    94 
    95     if (touch) {
    96         CGPoint locationInView = [touch locationInView: self];
    97         
    98         /* Make sure UIView points are converted to screen pixels: */
    99         if ([self respondsToSelector:@selector(contentScaleFactor)]) {
   100             locationInView.x *= self.contentScaleFactor;
   101             locationInView.y *= self.contentScaleFactor;
   102         }
   103 
   104         /* send moved event */
   105         SDL_SendMouseMotion(NULL, 0, locationInView.x, locationInView.y);
   106 
   107         /* send mouse down event */
   108         SDL_SendMouseButton(NULL, SDL_PRESSED, SDL_BUTTON_LEFT);
   109     }
   110 
   111 #ifdef FIXED_MULTITOUCH
   112     while(touch) {
   113         CGPoint locationInView = [self touchLocation:touch];
   114 
   115 #ifdef IPHONE_TOUCH_EFFICIENT_DANGEROUS
   116         //FIXME: TODO: Using touch as the fingerId is potentially dangerous
   117         //It is also much more efficient than storing the UITouch pointer
   118         //and comparing it to the incoming event.
   119         SDL_SendFingerDown(touchId, (long)touch,
   120                            SDL_TRUE, locationInView.x, locationInView.y,
   121                            1);
   122 #else
   123         int i;
   124         for(i = 0; i < MAX_SIMULTANEOUS_TOUCHES; i++) {
   125             if (finger[i] == NULL) {
   126                 finger[i] = touch;
   127                 SDL_SendFingerDown(touchId, i,
   128                                    SDL_TRUE, locationInView.x, locationInView.y,
   129                                    1);
   130                 break;
   131             }
   132         }
   133 #endif
   134 
   135         touch = (UITouch*)[enumerator nextObject];
   136     }
   137 #endif
   138 }
   139 
   140 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
   141 {
   142     NSEnumerator *enumerator = [touches objectEnumerator];
   143     UITouch *touch = (UITouch*)[enumerator nextObject];
   144 
   145     if (touch) {
   146         /* send mouse up */
   147         SDL_SendMouseButton(NULL, SDL_RELEASED, SDL_BUTTON_LEFT);
   148     }
   149 
   150 #ifdef FIXED_MULTITOUCH
   151     while(touch) {
   152         CGPoint locationInView = [self touchLocation:touch];
   153 
   154 #ifdef IPHONE_TOUCH_EFFICIENT_DANGEROUS
   155         SDL_SendFingerDown(touchId, (long)touch,
   156                            SDL_FALSE, locationInView.x, locationInView.y,
   157                            1);
   158 #else
   159         int i;
   160         for (i = 0; i < MAX_SIMULTANEOUS_TOUCHES; i++) {
   161             if (finger[i] == touch) {
   162                 SDL_SendFingerDown(touchId, i,
   163                                    SDL_FALSE, locationInView.x, locationInView.y,
   164                                    1);
   165                 finger[i] = NULL;
   166                 break;
   167             }
   168         }
   169 #endif
   170 
   171         touch = (UITouch*)[enumerator nextObject];
   172     }
   173 #endif
   174 }
   175 
   176 - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
   177 {
   178     /*
   179         this can happen if the user puts more than 5 touches on the screen
   180         at once, or perhaps in other circumstances.  Usually (it seems)
   181         all active touches are canceled.
   182     */
   183     [self touchesEnded: touches withEvent: event];
   184 }
   185 
   186 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
   187 {
   188     NSEnumerator *enumerator = [touches objectEnumerator];
   189     UITouch *touch = (UITouch*)[enumerator nextObject];
   190 
   191     if (touch) {
   192         CGPoint locationInView = [touch locationInView: self];
   193         
   194         /* Make sure UIView points are converted to screen pixels: */
   195         if ([self respondsToSelector:@selector(contentScaleFactor)]) {
   196             locationInView.x *= self.contentScaleFactor;
   197             locationInView.y *= self.contentScaleFactor;
   198         }
   199 
   200         /* send moved event */
   201         SDL_SendMouseMotion(NULL, 0, locationInView.x, locationInView.y);
   202     }
   203 
   204 #ifdef FIXED_MULTITOUCH
   205     while(touch) {
   206         CGPoint locationInView = [self touchLocation:touch];
   207 
   208 #ifdef IPHONE_TOUCH_EFFICIENT_DANGEROUS
   209         SDL_SendTouchMotion(touchId, (long)touch,
   210                             SDL_FALSE, locationInView.x, locationInView.y,
   211                             1);
   212 #else
   213         int i;
   214         for (i = 0; i < MAX_SIMULTANEOUS_TOUCHES; i++) {
   215             if (finger[i] == touch) {
   216                 SDL_SendTouchMotion(touchId, i,
   217                                     SDL_FALSE, locationInView.x, locationInView.y,
   218                                     1);
   219                 break;
   220             }
   221         }
   222 #endif
   223 
   224         touch = (UITouch*)[enumerator nextObject];
   225     }
   226 #endif
   227 }
   228 
   229 /*
   230     ---- Keyboard related functionality below this line ----
   231 */
   232 #if SDL_IPHONE_KEYBOARD
   233 
   234 /* Is the iPhone virtual keyboard visible onscreen? */
   235 - (BOOL)keyboardVisible
   236 {
   237     return keyboardVisible;
   238 }
   239 
   240 /* Set ourselves up as a UITextFieldDelegate */
   241 - (void)initializeKeyboard
   242 {
   243     textField = [[UITextField alloc] initWithFrame: CGRectZero];
   244     textField.delegate = self;
   245     /* placeholder so there is something to delete! */
   246     textField.text = @" ";
   247 
   248     /* set UITextInputTrait properties, mostly to defaults */
   249     textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
   250     textField.autocorrectionType = UITextAutocorrectionTypeNo;
   251     textField.enablesReturnKeyAutomatically = NO;
   252     textField.keyboardAppearance = UIKeyboardAppearanceDefault;
   253     textField.keyboardType = UIKeyboardTypeDefault;
   254     textField.returnKeyType = UIReturnKeyDefault;
   255     textField.secureTextEntry = NO;
   256 
   257     textField.hidden = YES;
   258     keyboardVisible = NO;
   259     /* add the UITextField (hidden) to our view */
   260     [self addSubview: textField];
   261     [textField release];
   262 }
   263 
   264 /* reveal onscreen virtual keyboard */
   265 - (void)showKeyboard
   266 {
   267     keyboardVisible = YES;
   268     [textField becomeFirstResponder];
   269 }
   270 
   271 /* hide onscreen virtual keyboard */
   272 - (void)hideKeyboard
   273 {
   274     keyboardVisible = NO;
   275     [textField resignFirstResponder];
   276 }
   277 
   278 /* UITextFieldDelegate method.  Invoked when user types something. */
   279 - (BOOL)textField:(UITextField *)_textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
   280 {
   281     if ([string length] == 0) {
   282         /* it wants to replace text with nothing, ie a delete */
   283         SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_DELETE);
   284         SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_DELETE);
   285     }
   286     else {
   287         /* go through all the characters in the string we've been sent
   288            and convert them to key presses */
   289         int i;
   290         for (i = 0; i < [string length]; i++) {
   291 
   292             unichar c = [string characterAtIndex: i];
   293 
   294             Uint16 mod = 0;
   295             SDL_Scancode code;
   296 
   297             if (c < 127) {
   298                 /* figure out the SDL_Scancode and SDL_keymod for this unichar */
   299                 code = unicharToUIKeyInfoTable[c].code;
   300                 mod  = unicharToUIKeyInfoTable[c].mod;
   301             }
   302             else {
   303                 /* we only deal with ASCII right now */
   304                 code = SDL_SCANCODE_UNKNOWN;
   305                 mod = 0;
   306             }
   307 
   308             if (mod & KMOD_SHIFT) {
   309                 /* If character uses shift, press shift down */
   310                 SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_LSHIFT);
   311             }
   312             /* send a keydown and keyup even for the character */
   313             SDL_SendKeyboardKey(SDL_PRESSED, code);
   314             SDL_SendKeyboardKey(SDL_RELEASED, code);
   315             if (mod & KMOD_SHIFT) {
   316                 /* If character uses shift, press shift back up */
   317                 SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LSHIFT);
   318             }
   319         }
   320         SDL_SendKeyboardText([string UTF8String]);
   321     }
   322     return NO; /* don't allow the edit! (keep placeholder text there) */
   323 }
   324 
   325 /* Terminates the editing session */
   326 - (BOOL)textFieldShouldReturn:(UITextField*)_textField
   327 {
   328     SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_RETURN);
   329     SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RETURN);
   330     [self hideKeyboard];
   331     return YES;
   332 }
   333 
   334 #endif
   335 
   336 @end
   337 
   338 /* iPhone keyboard addition functions */
   339 #if SDL_IPHONE_KEYBOARD
   340 
   341 static SDL_uikitview * getWindowView(SDL_Window * window)
   342 {
   343     if (window == NULL) {
   344         SDL_SetError("Window does not exist");
   345         return nil;
   346     }
   347 
   348     SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
   349     SDL_uikitview *view = data != NULL ? data->view : nil;
   350 
   351     if (view == nil) {
   352         SDL_SetError("Window has no view");
   353     }
   354 
   355     return view;
   356 }
   357 
   358 int SDL_iPhoneKeyboardShow(SDL_Window * window)
   359 {
   360     SDL_uikitview *view = getWindowView(window);
   361     if (view == nil) {
   362         return -1;
   363     }
   364 
   365     [view showKeyboard];
   366     return 0;
   367 }
   368 
   369 int SDL_iPhoneKeyboardHide(SDL_Window * window)
   370 {
   371     SDL_uikitview *view = getWindowView(window);
   372     if (view == nil) {
   373         return -1;
   374     }
   375 
   376     [view hideKeyboard];
   377     return 0;
   378 }
   379 
   380 SDL_bool SDL_iPhoneKeyboardIsShown(SDL_Window * window)
   381 {
   382     SDL_uikitview *view = getWindowView(window);
   383     if (view == nil) {
   384         return 0;
   385     }
   386 
   387     return view.keyboardVisible;
   388 }
   389 
   390 int SDL_iPhoneKeyboardToggle(SDL_Window * window)
   391 {
   392     SDL_uikitview *view = getWindowView(window);
   393     if (view == nil) {
   394         return -1;
   395     }
   396 
   397     if (SDL_iPhoneKeyboardIsShown(window)) {
   398         SDL_iPhoneKeyboardHide(window);
   399     }
   400     else {
   401         SDL_iPhoneKeyboardShow(window);
   402     }
   403     return 0;
   404 }
   405 
   406 #else
   407 
   408 /* stubs, used if compiled without keyboard support */
   409 
   410 int SDL_iPhoneKeyboardShow(SDL_Window * window)
   411 {
   412     SDL_SetError("Not compiled with keyboard support");
   413     return -1;
   414 }
   415 
   416 int SDL_iPhoneKeyboardHide(SDL_Window * window)
   417 {
   418     SDL_SetError("Not compiled with keyboard support");
   419     return -1;
   420 }
   421 
   422 SDL_bool SDL_iPhoneKeyboardIsShown(SDL_Window * window)
   423 {
   424     return 0;
   425 }
   426 
   427 int SDL_iPhoneKeyboardToggle(SDL_Window * window)
   428 {
   429     SDL_SetError("Not compiled with keyboard support");
   430     return -1;
   431 }
   432 
   433 #endif /* SDL_IPHONE_KEYBOARD */
   434 
   435 #endif /* SDL_VIDEO_DRIVER_UIKIT */
   436 
   437 /* vi: set ts=4 sw=4 expandtab: */