SDL  2.0
SDL_uikitview.m
Go to the documentation of this file.
1  /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2019 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 "SDL_hints.h"
28 #include "../../events/SDL_mouse_c.h"
29 #include "../../events/SDL_touch_c.h"
30 #include "../../events/SDL_events_c.h"
31 
32 #import "SDL_uikitappdelegate.h"
33 #import "SDL_uikitmodes.h"
34 #import "SDL_uikitwindow.h"
35 
36 /* This is defined in SDL_sysjoystick.m */
38 
39 @implementation SDL_uikitview {
40  SDL_Window *sdlwindow;
41 
42  SDL_TouchID directTouchId;
43  SDL_TouchID indirectTouchId;
44 }
45 
46 - (instancetype)initWithFrame:(CGRect)frame
47 {
48  if ((self = [super initWithFrame:frame])) {
49 #if TARGET_OS_TV
50  /* Apple TV Remote touchpad swipe gestures. */
51  UISwipeGestureRecognizer *swipeUp = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
52  swipeUp.direction = UISwipeGestureRecognizerDirectionUp;
53  [self addGestureRecognizer:swipeUp];
54 
55  UISwipeGestureRecognizer *swipeDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
56  swipeDown.direction = UISwipeGestureRecognizerDirectionDown;
57  [self addGestureRecognizer:swipeDown];
58 
59  UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
60  swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
61  [self addGestureRecognizer:swipeLeft];
62 
63  UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
64  swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
65  [self addGestureRecognizer:swipeRight];
66 #endif
67 
68  self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
69  self.autoresizesSubviews = YES;
70 
71  directTouchId = 1;
72  indirectTouchId = 2;
73 
74 #if !TARGET_OS_TV
75  self.multipleTouchEnabled = YES;
76  SDL_AddTouch(directTouchId, SDL_TOUCH_DEVICE_DIRECT, "");
77 #endif
78  }
79 
80  return self;
81 }
82 
83 - (void)setSDLWindow:(SDL_Window *)window
84 {
85  SDL_WindowData *data = nil;
86 
87  if (window == sdlwindow) {
88  return;
89  }
90 
91  /* Remove ourself from the old window. */
92  if (sdlwindow) {
93  SDL_uikitview *view = nil;
94  data = (__bridge SDL_WindowData *) sdlwindow->driverdata;
95 
96  [data.views removeObject:self];
97 
98  [self removeFromSuperview];
99 
100  /* Restore the next-oldest view in the old window. */
101  view = data.views.lastObject;
102 
103  data.viewcontroller.view = view;
104 
105  data.uiwindow.rootViewController = nil;
106  data.uiwindow.rootViewController = data.viewcontroller;
107 
108  [data.uiwindow layoutIfNeeded];
109  }
110 
111  /* Add ourself to the new window. */
112  if (window) {
113  data = (__bridge SDL_WindowData *) window->driverdata;
114 
115  /* Make sure the SDL window has a strong reference to this view. */
116  [data.views addObject:self];
117 
118  /* Replace the view controller's old view with this one. */
119  [data.viewcontroller.view removeFromSuperview];
120  data.viewcontroller.view = self;
121 
122  /* The root view controller handles rotation and the status bar.
123  * Assigning it also adds the controller's view to the window. We
124  * explicitly re-set it to make sure the view is properly attached to
125  * the window. Just adding the sub-view if the root view controller is
126  * already correct causes orientation issues on iOS 7 and below. */
127  data.uiwindow.rootViewController = nil;
128  data.uiwindow.rootViewController = data.viewcontroller;
129 
130  /* The view's bounds may not be correct until the next event cycle. That
131  * might happen after the current dimensions are queried, so we force a
132  * layout now to immediately update the bounds. */
133  [data.uiwindow layoutIfNeeded];
134  }
135 
136  sdlwindow = window;
137 }
138 
139 - (SDL_TouchDeviceType)touchTypeForTouch:(UITouch *)touch
140 {
141 #ifdef __IPHONE_9_0
142  if ([touch respondsToSelector:@selector((type))]) {
143  if (touch.type == UITouchTypeIndirect) {
145  }
146  }
147 #endif
148 
150 }
151 
152 - (SDL_TouchID)touchIdForType:(SDL_TouchDeviceType)type
153 {
154  switch (type) {
156  default:
157  return directTouchId;
159  return indirectTouchId;
160  }
161 }
162 
163 - (CGPoint)touchLocation:(UITouch *)touch shouldNormalize:(BOOL)normalize
164 {
165  CGPoint point = [touch locationInView:self];
166 
167  if (normalize) {
168  CGRect bounds = self.bounds;
169  point.x /= bounds.size.width;
170  point.y /= bounds.size.height;
171  }
172 
173  return point;
174 }
175 
176 - (float)pressureForTouch:(UITouch *)touch
177 {
178 #ifdef __IPHONE_9_0
179  if ([touch respondsToSelector:@selector(force)]) {
180  return (float) touch.force;
181  }
182 #endif
183 
184  return 1.0f;
185 }
186 
187 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
188 {
189  for (UITouch *touch in touches) {
190  SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
191  SDL_TouchID touchId = [self touchIdForType:touchType];
192  float pressure = [self pressureForTouch:touch];
193 
194  if (SDL_AddTouch(touchId, touchType, "") < 0) {
195  continue;
196  }
197 
198  /* FIXME, need to send: int clicks = (int) touch.tapCount; ? */
199 
200  CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
201  SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
202  SDL_TRUE, locationInView.x, locationInView.y, pressure);
203  }
204 }
205 
206 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
207 {
208  for (UITouch *touch in touches) {
209  SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
210  SDL_TouchID touchId = [self touchIdForType:touchType];
211  float pressure = [self pressureForTouch:touch];
212 
213  if (SDL_AddTouch(touchId, touchType, "") < 0) {
214  continue;
215  }
216 
217  /* FIXME, need to send: int clicks = (int) touch.tapCount; ? */
218 
219  CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
220  SDL_SendTouch(touchId, (SDL_FingerID)((size_t)touch),
221  SDL_FALSE, locationInView.x, locationInView.y, pressure);
222  }
223 }
224 
225 - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
226 {
227  [self touchesEnded:touches withEvent:event];
228 }
229 
230 - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
231 {
232  for (UITouch *touch in touches) {
233  SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
234  SDL_TouchID touchId = [self touchIdForType:touchType];
235  float pressure = [self pressureForTouch:touch];
236 
237  if (SDL_AddTouch(touchId, touchType, "") < 0) {
238  continue;
239  }
240 
241  CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
242  SDL_SendTouchMotion(touchId, (SDL_FingerID)((size_t)touch),
243  locationInView.x, locationInView.y, pressure);
244  }
245 }
246 
247 #if TARGET_OS_TV || defined(__IPHONE_9_1)
248 - (SDL_Scancode)scancodeFromPressType:(UIPressType)presstype
249 {
250  switch (presstype) {
251  case UIPressTypeUpArrow:
252  return SDL_SCANCODE_UP;
253  case UIPressTypeDownArrow:
254  return SDL_SCANCODE_DOWN;
255  case UIPressTypeLeftArrow:
256  return SDL_SCANCODE_LEFT;
257  case UIPressTypeRightArrow:
258  return SDL_SCANCODE_RIGHT;
259  case UIPressTypeSelect:
260  /* HIG says: "primary button behavior" */
261  return SDL_SCANCODE_RETURN;
262  case UIPressTypeMenu:
263  /* HIG says: "returns to previous screen" */
264  return SDL_SCANCODE_ESCAPE;
265  case UIPressTypePlayPause:
266  /* HIG says: "secondary button behavior" */
267  return SDL_SCANCODE_PAUSE;
268  default:
269  return SDL_SCANCODE_UNKNOWN;
270  }
271 }
272 
273 - (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
274 {
276  for (UIPress *press in presses) {
277  SDL_Scancode scancode = [self scancodeFromPressType:press.type];
278  SDL_SendKeyboardKey(SDL_PRESSED, scancode);
279  }
280  }
281  [super pressesBegan:presses withEvent:event];
282 }
283 
284 - (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
285 {
287  for (UIPress *press in presses) {
288  SDL_Scancode scancode = [self scancodeFromPressType:press.type];
290  }
291  }
292  [super pressesEnded:presses withEvent:event];
293 }
294 
295 - (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
296 {
298  for (UIPress *press in presses) {
299  SDL_Scancode scancode = [self scancodeFromPressType:press.type];
301  }
302  }
303  [super pressesCancelled:presses withEvent:event];
304 }
305 
306 - (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
307 {
308  /* This is only called when the force of a press changes. */
309  [super pressesChanged:presses withEvent:event];
310 }
311 #endif /* TARGET_OS_TV || defined(__IPHONE_9_1) */
312 
313 #if TARGET_OS_TV
314 -(void)swipeGesture:(UISwipeGestureRecognizer *)gesture
315 {
316  /* Swipe gestures don't trigger begin states. */
317  if (gesture.state == UIGestureRecognizerStateEnded) {
319  /* Send arrow key presses for now, as we don't have an external API
320  * which better maps to swipe gestures. */
321  switch (gesture.direction) {
322  case UISwipeGestureRecognizerDirectionUp:
325  break;
326  case UISwipeGestureRecognizerDirectionDown:
329  break;
330  case UISwipeGestureRecognizerDirectionLeft:
333  break;
334  case UISwipeGestureRecognizerDirectionRight:
337  break;
338  }
339  }
340  }
341 }
342 #endif /* TARGET_OS_TV */
343 
344 @end
345 
346 #endif /* SDL_VIDEO_DRIVER_UIKIT */
347 
348 /* vi: set ts=4 sw=4 expandtab: */
Sint64 SDL_FingerID
Definition: SDL_touch.h:42
SDL_uikitviewcontroller * viewcontroller
int SDL_SendTouch(SDL_TouchID id, SDL_FingerID fingerid, SDL_bool down, float x, float y, float pressure)
Definition: SDL_touch.c:242
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
SDL_TouchDeviceType
Definition: SDL_touch.h:44
int SDL_SendKeyboardKey(Uint8 state, SDL_Scancode scancode)
Definition: SDL_keyboard.c:679
int SDL_SendTouchMotion(SDL_TouchID id, SDL_FingerID fingerid, float x, float y, float pressure)
Definition: SDL_touch.c:364
int SDL_AddTouch(SDL_TouchID touchID, SDL_TouchDeviceType type, const char *name)
Definition: SDL_touch.c:155
NSMutableArray * views
int frame
Definition: teststreaming.c:60
Sint64 SDL_TouchID
Definition: SDL_touch.h:41
UIWindow * uiwindow
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
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:73
int SDL_AppleTVRemoteOpenedAsJoystick
GLuint GLuint GLsizei GLenum type
Definition: SDL_opengl.h:1571
void * driverdata
Definition: SDL_sysvideo.h:111
#define SDL_PRESSED
Definition: SDL_events.h:50
#define SDL_RELEASED
Definition: SDL_events.h:49
GLuint in
SDL_Scancode
The SDL keyboard scancode representation.
Definition: SDL_scancode.h:43