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