src/video/uikit/SDL_uikitview.m
author Sam Lantinga <slouken@libsdl.org>
Sat, 11 Aug 2012 10:15:59 -0700
changeset 6392 fa7eb111f994
parent 6342 ac83b73f0edd
child 6436 29a35c72905a
child 8311 40f2e97c4051
permissions -rwxr-xr-x
Fixed bug 1564 - SDL has no function to open a screen keyboard on Android.

Philipp Wiesemann implemented a general on-screen keyboard API for SDL, and I switched iOS code over to use it.
     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         /* send moved event */
    99         SDL_SendMouseMotion(NULL, 0, locationInView.x, locationInView.y);
   100 
   101         /* send mouse down event */
   102         SDL_SendMouseButton(NULL, SDL_PRESSED, SDL_BUTTON_LEFT);
   103     }
   104 
   105 #ifdef FIXED_MULTITOUCH
   106     while(touch) {
   107         CGPoint locationInView = [self touchLocation:touch];
   108 
   109 #ifdef IPHONE_TOUCH_EFFICIENT_DANGEROUS
   110         //FIXME: TODO: Using touch as the fingerId is potentially dangerous
   111         //It is also much more efficient than storing the UITouch pointer
   112         //and comparing it to the incoming event.
   113         SDL_SendFingerDown(touchId, (long)touch,
   114                            SDL_TRUE, locationInView.x, locationInView.y,
   115                            1);
   116 #else
   117         int i;
   118         for(i = 0; i < MAX_SIMULTANEOUS_TOUCHES; i++) {
   119             if (finger[i] == NULL) {
   120                 finger[i] = touch;
   121                 SDL_SendFingerDown(touchId, i,
   122                                    SDL_TRUE, locationInView.x, locationInView.y,
   123                                    1);
   124                 break;
   125             }
   126         }
   127 #endif
   128 
   129         touch = (UITouch*)[enumerator nextObject];
   130     }
   131 #endif
   132 }
   133 
   134 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
   135 {
   136     NSEnumerator *enumerator = [touches objectEnumerator];
   137     UITouch *touch = (UITouch*)[enumerator nextObject];
   138 
   139     if (touch) {
   140         /* send mouse up */
   141         SDL_SendMouseButton(NULL, SDL_RELEASED, SDL_BUTTON_LEFT);
   142     }
   143 
   144 #ifdef FIXED_MULTITOUCH
   145     while(touch) {
   146         CGPoint locationInView = [self touchLocation:touch];
   147 
   148 #ifdef IPHONE_TOUCH_EFFICIENT_DANGEROUS
   149         SDL_SendFingerDown(touchId, (long)touch,
   150                            SDL_FALSE, locationInView.x, locationInView.y,
   151                            1);
   152 #else
   153         int i;
   154         for (i = 0; i < MAX_SIMULTANEOUS_TOUCHES; i++) {
   155             if (finger[i] == touch) {
   156                 SDL_SendFingerDown(touchId, i,
   157                                    SDL_FALSE, locationInView.x, locationInView.y,
   158                                    1);
   159                 finger[i] = NULL;
   160                 break;
   161             }
   162         }
   163 #endif
   164 
   165         touch = (UITouch*)[enumerator nextObject];
   166     }
   167 #endif
   168 }
   169 
   170 - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
   171 {
   172     /*
   173         this can happen if the user puts more than 5 touches on the screen
   174         at once, or perhaps in other circumstances.  Usually (it seems)
   175         all active touches are canceled.
   176     */
   177     [self touchesEnded: touches withEvent: event];
   178 }
   179 
   180 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
   181 {
   182     NSEnumerator *enumerator = [touches objectEnumerator];
   183     UITouch *touch = (UITouch*)[enumerator nextObject];
   184 
   185     if (touch) {
   186         CGPoint locationInView = [touch locationInView: self];
   187 
   188         /* send moved event */
   189         SDL_SendMouseMotion(NULL, 0, locationInView.x, locationInView.y);
   190     }
   191 
   192 #ifdef FIXED_MULTITOUCH
   193     while(touch) {
   194         CGPoint locationInView = [self touchLocation:touch];
   195 
   196 #ifdef IPHONE_TOUCH_EFFICIENT_DANGEROUS
   197         SDL_SendTouchMotion(touchId, (long)touch,
   198                             SDL_FALSE, locationInView.x, locationInView.y,
   199                             1);
   200 #else
   201         int i;
   202         for (i = 0; i < MAX_SIMULTANEOUS_TOUCHES; i++) {
   203             if (finger[i] == touch) {
   204                 SDL_SendTouchMotion(touchId, i,
   205                                     SDL_FALSE, locationInView.x, locationInView.y,
   206                                     1);
   207                 break;
   208             }
   209         }
   210 #endif
   211 
   212         touch = (UITouch*)[enumerator nextObject];
   213     }
   214 #endif
   215 }
   216 
   217 /*
   218     ---- Keyboard related functionality below this line ----
   219 */
   220 #if SDL_IPHONE_KEYBOARD
   221 
   222 /* Is the iPhone virtual keyboard visible onscreen? */
   223 - (BOOL)keyboardVisible
   224 {
   225     return keyboardVisible;
   226 }
   227 
   228 /* Set ourselves up as a UITextFieldDelegate */
   229 - (void)initializeKeyboard
   230 {
   231     textField = [[UITextField alloc] initWithFrame: CGRectZero];
   232     textField.delegate = self;
   233     /* placeholder so there is something to delete! */
   234     textField.text = @" ";
   235 
   236     /* set UITextInputTrait properties, mostly to defaults */
   237     textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
   238     textField.autocorrectionType = UITextAutocorrectionTypeNo;
   239     textField.enablesReturnKeyAutomatically = NO;
   240     textField.keyboardAppearance = UIKeyboardAppearanceDefault;
   241     textField.keyboardType = UIKeyboardTypeDefault;
   242     textField.returnKeyType = UIReturnKeyDefault;
   243     textField.secureTextEntry = NO;
   244 
   245     textField.hidden = YES;
   246     keyboardVisible = NO;
   247     /* add the UITextField (hidden) to our view */
   248     [self addSubview: textField];
   249     [textField release];
   250 }
   251 
   252 /* reveal onscreen virtual keyboard */
   253 - (void)showKeyboard
   254 {
   255     keyboardVisible = YES;
   256     [textField becomeFirstResponder];
   257 }
   258 
   259 /* hide onscreen virtual keyboard */
   260 - (void)hideKeyboard
   261 {
   262     keyboardVisible = NO;
   263     [textField resignFirstResponder];
   264 }
   265 
   266 /* UITextFieldDelegate method.  Invoked when user types something. */
   267 - (BOOL)textField:(UITextField *)_textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
   268 {
   269     if ([string length] == 0) {
   270         /* it wants to replace text with nothing, ie a delete */
   271         SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_DELETE);
   272         SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_DELETE);
   273     }
   274     else {
   275         /* go through all the characters in the string we've been sent
   276            and convert them to key presses */
   277         int i;
   278         for (i = 0; i < [string length]; i++) {
   279 
   280             unichar c = [string characterAtIndex: i];
   281 
   282             Uint16 mod = 0;
   283             SDL_Scancode code;
   284 
   285             if (c < 127) {
   286                 /* figure out the SDL_Scancode and SDL_keymod for this unichar */
   287                 code = unicharToUIKeyInfoTable[c].code;
   288                 mod  = unicharToUIKeyInfoTable[c].mod;
   289             }
   290             else {
   291                 /* we only deal with ASCII right now */
   292                 code = SDL_SCANCODE_UNKNOWN;
   293                 mod = 0;
   294             }
   295 
   296             if (mod & KMOD_SHIFT) {
   297                 /* If character uses shift, press shift down */
   298                 SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_LSHIFT);
   299             }
   300             /* send a keydown and keyup even for the character */
   301             SDL_SendKeyboardKey(SDL_PRESSED, code);
   302             SDL_SendKeyboardKey(SDL_RELEASED, code);
   303             if (mod & KMOD_SHIFT) {
   304                 /* If character uses shift, press shift back up */
   305                 SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LSHIFT);
   306             }
   307         }
   308         SDL_SendKeyboardText([string UTF8String]);
   309     }
   310     return NO; /* don't allow the edit! (keep placeholder text there) */
   311 }
   312 
   313 /* Terminates the editing session */
   314 - (BOOL)textFieldShouldReturn:(UITextField*)_textField
   315 {
   316     SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_RETURN);
   317     SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RETURN);
   318     [self hideKeyboard];
   319     return YES;
   320 }
   321 
   322 #endif
   323 
   324 @end
   325 
   326 /* iPhone keyboard addition functions */
   327 #if SDL_IPHONE_KEYBOARD
   328 
   329 static SDL_uikitview * getWindowView(SDL_Window * window)
   330 {
   331     if (window == NULL) {
   332         SDL_SetError("Window does not exist");
   333         return nil;
   334     }
   335 
   336     SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
   337     SDL_uikitview *view = data != NULL ? data->view : nil;
   338 
   339     if (view == nil) {
   340         SDL_SetError("Window has no view");
   341     }
   342 
   343     return view;
   344 }
   345 
   346 SDL_bool UIKit_HasScreenKeyboardSupport(_THIS, SDL_Window *window)
   347 {
   348     SDL_uikitview *view = getWindowView(window);
   349     if (view == nil) {
   350         return SDL_FALSE;
   351     }
   352 
   353     return SDL_TRUE;
   354 }
   355 
   356 int UIKit_ShowScreenKeyboard(_THIS, SDL_Window *window)
   357 {
   358     SDL_uikitview *view = getWindowView(window);
   359     if (view == nil) {
   360         return -1;
   361     }
   362 
   363     [view showKeyboard];
   364     return 0;
   365 }
   366 
   367 int UIKit_HideScreenKeyboard(_THIS, SDL_Window *window)
   368 {
   369     SDL_uikitview *view = getWindowView(window);
   370     if (view == nil) {
   371         return -1;
   372     }
   373 
   374     [view hideKeyboard];
   375     return 0;
   376 }
   377 
   378 SDL_bool UIKit_IsScreenKeyboardShown(_THIS, SDL_Window *window)
   379 {
   380     SDL_uikitview *view = getWindowView(window);
   381     if (view == nil) {
   382         return 0;
   383     }
   384 
   385     return view.keyboardVisible;
   386 }
   387 
   388 int UIKit_ToggleScreenKeyboard(_THIS, SDL_Window *window)
   389 {
   390     SDL_uikitview *view = getWindowView(window);
   391     if (view == nil) {
   392         return -1;
   393     }
   394 
   395     if (UIKit_IsScreenKeyboardShown(_this, window)) {
   396         UIKit_HideScreenKeyboard(_this, window);
   397     }
   398     else {
   399         UIKit_ShowScreenKeyboard(_this, window);
   400     }
   401     return 0;
   402 }
   403 
   404 #endif /* SDL_IPHONE_KEYBOARD */
   405 
   406 #endif /* SDL_VIDEO_DRIVER_UIKIT */
   407 
   408 /* vi: set ts=4 sw=4 expandtab: */