SDL  2.0
SDL_uikitviewcontroller.m
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2016 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_video.h"
26 #include "SDL_assert.h"
27 #include "SDL_hints.h"
28 #include "../SDL_sysvideo.h"
29 #include "../../events/SDL_events_c.h"
30 
32 #import "SDL_uikitmessagebox.h"
33 #include "SDL_uikitvideo.h"
34 #include "SDL_uikitmodes.h"
35 #include "SDL_uikitwindow.h"
36 #include "SDL_uikitopengles.h"
37 
38 #if SDL_IPHONE_KEYBOARD
39 #include "keyinfotable.h"
40 #endif
41 
42 #if TARGET_OS_TV
43 static void
44 SDL_AppleTVControllerUIHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
45 {
46  @autoreleasepool {
47  SDL_uikitviewcontroller *viewcontroller = (__bridge SDL_uikitviewcontroller *) userdata;
48  viewcontroller.controllerUserInteractionEnabled = hint && (*hint != '0');
49  }
50 }
51 #endif
52 
53 @implementation SDL_uikitviewcontroller {
54  CADisplayLink *displayLink;
55  int animationInterval;
56  void (*animationCallback)(void*);
57  void *animationCallbackParam;
58 
59 #if SDL_IPHONE_KEYBOARD
60  UITextField *textField;
61 #endif
62 }
63 
64 @synthesize window;
65 
66 - (instancetype)initWithSDLWindow:(SDL_Window *)_window
67 {
68  if (self = [super initWithNibName:nil bundle:nil]) {
69  self.window = _window;
70 
71 #if SDL_IPHONE_KEYBOARD
72  [self initKeyboard];
73 #endif
74 
75 #if TARGET_OS_TV
77  SDL_AppleTVControllerUIHintChanged,
78  (__bridge void *) self);
79 #endif
80  }
81  return self;
82 }
83 
84 - (void)dealloc
85 {
86 #if SDL_IPHONE_KEYBOARD
87  [self deinitKeyboard];
88 #endif
89 
90 #if TARGET_OS_TV
92  SDL_AppleTVControllerUIHintChanged,
93  (__bridge void *) self);
94 #endif
95 }
96 
97 - (void)setAnimationCallback:(int)interval
98  callback:(void (*)(void*))callback
99  callbackParam:(void*)callbackParam
100 {
101  [self stopAnimation];
102 
103  animationInterval = interval;
104  animationCallback = callback;
105  animationCallbackParam = callbackParam;
106 
107  if (animationCallback) {
108  [self startAnimation];
109  }
110 }
111 
113 {
114  displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(doLoop:)];
115  [displayLink setFrameInterval:animationInterval];
116  [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
117 }
118 
120 {
121  [displayLink invalidate];
122  displayLink = nil;
123 }
124 
125 - (void)doLoop:(CADisplayLink*)sender
126 {
127  /* Don't run the game loop while a messagebox is up */
128  if (!UIKit_ShowingMessageBox()) {
129  /* See the comment in the function definition. */
131 
132  animationCallback(animationCallbackParam);
133  }
134 }
135 
136 - (void)loadView
137 {
138  /* Do nothing. */
139 }
140 
142 {
143  const CGSize size = self.view.bounds.size;
144  int w = (int) size.width;
145  int h = (int) size.height;
146 
148 }
149 
150 #if !TARGET_OS_TV
151 - (NSUInteger)supportedInterfaceOrientations
152 {
154 }
155 
156 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orient
157 {
158  return ([self supportedInterfaceOrientations] & (1 << orient)) != 0;
159 }
160 
162 {
164 }
165 #endif
166 
167 /*
168  ---- Keyboard related functionality below this line ----
169  */
170 #if SDL_IPHONE_KEYBOARD
171 
172 @synthesize textInputRect;
173 @synthesize keyboardHeight;
174 @synthesize keyboardVisible;
175 
176 /* Set ourselves up as a UITextFieldDelegate */
177 - (void)initKeyboard
178 {
179  textField = [[UITextField alloc] initWithFrame:CGRectZero];
180  textField.delegate = self;
181  /* placeholder so there is something to delete! */
182  textField.text = @" ";
183 
184  /* set UITextInputTrait properties, mostly to defaults */
185  textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
186  textField.autocorrectionType = UITextAutocorrectionTypeNo;
187  textField.enablesReturnKeyAutomatically = NO;
188  textField.keyboardAppearance = UIKeyboardAppearanceDefault;
189  textField.keyboardType = UIKeyboardTypeDefault;
190  textField.returnKeyType = UIReturnKeyDefault;
191  textField.secureTextEntry = NO;
192 
193  textField.hidden = YES;
194  keyboardVisible = NO;
195 
196 #if !TARGET_OS_TV
197  NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
198  [center addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
199  [center addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
200 #endif
201 }
202 
203 - (void)setView:(UIView *)view
204 {
205  [super setView:view];
206 
207  [view addSubview:textField];
208 
209  if (keyboardVisible) {
210  [self showKeyboard];
211  }
212 }
213 
214 - (void)deinitKeyboard
215 {
216 #if !TARGET_OS_TV
217  NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
218  [center removeObserver:self name:UIKeyboardWillShowNotification object:nil];
219  [center removeObserver:self name:UIKeyboardWillHideNotification object:nil];
220 #endif
221 }
222 
223 /* reveal onscreen virtual keyboard */
224 - (void)showKeyboard
225 {
226  keyboardVisible = YES;
227  if (textField.window) {
228  [textField becomeFirstResponder];
229  }
230 }
231 
232 /* hide onscreen virtual keyboard */
233 - (void)hideKeyboard
234 {
235  keyboardVisible = NO;
236  [textField resignFirstResponder];
237 }
238 
239 - (void)keyboardWillShow:(NSNotification *)notification
240 {
241 #if !TARGET_OS_TV
242  CGRect kbrect = [[notification userInfo][UIKeyboardFrameBeginUserInfoKey] CGRectValue];
243 
244  /* The keyboard rect is in the coordinate space of the screen/window, but we
245  * want its height in the coordinate space of the view. */
246  kbrect = [self.view convertRect:kbrect fromView:nil];
247 
248  [self setKeyboardHeight:(int)kbrect.size.height];
249 #endif
250 }
251 
252 - (void)keyboardWillHide:(NSNotification *)notification
253 {
255  [self setKeyboardHeight:0];
256 }
257 
258 - (void)updateKeyboard
259 {
260  CGAffineTransform t = self.view.transform;
261  CGPoint offset = CGPointMake(0.0, 0.0);
262  CGRect frame = UIKit_ComputeViewFrame(window, self.view.window.screen);
263 
264  if (self.keyboardHeight) {
265  int rectbottom = self.textInputRect.y + self.textInputRect.h;
266  int keybottom = self.view.bounds.size.height - self.keyboardHeight;
267  if (keybottom < rectbottom) {
268  offset.y = keybottom - rectbottom;
269  }
270  }
271 
272  /* Apply this view's transform (except any translation) to the offset, in
273  * order to orient it correctly relative to the frame's coordinate space. */
274  t.tx = 0.0;
275  t.ty = 0.0;
276  offset = CGPointApplyAffineTransform(offset, t);
277 
278  /* Apply the updated offset to the view's frame. */
279  frame.origin.x += offset.x;
280  frame.origin.y += offset.y;
281 
282  self.view.frame = frame;
283 }
284 
285 - (void)setKeyboardHeight:(int)height
286 {
287  keyboardVisible = height > 0;
288  keyboardHeight = height;
289  [self updateKeyboard];
290 }
291 
292 /* UITextFieldDelegate method. Invoked when user types something. */
293 - (BOOL)textField:(UITextField *)_textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
294 {
295  NSUInteger len = string.length;
296 
297  if (len == 0) {
298  /* it wants to replace text with nothing, ie a delete */
301  } else {
302  /* go through all the characters in the string we've been sent and
303  * convert them to key presses */
304  int i;
305  for (i = 0; i < len; i++) {
306  unichar c = [string characterAtIndex:i];
307  Uint16 mod = 0;
308  SDL_Scancode code;
309 
310  if (c < 127) {
311  /* figure out the SDL_Scancode and SDL_keymod for this unichar */
312  code = unicharToUIKeyInfoTable[c].code;
313  mod = unicharToUIKeyInfoTable[c].mod;
314  } else {
315  /* we only deal with ASCII right now */
316  code = SDL_SCANCODE_UNKNOWN;
317  mod = 0;
318  }
319 
320  if (mod & KMOD_SHIFT) {
321  /* If character uses shift, press shift down */
323  }
324 
325  /* send a keydown and keyup even for the character */
328 
329  if (mod & KMOD_SHIFT) {
330  /* If character uses shift, press shift back up */
332  }
333  }
334 
335  SDL_SendKeyboardText([string UTF8String]);
336  }
337 
338  return NO; /* don't allow the edit! (keep placeholder text there) */
339 }
340 
341 /* Terminates the editing session */
342 - (BOOL)textFieldShouldReturn:(UITextField*)_textField
343 {
347  return YES;
348 }
349 
350 #endif
351 
352 @end
353 
354 /* iPhone keyboard addition functions */
355 #if SDL_IPHONE_KEYBOARD
356 
358 GetWindowViewController(SDL_Window * window)
359 {
360  if (!window || !window->driverdata) {
361  SDL_SetError("Invalid window");
362  return nil;
363  }
364 
365  SDL_WindowData *data = (__bridge SDL_WindowData *)window->driverdata;
366 
367  return data.viewcontroller;
368 }
369 
370 SDL_bool
371 UIKit_HasScreenKeyboardSupport(_THIS)
372 {
373  return SDL_TRUE;
374 }
375 
376 void
377 UIKit_ShowScreenKeyboard(_THIS, SDL_Window *window)
378 {
379  @autoreleasepool {
380  SDL_uikitviewcontroller *vc = GetWindowViewController(window);
381  [vc showKeyboard];
382  }
383 }
384 
385 void
386 UIKit_HideScreenKeyboard(_THIS, SDL_Window *window)
387 {
388  @autoreleasepool {
389  SDL_uikitviewcontroller *vc = GetWindowViewController(window);
390  [vc hideKeyboard];
391  }
392 }
393 
394 SDL_bool
395 UIKit_IsScreenKeyboardShown(_THIS, SDL_Window *window)
396 {
397  @autoreleasepool {
398  SDL_uikitviewcontroller *vc = GetWindowViewController(window);
399  if (vc != nil) {
400  return vc.isKeyboardVisible;
401  }
402  return SDL_FALSE;
403  }
404 }
405 
406 void
407 UIKit_SetTextInputRect(_THIS, SDL_Rect *rect)
408 {
409  if (!rect) {
410  SDL_InvalidParamError("rect");
411  return;
412  }
413 
414  @autoreleasepool {
415  SDL_uikitviewcontroller *vc = GetWindowViewController(SDL_GetFocusWindow());
416  if (vc != nil) {
417  vc.textInputRect = *rect;
418 
419  if (vc.keyboardVisible) {
420  [vc updateKeyboard];
421  }
422  }
423  }
424 }
425 
426 
427 #endif /* SDL_IPHONE_KEYBOARD */
428 
429 #endif /* SDL_VIDEO_DRIVER_UIKIT */
430 
431 /* vi: set ts=4 sw=4 expandtab: */
GLsizei const GLchar *const * string
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1567
void UIKit_GL_RestoreCurrentContext()
SDL_Rect rect
Definition: testrelative.c:27
SDL_uikitviewcontroller * viewcontroller
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1967
SDL_Scancode code
Definition: keyinfotable.h:36
GLuint const GLchar * name
int SDL_SendWindowEvent(SDL_Window *window, Uint8 windowevent, int data1, int data2)
#define SDL_InvalidParamError(param)
Definition: SDL_error.h:54
GLint GLint GLsizei GLsizei height
Definition: SDL_opengl.h:1565
NSUInteger UIKit_GetSupportedOrientations(SDL_Window *window)
GLsizeiptr size
GLenum GLsizei len
int SDL_SendKeyboardKey(Uint8 state, SDL_Scancode scancode)
Definition: SDL_keyboard.c:661
#define SDL_StopTextInput
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1567
#define KMOD_SHIFT
Definition: SDL_keycode.h:335
static UIKitKeyInfo unicharToUIKeyInfoTable[]
Definition: keyinfotable.h:41
#define _THIS
int SDL_SendKeyboardText(const char *text)
Definition: SDL_keyboard.c:774
int frame
Definition: teststreaming.c:60
SDL_Window * SDL_GetFocusWindow(void)
Definition: SDL_video.c:2534
const GLubyte * c
static Uint32 callback(Uint32 interval, void *param)
Definition: testtimer.c:34
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
SDL_bool
Definition: SDL_stdinc.h:130
GLintptr offset
#define SDL_SetError
SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char const char SDL_SCANF_FORMAT_STRING const char return SDL_ThreadFunction const char void return Uint32 return Uint32 void
The type used to identify a window.
Definition: SDL_sysvideo.h:71
#define SDL_AddHintCallback
#define SDL_DelHintCallback
uint16_t Uint16
An unsigned 16-bit integer type.
Definition: SDL_stdinc.h:151
GLubyte GLubyte GLubyte GLubyte w
void * driverdata
Definition: SDL_sysvideo.h:109
#define SDL_PRESSED
Definition: SDL_events.h:50
Uint32 flags
Definition: SDL_sysvideo.h:81
#define SDL_RELEASED
Definition: SDL_events.h:49
SDL_Scancode
The SDL keyboard scancode representation.
Definition: SDL_scancode.h:43
#define SDL_HINT_APPLE_TV_CONTROLLER_UI_EVENTS
A variable controlling whether controllers used with the Apple TV generate UI events.
Definition: SDL_hints.h:307
GLfloat GLfloat GLfloat GLfloat h
GLdouble GLdouble t
Definition: SDL_opengl.h:2064
A rectangle, with the origin at the upper left.
Definition: SDL_rect.h:64
NSUInteger supportedInterfaceOrientations()