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: */
SDL_TOUCH_DEVICE_DIRECT
@ SDL_TOUCH_DEVICE_DIRECT
Definition: SDL_touch.h:47
SDL_uikitmodes.h
SDL_SCANCODE_LEFT
@ SDL_SCANCODE_LEFT
Definition: SDL_scancode.h:177
in
GLuint in
Definition: SDL_opengl_glext.h:7940
SDL_SCANCODE_RIGHT
@ SDL_SCANCODE_RIGHT
Definition: SDL_scancode.h:176
SDL_WindowData
Definition: SDL_androidwindow.h:38
SDL_FingerID
Sint64 SDL_FingerID
Definition: SDL_touch.h:42
SDL_TouchID
Sint64 SDL_TouchID
Definition: SDL_touch.h:41
SDL_Scancode
SDL_Scancode
The SDL keyboard scancode representation.
Definition: SDL_scancode.h:43
SDL_SCANCODE_UP
@ SDL_SCANCODE_UP
Definition: SDL_scancode.h:179
SDL_TOUCH_DEVICE_INDIRECT_RELATIVE
@ SDL_TOUCH_DEVICE_INDIRECT_RELATIVE
Definition: SDL_touch.h:49
SDL_SendTouch
int SDL_SendTouch(SDL_TouchID id, SDL_FingerID fingerid, SDL_bool down, float x, float y, float pressure)
Definition: SDL_touch.c:242
SDL_RELEASED
#define SDL_RELEASED
Definition: SDL_events.h:49
SDL_SendTouchMotion
int SDL_SendTouchMotion(SDL_TouchID id, SDL_FingerID fingerid, float x, float y, float pressure)
Definition: SDL_touch.c:364
SDL_SendKeyboardKey
int SDL_SendKeyboardKey(Uint8 state, SDL_Scancode scancode)
Definition: SDL_keyboard.c:679
data
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
SDL_Window
The type used to identify a window.
Definition: SDL_sysvideo.h:73
SDL_uikitview.h
SDL_TouchDeviceType
SDL_TouchDeviceType
Definition: SDL_touch.h:44
SDL_PRESSED
#define SDL_PRESSED
Definition: SDL_events.h:50
SDL_FALSE
@ SDL_FALSE
Definition: SDL_stdinc.h:163
SDL_AddTouch
int SDL_AddTouch(SDL_TouchID touchID, SDL_TouchDeviceType type, const char *name)
Definition: SDL_touch.c:155
SDL_SCANCODE_PAUSE
@ SDL_SCANCODE_PAUSE
Definition: SDL_scancode.h:168
window
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
frame
int frame
Definition: teststreaming.c:60
SDL_SCANCODE_ESCAPE
@ SDL_SCANCODE_ESCAPE
Definition: SDL_scancode.h:93
SDL_AppleTVRemoteOpenedAsJoystick
int SDL_AppleTVRemoteOpenedAsJoystick
Definition: SDL_sysjoystick.m:84
SDL_SCANCODE_UNKNOWN
@ SDL_SCANCODE_UNKNOWN
Definition: SDL_scancode.h:45
SDL_uikitwindow.h
SDL_TRUE
@ SDL_TRUE
Definition: SDL_stdinc.h:164
SDL_hints.h
SDL_uikitview
Definition: SDL_uikitview.h:28
SDL_uikitappdelegate.h
SDL_SCANCODE_DOWN
@ SDL_SCANCODE_DOWN
Definition: SDL_scancode.h:178
void
const SDL_PRINTF_FORMAT_STRING char int const SDL_PRINTF_FORMAT_STRING char int const SDL_PRINTF_FORMAT_STRING char int const SDL_PRINTF_FORMAT_STRING char const char const SDL_SCANF_FORMAT_STRING char return SDL_ThreadFunction const char void return Uint32 return Uint32 void
Definition: SDL_dynapi_procs.h:89
type
GLuint GLuint GLsizei GLenum type
Definition: SDL_opengl.h:1571
SDL_SCANCODE_RETURN
@ SDL_SCANCODE_RETURN
Definition: SDL_scancode.h:92