2 Simple DirectMedia Layer
3 Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
21 #include "../../SDL_internal.h"
23 #if SDL_VIDEO_DRIVER_UIKIT
25 #include "SDL_uikitview.h"
27 #include "../../events/SDL_keyboard_c.h"
28 #include "../../events/SDL_mouse_c.h"
29 #include "../../events/SDL_touch_c.h"
31 #if SDL_IPHONE_KEYBOARD
32 #include "keyinfotable.h"
34 #include "SDL_uikitappdelegate.h"
35 #include "SDL_uikitmodes.h"
36 #include "SDL_uikitwindow.h"
38 void _uikit_keyboard_init() ;
40 @implementation SDL_uikitview
47 - (id)initWithFrame:(CGRect)frame
49 self = [super initWithFrame: frame];
51 #if SDL_IPHONE_KEYBOARD
52 [self initializeKeyboard];
55 self.multipleTouchEnabled = YES;
58 SDL_AddTouch(touchId, "");
64 - (CGPoint)touchLocation:(UITouch *)touch shouldNormalize:(BOOL)normalize
66 CGPoint point = [touch locationInView: self];
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;
74 CGRect bounds = [self bounds];
75 point.x /= bounds.size.width;
76 point.y /= bounds.size.height;
78 point.x *= displaymodedata->scale;
79 point.y *= displaymodedata->scale;
84 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
86 NSEnumerator *enumerator = [touches objectEnumerator];
87 UITouch *touch = (UITouch*)[enumerator nextObject];
90 if (!leftFingerDown) {
91 CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO];
93 /* send moved event */
94 SDL_SendMouseMotion(self->viewcontroller.window, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y);
96 /* send mouse down event */
97 SDL_SendMouseButton(self->viewcontroller.window, SDL_TOUCH_MOUSEID, SDL_PRESSED, SDL_BUTTON_LEFT);
99 leftFingerDown = touch;
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.
108 SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
109 SDL_TRUE, locationInView.x, locationInView.y, 1.0f);
112 for(i = 0; i < MAX_SIMULTANEOUS_TOUCHES; i++) {
113 if (finger[i] == NULL) {
115 SDL_SendTouch(touchId, i,
116 SDL_TRUE, locationInView.x, locationInView.y, 1.0f);
121 touch = (UITouch*)[enumerator nextObject];
125 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
127 NSEnumerator *enumerator = [touches objectEnumerator];
128 UITouch *touch = (UITouch*)[enumerator nextObject];
131 if (touch == leftFingerDown) {
133 SDL_SendMouseButton(self->viewcontroller.window, SDL_TOUCH_MOUSEID, SDL_RELEASED, SDL_BUTTON_LEFT);
134 leftFingerDown = nil;
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);
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);
152 touch = (UITouch*)[enumerator nextObject];
156 - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
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.
163 [self touchesEnded: touches withEvent: event];
166 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
168 NSEnumerator *enumerator = [touches objectEnumerator];
169 UITouch *touch = (UITouch*)[enumerator nextObject];
172 if (touch == leftFingerDown) {
173 CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO];
175 /* send moved event */
176 SDL_SendMouseMotion(self->viewcontroller.window, SDL_TOUCH_MOUSEID, 0, locationInView.x, locationInView.y);
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);
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);
193 touch = (UITouch*)[enumerator nextObject];
198 ---- Keyboard related functionality below this line ----
200 #if SDL_IPHONE_KEYBOARD
202 @synthesize textInputRect = textInputRect;
203 @synthesize keyboardHeight = keyboardHeight;
205 /* Is the iPhone virtual keyboard visible onscreen? */
206 - (BOOL)keyboardVisible
208 return keyboardVisible;
211 /* Set ourselves up as a UITextFieldDelegate */
212 - (void)initializeKeyboard
214 textField = [[UITextField alloc] initWithFrame: CGRectZero];
215 textField.delegate = self;
216 /* placeholder so there is something to delete! */
217 textField.text = @" ";
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;
228 textField.hidden = YES;
229 keyboardVisible = NO;
230 /* add the UITextField (hidden) to our view */
231 [self addSubview: textField];
234 _uikit_keyboard_init();
237 /* reveal onscreen virtual keyboard */
240 keyboardVisible = YES;
241 [textField becomeFirstResponder];
244 /* hide onscreen virtual keyboard */
247 keyboardVisible = NO;
248 [textField resignFirstResponder];
251 /* UITextFieldDelegate method. Invoked when user types something. */
252 - (BOOL)textField:(UITextField *)_textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
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);
260 /* go through all the characters in the string we've been sent
261 and convert them to key presses */
263 for (i = 0; i < [string length]; i++) {
265 unichar c = [string characterAtIndex: i];
271 /* figure out the SDL_Scancode and SDL_keymod for this unichar */
272 code = unicharToUIKeyInfoTable[c].code;
273 mod = unicharToUIKeyInfoTable[c].mod;
276 /* we only deal with ASCII right now */
277 code = SDL_SCANCODE_UNKNOWN;
281 if (mod & KMOD_SHIFT) {
282 /* If character uses shift, press shift down */
283 SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_LSHIFT);
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);
293 SDL_SendKeyboardText([string UTF8String]);
295 return NO; /* don't allow the edit! (keep placeholder text there) */
298 /* Terminates the editing session */
299 - (BOOL)textFieldShouldReturn:(UITextField*)_textField
301 SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_RETURN);
302 SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_RETURN);
311 /* iPhone keyboard addition functions */
312 #if SDL_IPHONE_KEYBOARD
314 static SDL_uikitview * getWindowView(SDL_Window * window)
316 if (window == NULL) {
317 SDL_SetError("Window does not exist");
321 SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
322 SDL_uikitview *view = data != NULL ? data->view : nil;
325 SDL_SetError("Window has no view");
331 SDL_bool UIKit_HasScreenKeyboardSupport(_THIS)
336 void UIKit_ShowScreenKeyboard(_THIS, SDL_Window *window)
338 SDL_uikitview *view = getWindowView(window);
344 void UIKit_HideScreenKeyboard(_THIS, SDL_Window *window)
346 SDL_uikitview *view = getWindowView(window);
352 SDL_bool UIKit_IsScreenKeyboardShown(_THIS, SDL_Window *window)
354 SDL_uikitview *view = getWindowView(window);
359 return view.keyboardVisible;
363 void _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; }
371 SDL_Rect r = view.textInputRect;
372 int height = view.keyboardHeight;
375 float scale = [UIScreen mainScreen].scale;
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;
385 UIInterfaceOrientation ui_orient = [[UIApplication sharedApplication] statusBarOrientation];
386 if (ui_orient == UIInterfaceOrientationLandscapeLeft) {
387 int tmp = offsetx; offsetx = offsety; offsety = tmp;
389 if (ui_orient == UIInterfaceOrientationLandscapeRight) {
391 int tmp = offsetx; offsetx = offsety; offsety = tmp;
393 if (ui_orient == UIInterfaceOrientationPortraitUpsideDown) {
400 view.frame = CGRectMake(offsetx,offsety,view.frame.size.width,view.frame.size.height);
403 void _uikit_keyboard_set_height(int height) {
404 SDL_uikitview *view = getWindowView(SDL_GetFocusWindow());
409 view.keyboardHeight = height;
410 _uikit_keyboard_update();
413 void _uikit_keyboard_init() {
414 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
415 NSOperationQueue *queue = [NSOperationQueue mainQueue];
416 [center addObserverForName:UIKeyboardWillShowNotification
419 usingBlock:^(NSNotification *notification) {
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;
427 height *= [UIScreen mainScreen].scale;
428 _uikit_keyboard_set_height(height);
431 [center addObserverForName:UIKeyboardDidHideNotification
434 usingBlock:^(NSNotification *notification) {
435 _uikit_keyboard_set_height(0);
441 UIKit_SetTextInputRect(_THIS, SDL_Rect *rect)
444 SDL_InvalidParamError("rect");
448 SDL_uikitview *view = getWindowView(SDL_GetFocusWindow());
453 view.textInputRect = *rect;
457 #endif /* SDL_IPHONE_KEYBOARD */
459 #endif /* SDL_VIDEO_DRIVER_UIKIT */
461 /* vi: set ts=4 sw=4 expandtab: */